Merge branch 'devel' into araq
This commit is contained in:
commit
764cc02177
42 changed files with 960 additions and 453 deletions
|
|
@ -1324,8 +1324,11 @@ proc shouldRecompile(code: Rope, cfile: Cfile): bool =
|
||||||
if optForceFullMake notin gGlobalOptions:
|
if optForceFullMake notin gGlobalOptions:
|
||||||
if not equalsFile(code, cfile.cname):
|
if not equalsFile(code, cfile.cname):
|
||||||
if isDefined("nimdiff"):
|
if isDefined("nimdiff"):
|
||||||
|
if fileExists(cfile.cname):
|
||||||
copyFile(cfile.cname, cfile.cname & ".backup")
|
copyFile(cfile.cname, cfile.cname & ".backup")
|
||||||
echo "diff ", cfile.cname, ".backup ", cfile.cname
|
echo "diff ", cfile.cname, ".backup ", cfile.cname
|
||||||
|
else:
|
||||||
|
echo "new file ", cfile.cname
|
||||||
writeRope(code, cfile.cname)
|
writeRope(code, cfile.cname)
|
||||||
return
|
return
|
||||||
if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
|
if existsFile(cfile.obj) and os.fileNewer(cfile.obj, cfile.cname):
|
||||||
|
|
|
||||||
|
|
@ -449,7 +449,8 @@ proc unsignedTrimmerPHP(size: BiggestInt): Rope =
|
||||||
template unsignedTrimmer(size: BiggestInt): Rope =
|
template unsignedTrimmer(size: BiggestInt): Rope =
|
||||||
size.unsignedTrimmerJS | size.unsignedTrimmerPHP
|
size.unsignedTrimmerJS | size.unsignedTrimmerPHP
|
||||||
|
|
||||||
proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string, reassign: bool = false) =
|
proc binaryUintExpr(p: PProc, n: PNode, r: var TCompRes, op: string,
|
||||||
|
reassign = false) =
|
||||||
var x, y: TCompRes
|
var x, y: TCompRes
|
||||||
gen(p, n.sons[1], x)
|
gen(p, n.sons[1], x)
|
||||||
gen(p, n.sons[2], y)
|
gen(p, n.sons[2], y)
|
||||||
|
|
@ -1863,8 +1864,8 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
|
proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
var
|
var
|
||||||
a, b: TCompRes
|
a, b: TCompRes
|
||||||
useMagic(p, "SetConstr")
|
useMagic(p, "setConstr")
|
||||||
r.res = rope("SetConstr(")
|
r.res = rope("setConstr(")
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
for i in countup(0, sonsLen(n) - 1):
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
if i > 0: add(r.res, ", ")
|
if i > 0: add(r.res, ", ")
|
||||||
|
|
@ -1877,6 +1878,12 @@ proc genSetConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
gen(p, it, a)
|
gen(p, it, a)
|
||||||
add(r.res, a.res)
|
add(r.res, a.res)
|
||||||
add(r.res, ")")
|
add(r.res, ")")
|
||||||
|
# emit better code for constant sets:
|
||||||
|
if p.target == targetJS and isDeepConstExpr(n):
|
||||||
|
inc(p.g.unique)
|
||||||
|
let tmp = rope("ConstSet") & rope(p.g.unique)
|
||||||
|
addf(p.g.constants, "var $1 = $2;$n", [tmp, r.res])
|
||||||
|
r.res = tmp
|
||||||
|
|
||||||
proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
proc genArrayConstr(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
var a: TCompRes
|
var a: TCompRes
|
||||||
|
|
@ -2128,6 +2135,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) =
|
||||||
else: r.res = rope(f.toStrMaxPrecision)
|
else: r.res = rope(f.toStrMaxPrecision)
|
||||||
r.kind = resExpr
|
r.kind = resExpr
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
|
if isEmptyType(n.typ): genLineDir(p, n)
|
||||||
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
|
if (n.sons[0].kind == nkSym) and (n.sons[0].sym.magic != mNone):
|
||||||
genMagic(p, n, r)
|
genMagic(p, n, r)
|
||||||
elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
|
elif n.sons[0].kind == nkSym and sfInfixCall in n.sons[0].sym.flags and
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,9 @@ proc lowerTupleUnpackingForAsgn*(n: PNode; owner: PSym): PNode =
|
||||||
let value = n.lastSon
|
let value = n.lastSon
|
||||||
result = newNodeI(nkStmtList, n.info)
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
|
||||||
var temp = newSym(skTemp, getIdent(genPrefix), owner, value.info)
|
var temp = newSym(skLet, getIdent("_"), owner, value.info)
|
||||||
var v = newNodeI(nkLetSection, value.info)
|
var v = newNodeI(nkLetSection, value.info)
|
||||||
let tempAsNode = newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info)
|
||||||
|
|
||||||
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
var vpart = newNodeI(nkIdentDefs, tempAsNode.info, 3)
|
||||||
vpart.sons[0] = tempAsNode
|
vpart.sons[0] = tempAsNode
|
||||||
|
|
@ -115,7 +115,7 @@ proc createObj*(owner: PSym, info: TLineInfo): PType =
|
||||||
incl result.flags, tfFinal
|
incl result.flags, tfFinal
|
||||||
result.n = newNodeI(nkRecList, info)
|
result.n = newNodeI(nkRecList, info)
|
||||||
when true:
|
when true:
|
||||||
let s = newSym(skType, getIdent("Env_" & info.toFilename & "_" & $info.line),
|
let s = newSym(skType, getIdent("Env_" & info.toFilename),
|
||||||
owner, info)
|
owner, info)
|
||||||
incl s.flags, sfAnon
|
incl s.flags, sfAnon
|
||||||
s.typ = result
|
s.typ = result
|
||||||
|
|
@ -137,10 +137,32 @@ proc rawIndirectAccess*(a: PNode; field: PSym; info: TLineInfo): PNode =
|
||||||
addSon(result, newSymNode(field))
|
addSon(result, newSymNode(field))
|
||||||
result.typ = field.typ
|
result.typ = field.typ
|
||||||
|
|
||||||
|
proc lookupInRecord(n: PNode, id: int): PSym =
|
||||||
|
result = nil
|
||||||
|
case n.kind
|
||||||
|
of nkRecList:
|
||||||
|
for i in countup(0, sonsLen(n) - 1):
|
||||||
|
result = lookupInRecord(n.sons[i], id)
|
||||||
|
if result != nil: return
|
||||||
|
of nkRecCase:
|
||||||
|
if n.sons[0].kind != nkSym: return
|
||||||
|
result = lookupInRecord(n.sons[0], id)
|
||||||
|
if result != nil: return
|
||||||
|
for i in countup(1, sonsLen(n) - 1):
|
||||||
|
case n.sons[i].kind
|
||||||
|
of nkOfBranch, nkElse:
|
||||||
|
result = lookupInRecord(lastSon(n.sons[i]), id)
|
||||||
|
if result != nil: return
|
||||||
|
else: discard
|
||||||
|
of nkSym:
|
||||||
|
if n.sym.id == -abs(id): result = n.sym
|
||||||
|
else: discard
|
||||||
|
|
||||||
proc addField*(obj: PType; s: PSym) =
|
proc addField*(obj: PType; s: PSym) =
|
||||||
# because of 'gensym' support, we have to mangle the name with its ID.
|
# because of 'gensym' support, we have to mangle the name with its ID.
|
||||||
# This is hacky but the clean solution is much more complex than it looks.
|
# This is hacky but the clean solution is much more complex than it looks.
|
||||||
var field = newSym(skField, getIdent(s.name.s & $s.id), s.owner, s.info)
|
var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
|
||||||
|
field.id = -s.id
|
||||||
let t = skipIntLit(s.typ)
|
let t = skipIntLit(s.typ)
|
||||||
field.typ = t
|
field.typ = t
|
||||||
assert t.kind != tyStmt
|
assert t.kind != tyStmt
|
||||||
|
|
@ -148,9 +170,9 @@ proc addField*(obj: PType; s: PSym) =
|
||||||
addSon(obj.n, newSymNode(field))
|
addSon(obj.n, newSymNode(field))
|
||||||
|
|
||||||
proc addUniqueField*(obj: PType; s: PSym) =
|
proc addUniqueField*(obj: PType; s: PSym) =
|
||||||
let fieldName = getIdent(s.name.s & $s.id)
|
if lookupInRecord(obj.n, s.id) == nil:
|
||||||
if lookupInRecord(obj.n, fieldName) == nil:
|
var field = newSym(skField, getIdent(s.name.s & $obj.n.len), s.owner, s.info)
|
||||||
var field = newSym(skField, fieldName, s.owner, s.info)
|
field.id = -s.id
|
||||||
let t = skipIntLit(s.typ)
|
let t = skipIntLit(s.typ)
|
||||||
field.typ = t
|
field.typ = t
|
||||||
assert t.kind != tyStmt
|
assert t.kind != tyStmt
|
||||||
|
|
@ -159,13 +181,36 @@ proc addUniqueField*(obj: PType; s: PSym) =
|
||||||
|
|
||||||
proc newDotExpr(obj, b: PSym): PNode =
|
proc newDotExpr(obj, b: PSym): PNode =
|
||||||
result = newNodeI(nkDotExpr, obj.info)
|
result = newNodeI(nkDotExpr, obj.info)
|
||||||
let field = getSymFromList(obj.typ.n, getIdent(b.name.s & $b.id))
|
let field = lookupInRecord(obj.typ.n, b.id)
|
||||||
assert field != nil, b.name.s
|
assert field != nil, b.name.s
|
||||||
addSon(result, newSymNode(obj))
|
addSon(result, newSymNode(obj))
|
||||||
addSon(result, newSymNode(field))
|
addSon(result, newSymNode(field))
|
||||||
result.typ = field.typ
|
result.typ = field.typ
|
||||||
|
|
||||||
proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
|
proc indirectAccess*(a: PNode, b: int, info: TLineInfo): PNode =
|
||||||
|
# returns a[].b as a node
|
||||||
|
var deref = newNodeI(nkHiddenDeref, info)
|
||||||
|
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
|
||||||
|
var t = deref.typ.skipTypes(abstractInst)
|
||||||
|
var field: PSym
|
||||||
|
while true:
|
||||||
|
assert t.kind == tyObject
|
||||||
|
field = lookupInRecord(t.n, b)
|
||||||
|
if field != nil: break
|
||||||
|
t = t.sons[0]
|
||||||
|
if t == nil: break
|
||||||
|
t = t.skipTypes(skipPtrs)
|
||||||
|
#if field == nil:
|
||||||
|
# echo "FIELD ", b
|
||||||
|
# debug deref.typ
|
||||||
|
internalAssert field != nil
|
||||||
|
addSon(deref, a)
|
||||||
|
result = newNodeI(nkDotExpr, info)
|
||||||
|
addSon(result, deref)
|
||||||
|
addSon(result, newSymNode(field))
|
||||||
|
result.typ = field.typ
|
||||||
|
|
||||||
|
proc indirectAccess(a: PNode, b: string, info: TLineInfo): PNode =
|
||||||
# returns a[].b as a node
|
# returns a[].b as a node
|
||||||
var deref = newNodeI(nkHiddenDeref, info)
|
var deref = newNodeI(nkHiddenDeref, info)
|
||||||
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
|
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
|
||||||
|
|
@ -191,11 +236,10 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
|
||||||
|
|
||||||
proc getFieldFromObj*(t: PType; v: PSym): PSym =
|
proc getFieldFromObj*(t: PType; v: PSym): PSym =
|
||||||
assert v.kind != skField
|
assert v.kind != skField
|
||||||
let fieldName = getIdent(v.name.s & $v.id)
|
|
||||||
var t = t
|
var t = t
|
||||||
while true:
|
while true:
|
||||||
assert t.kind == tyObject
|
assert t.kind == tyObject
|
||||||
result = getSymFromList(t.n, fieldName)
|
result = lookupInRecord(t.n, v.id)
|
||||||
if result != nil: break
|
if result != nil: break
|
||||||
t = t.sons[0]
|
t = t.sons[0]
|
||||||
if t == nil: break
|
if t == nil: break
|
||||||
|
|
@ -203,7 +247,7 @@ proc getFieldFromObj*(t: PType; v: PSym): PSym =
|
||||||
|
|
||||||
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
|
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
|
||||||
# returns a[].b as a node
|
# returns a[].b as a node
|
||||||
result = indirectAccess(a, b.name.s & $b.id, info)
|
result = indirectAccess(a, b.id, info)
|
||||||
|
|
||||||
proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
||||||
result = indirectAccess(newSymNode(a), b, info)
|
result = indirectAccess(newSymNode(a), b, info)
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,17 @@ proc addPath*(path: string, info: TLineInfo) =
|
||||||
|
|
||||||
proc versionSplitPos(s: string): int =
|
proc versionSplitPos(s: string): int =
|
||||||
result = s.len-2
|
result = s.len-2
|
||||||
while result > 1 and s[result] in {'0'..'9', '.'}: dec result
|
#while result > 1 and s[result] in {'0'..'9', '.'}: dec result
|
||||||
|
while result > 1 and s[result] != '-': dec result
|
||||||
if s[result] != '-': result = s.len
|
if s[result] != '-': result = s.len
|
||||||
|
|
||||||
const
|
const
|
||||||
latest = "head"
|
latest = ""
|
||||||
|
|
||||||
proc `<.`(a, b: string): bool =
|
proc `<.`(a, b: string): bool =
|
||||||
# wether a has a smaller version than b:
|
# wether a has a smaller version than b:
|
||||||
if a == latest: return false
|
if a == latest: return true
|
||||||
|
elif b == latest: return false
|
||||||
var i = 0
|
var i = 0
|
||||||
var j = 0
|
var j = 0
|
||||||
var verA = 0
|
var verA = 0
|
||||||
|
|
@ -33,8 +35,13 @@ proc `<.`(a, b: string): bool =
|
||||||
while true:
|
while true:
|
||||||
let ii = parseInt(a, verA, i)
|
let ii = parseInt(a, verA, i)
|
||||||
let jj = parseInt(b, verB, j)
|
let jj = parseInt(b, verB, j)
|
||||||
|
if ii <= 0 or jj <= 0:
|
||||||
|
# if A has no number and B has but A has no number whatsoever ("#head"),
|
||||||
|
# A is preferred:
|
||||||
|
if ii > 0 and jj <= 0 and j == 0: return true
|
||||||
|
if ii <= 0 and jj > 0 and i == 0: return false
|
||||||
# if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3
|
# if A has no number left, but B has, B is preferred: 0.8 vs 0.8.3
|
||||||
if ii <= 0 or jj <= 0: return jj > 0
|
return jj > 0
|
||||||
if verA < verB: return true
|
if verA < verB: return true
|
||||||
elif verA > verB: return false
|
elif verA > verB: return false
|
||||||
# else: same version number; continue:
|
# else: same version number; continue:
|
||||||
|
|
@ -46,12 +53,9 @@ proc `<.`(a, b: string): bool =
|
||||||
proc addPackage(packages: StringTableRef, p: string) =
|
proc addPackage(packages: StringTableRef, p: string) =
|
||||||
let x = versionSplitPos(p)
|
let x = versionSplitPos(p)
|
||||||
let name = p.substr(0, x-1)
|
let name = p.substr(0, x-1)
|
||||||
if x < p.len:
|
let version = if x < p.len: p.substr(x+1) else: ""
|
||||||
let version = p.substr(x+1)
|
|
||||||
if packages.getOrDefault(name) <. version:
|
if packages.getOrDefault(name) <. version:
|
||||||
packages[name] = version
|
packages[name] = version
|
||||||
else:
|
|
||||||
packages[name] = latest
|
|
||||||
|
|
||||||
iterator chosen(packages: StringTableRef): string =
|
iterator chosen(packages: StringTableRef): string =
|
||||||
for key, val in pairs(packages):
|
for key, val in pairs(packages):
|
||||||
|
|
@ -76,3 +80,18 @@ proc addPathRec(dir: string, info: TLineInfo) =
|
||||||
proc nimblePath*(path: string, info: TLineInfo) =
|
proc nimblePath*(path: string, info: TLineInfo) =
|
||||||
addPathRec(path, info)
|
addPathRec(path, info)
|
||||||
addNimblePath(path, info)
|
addNimblePath(path, info)
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
var rr = newStringTable()
|
||||||
|
addPackage rr, "irc-#head"
|
||||||
|
addPackage rr, "irc-0.1.0"
|
||||||
|
addPackage rr, "irc"
|
||||||
|
addPackage rr, "another"
|
||||||
|
addPackage rr, "another-0.1"
|
||||||
|
|
||||||
|
addPackage rr, "ab-0.1.3"
|
||||||
|
addPackage rr, "ab-0.1"
|
||||||
|
addPackage rr, "justone"
|
||||||
|
|
||||||
|
for p in rr.chosen:
|
||||||
|
echo p
|
||||||
|
|
|
||||||
|
|
@ -1238,9 +1238,6 @@ proc parseExprStmt(p: var TParser): PNode =
|
||||||
addSon(result, e)
|
addSon(result, e)
|
||||||
if p.tok.tokType != tkComma: break
|
if p.tok.tokType != tkComma: break
|
||||||
elif p.tok.indent < 0 and isExprStart(p):
|
elif p.tok.indent < 0 and isExprStart(p):
|
||||||
if a.kind == nkCommand:
|
|
||||||
result = a
|
|
||||||
else:
|
|
||||||
result = newNode(nkCommand, a.info, @[a])
|
result = newNode(nkCommand, a.info, @[a])
|
||||||
while true:
|
while true:
|
||||||
var e = parseExpr(p)
|
var e = parseExpr(p)
|
||||||
|
|
|
||||||
|
|
@ -1266,7 +1266,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result.typ = makeTypeDesc(c, semTypeNode(c, n, nil))
|
result.typ = makeTypeDesc(c, semTypeNode(c, n, nil))
|
||||||
#result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
|
#result = symNodeFromType(c, semTypeNode(c, n, nil), n.info)
|
||||||
of tyTuple:
|
of tyTuple:
|
||||||
checkSonsLen(n, 2)
|
if n.len != 2: return nil
|
||||||
n.sons[0] = makeDeref(n.sons[0])
|
n.sons[0] = makeDeref(n.sons[0])
|
||||||
c.p.bracketExpr = n.sons[0]
|
c.p.bracketExpr = n.sons[0]
|
||||||
# [] operator for tuples requires constant expression:
|
# [] operator for tuples requires constant expression:
|
||||||
|
|
@ -1276,9 +1276,9 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
var idx = getOrdValue(n.sons[1])
|
var idx = getOrdValue(n.sons[1])
|
||||||
if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)]
|
if idx >= 0 and idx < sonsLen(arr): n.typ = arr.sons[int(idx)]
|
||||||
else: localError(n.info, errInvalidIndexValueForTuple)
|
else: localError(n.info, errInvalidIndexValueForTuple)
|
||||||
else:
|
|
||||||
localError(n.info, errIndexTypesDoNotMatch)
|
|
||||||
result = n
|
result = n
|
||||||
|
else:
|
||||||
|
result = nil
|
||||||
else:
|
else:
|
||||||
let s = if n.sons[0].kind == nkSym: n.sons[0].sym
|
let s = if n.sons[0].kind == nkSym: n.sons[0].sym
|
||||||
elif n[0].kind in nkSymChoices: n.sons[0][0].sym
|
elif n[0].kind in nkSymChoices: n.sons[0][0].sym
|
||||||
|
|
|
||||||
|
|
@ -517,7 +517,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) =
|
||||||
proc procVarcheck(n: PNode) =
|
proc procVarcheck(n: PNode) =
|
||||||
if n.kind in nkSymChoices:
|
if n.kind in nkSymChoices:
|
||||||
for x in n: procVarCheck(x)
|
for x in n: procVarCheck(x)
|
||||||
elif n.kind == nkSym and n.sym.magic != mNone:
|
elif n.kind == nkSym and n.sym.magic != mNone and n.sym.kind in routineKinds:
|
||||||
localError(n.info, errXCannotBePassedToProcVar, n.sym.name.s)
|
localError(n.info, errXCannotBePassedToProcVar, n.sym.name.s)
|
||||||
|
|
||||||
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) =
|
||||||
|
|
|
||||||
|
|
@ -96,7 +96,7 @@ proc isDeepConstExpr*(n: PNode): bool =
|
||||||
result = true
|
result = true
|
||||||
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv:
|
||||||
result = isDeepConstExpr(n.sons[1])
|
result = isDeepConstExpr(n.sons[1])
|
||||||
of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure:
|
of nkCurly, nkBracket, nkPar, nkObjConstr, nkClosure, nkRange:
|
||||||
for i in ord(n.kind == nkObjConstr) .. <n.len:
|
for i in ord(n.kind == nkObjConstr) .. <n.len:
|
||||||
if not isDeepConstExpr(n.sons[i]): return false
|
if not isDeepConstExpr(n.sons[i]): return false
|
||||||
if n.typ.isNil: result = true
|
if n.typ.isNil: result = true
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,8 @@ doc.file = """<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
|
<link rel="shortcut icon" href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAUAAAAF////AP///wD///8A////AP///wD///8A////AP///wD///8A////AAAAAAIAAABbAAAAlQAAAKIAAACbAAAAmwAAAKIAAACVAAAAWwAAAAL///8A////AP///wD///8A////AAAAABQAAADAAAAAYwAAAA3///8A////AP///wD///8AAAAADQAAAGMAAADAAAAAFP///wD///8A////AP///wAAAACdAAAAOv///wD///8A////AP///wD///8A////AP///wD///8AAAAAOgAAAJ3///8A////AP///wAAAAAnAAAAcP///wAAAAAoAAAASv///wD///8A////AP///wAAAABKAAAAKP///wAAAABwAAAAJ////wD///8AAAAAgQAAABwAAACIAAAAkAAAAJMAAACtAAAAFQAAABUAAACtAAAAkwAAAJAAAACIAAAAHAAAAIH///8A////AAAAAKQAAACrAAAAaP///wD///8AAAAARQAAANIAAADSAAAARf///wD///8AAAAAaAAAAKsAAACk////AAAAADMAAACcAAAAnQAAABj///8A////AP///wAAAAAYAAAAGP///wD///8A////AAAAABgAAACdAAAAnAAAADMAAAB1AAAAwwAAAP8AAADpAAAAsQAAAE4AAAAb////AP///wAAAAAbAAAATgAAALEAAADpAAAA/wAAAMMAAAB1AAAAtwAAAOkAAAD/AAAA/wAAAP8AAADvAAAA3gAAAN4AAADeAAAA3gAAAO8AAAD/AAAA/wAAAP8AAADpAAAAtwAAAGUAAAA/AAAA3wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADfAAAAPwAAAGX///8A////AAAAAEgAAADtAAAAvwAAAL0AAADGAAAA7wAAAO8AAADGAAAAvQAAAL8AAADtAAAASP///wD///8A////AP///wD///8AAAAAO////wD///8A////AAAAAIcAAACH////AP///wD///8AAAAAO////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A//8AAP//AAD4HwAA7/cAAN/7AAD//wAAoYUAAJ55AACf+QAAh+EAAAAAAADAAwAA4AcAAP5/AAD//wAA//8AAA=="/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ compilation fails. This exit code tells ``git bisect`` to skip the
|
||||||
current commit.::
|
current commit.::
|
||||||
|
|
||||||
git bisect start bad-commit good-commit
|
git bisect start bad-commit good-commit
|
||||||
git bisect ./koch -r c test-source.nim
|
git bisect run ./koch temp -r c test-source.nim
|
||||||
|
|
||||||
The compiler's architecture
|
The compiler's architecture
|
||||||
===========================
|
===========================
|
||||||
|
|
|
||||||
4
koch.nim
4
koch.nim
|
|
@ -178,7 +178,7 @@ proc bundleNimbleExe() =
|
||||||
bundleNimbleSrc()
|
bundleNimbleSrc()
|
||||||
# now compile Nimble and copy it to $nim/bin for the installer.ini
|
# now compile Nimble and copy it to $nim/bin for the installer.ini
|
||||||
# to pick it up:
|
# to pick it up:
|
||||||
nimexec("c dist/nimble/src/nimble.nim")
|
nimexec("c -d:release dist/nimble/src/nimble.nim")
|
||||||
copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
|
copyExe("dist/nimble/src/nimble".exe, "bin/nimble".exe)
|
||||||
|
|
||||||
proc buildNimble(latest: bool) =
|
proc buildNimble(latest: bool) =
|
||||||
|
|
@ -205,7 +205,7 @@ proc buildNimble(latest: bool) =
|
||||||
else:
|
else:
|
||||||
exec("git checkout -f stable")
|
exec("git checkout -f stable")
|
||||||
exec("git pull")
|
exec("git pull")
|
||||||
nimexec("c --noNimblePath -p:compiler " & installDir / "src/nimble.nim")
|
nimexec("c --noNimblePath -p:compiler -d:release " & installDir / "src/nimble.nim")
|
||||||
copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)
|
copyExe(installDir / "src/nimble".exe, "bin/nimble".exe)
|
||||||
|
|
||||||
proc bundleNimsuggest(buildExe: bool) =
|
proc bundleNimsuggest(buildExe: bool) =
|
||||||
|
|
|
||||||
|
|
@ -97,15 +97,6 @@ when not defined(macosx):
|
||||||
## Second-granularity time of last status change.
|
## Second-granularity time of last status change.
|
||||||
result = s.st_ctim.tv_sec
|
result = s.st_ctim.tv_sec
|
||||||
|
|
||||||
proc WIFCONTINUED*(s:cint) : bool {.importc, header: "<sys/wait.h>".}
|
|
||||||
## True if child has been continued.
|
|
||||||
proc WIFEXITED*(s:cint) : bool {.importc, header: "<sys/wait.h>".}
|
|
||||||
## True if child exited normally.
|
|
||||||
proc WIFSIGNALED*(s:cint) : bool {.importc, header: "<sys/wait.h>".}
|
|
||||||
## True if child exited due to uncaught signal.
|
|
||||||
proc WIFSTOPPED*(s:cint) : bool {.importc, header: "<sys/wait.h>".}
|
|
||||||
## True if child is currently stopped.
|
|
||||||
|
|
||||||
when hasAioH:
|
when hasAioH:
|
||||||
proc aio_cancel*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".}
|
proc aio_cancel*(a1: cint, a2: ptr Taiocb): cint {.importc, header: "<aio.h>".}
|
||||||
proc aio_error*(a1: ptr Taiocb): cint {.importc, header: "<aio.h>".}
|
proc aio_error*(a1: ptr Taiocb): cint {.importc, header: "<aio.h>".}
|
||||||
|
|
|
||||||
|
|
@ -602,3 +602,12 @@ var
|
||||||
include posix_linux_amd64_consts
|
include posix_linux_amd64_consts
|
||||||
|
|
||||||
const POSIX_SPAWN_USEVFORK* = cint(0x40) # needs _GNU_SOURCE!
|
const POSIX_SPAWN_USEVFORK* = cint(0x40) # needs _GNU_SOURCE!
|
||||||
|
|
||||||
|
# <sys/wait.h>
|
||||||
|
proc WEXITSTATUS*(s: cint): cint = (s and 0xff00) shr 8
|
||||||
|
proc WTERMSIG*(s:cint): cint = s and 0x7f
|
||||||
|
proc WSTOPSIG*(s:cint): cint = WEXITSTATUS(s)
|
||||||
|
proc WIFEXITED*(s:cint) : bool = WTERMSIG(s) == 0
|
||||||
|
proc WIFSIGNALED*(s:cint) : bool = (cast[int8]((s and 0x7f) + 1) shr 1) > 0
|
||||||
|
proc WIFSTOPPED*(s:cint) : bool = (s and 0xff) == 0x7f
|
||||||
|
proc WIFCONTINUED*(s:cint) : bool = s == W_CONTINUED
|
||||||
|
|
|
||||||
|
|
@ -611,3 +611,18 @@ when hasSpawnH:
|
||||||
# OR'ing of flags:
|
# OR'ing of flags:
|
||||||
const POSIX_SPAWN_USEVFORK* = cint(0)
|
const POSIX_SPAWN_USEVFORK* = cint(0)
|
||||||
|
|
||||||
|
# <sys/wait.h>
|
||||||
|
proc WEXITSTATUS*(s: cint): cint {.importc, header: "<sys/wait.h>".}
|
||||||
|
## Exit code, iff WIFEXITED(s)
|
||||||
|
proc WTERMSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
|
||||||
|
## Termination signal, iff WIFSIGNALED(s)
|
||||||
|
proc WSTOPSIG*(s: cint): cint {.importc, header: "<sys/wait.h>".}
|
||||||
|
## Stop signal, iff WIFSTOPPED(s)
|
||||||
|
proc WIFEXITED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
|
||||||
|
## True if child exited normally.
|
||||||
|
proc WIFSIGNALED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
|
||||||
|
## True if child exited due to uncaught signal.
|
||||||
|
proc WIFSTOPPED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
|
||||||
|
## True if child is currently stopped.
|
||||||
|
proc WIFCONTINUED*(s: cint): bool {.importc, header: "<sys/wait.h>".}
|
||||||
|
## True if child has been continued.
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
import os, oids, tables, strutils, times, heapqueue
|
import os, oids, tables, strutils, times, heapqueue, options
|
||||||
|
|
||||||
import nativesockets, net, deques
|
import nativesockets, net, deques
|
||||||
|
|
||||||
|
|
@ -385,68 +385,6 @@ when defined(windows) or defined(nimdoc):
|
||||||
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
|
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
|
||||||
RemoteSockaddr, RemoteSockaddrLength)
|
RemoteSockaddr, RemoteSockaddrLength)
|
||||||
|
|
||||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
|
||||||
domain = nativesockets.AF_INET): Future[void] =
|
|
||||||
## Connects ``socket`` to server at ``address:port``.
|
|
||||||
##
|
|
||||||
## Returns a ``Future`` which will complete when the connection succeeds
|
|
||||||
## or an error occurs.
|
|
||||||
verifyPresence(socket)
|
|
||||||
var retFuture = newFuture[void]("connect")
|
|
||||||
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
|
||||||
var saddr: Sockaddr_in
|
|
||||||
saddr.sin_family = int16(toInt(domain))
|
|
||||||
saddr.sin_port = 0
|
|
||||||
saddr.sin_addr.s_addr = INADDR_ANY
|
|
||||||
if bindAddr(socket.SocketHandle, cast[ptr SockAddr](addr(saddr)),
|
|
||||||
sizeof(saddr).SockLen) < 0'i32:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
var aiList = getAddrInfo(address, port, domain)
|
|
||||||
var success = false
|
|
||||||
var lastError: OSErrorCode
|
|
||||||
var it = aiList
|
|
||||||
while it != nil:
|
|
||||||
# "the OVERLAPPED structure must remain valid until the I/O completes"
|
|
||||||
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
|
|
||||||
var ol = PCustomOverlapped()
|
|
||||||
GC_ref(ol)
|
|
||||||
ol.data = CompletionData(fd: socket, cb:
|
|
||||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
|
||||||
if not retFuture.finished:
|
|
||||||
if errcode == OSErrorCode(-1):
|
|
||||||
retFuture.complete()
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
|
||||||
)
|
|
||||||
|
|
||||||
var ret = connectEx(socket.SocketHandle, it.ai_addr,
|
|
||||||
sizeof(Sockaddr_in).cint, nil, 0, nil,
|
|
||||||
cast[POVERLAPPED](ol))
|
|
||||||
if ret:
|
|
||||||
# Request to connect completed immediately.
|
|
||||||
success = true
|
|
||||||
retFuture.complete()
|
|
||||||
# We don't deallocate ``ol`` here because even though this completed
|
|
||||||
# immediately poll will still be notified about its completion and it will
|
|
||||||
# free ``ol``.
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
lastError = osLastError()
|
|
||||||
if lastError.int32 == ERROR_IO_PENDING:
|
|
||||||
# In this case ``ol`` will be deallocated in ``poll``.
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
GC_unref(ol)
|
|
||||||
success = false
|
|
||||||
it = it.ai_next
|
|
||||||
|
|
||||||
freeAddrInfo(aiList)
|
|
||||||
if not success:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc recv*(socket: AsyncFD, size: int,
|
proc recv*(socket: AsyncFD, size: int,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||||
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
||||||
|
|
@ -754,8 +692,8 @@ when defined(windows) or defined(nimdoc):
|
||||||
var lpOutputBuf = newString(lpOutputLen)
|
var lpOutputBuf = newString(lpOutputLen)
|
||||||
var dwBytesReceived: Dword
|
var dwBytesReceived: Dword
|
||||||
let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
|
let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
|
||||||
let dwLocalAddressLength = Dword(sizeof(Sockaddr_in) + 16)
|
let dwLocalAddressLength = Dword(sizeof(Sockaddr_in6) + 16)
|
||||||
let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16)
|
let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in6) + 16)
|
||||||
|
|
||||||
template failAccept(errcode) =
|
template failAccept(errcode) =
|
||||||
if flags.isDisconnectionError(errcode):
|
if flags.isDisconnectionError(errcode):
|
||||||
|
|
@ -785,12 +723,14 @@ when defined(windows) or defined(nimdoc):
|
||||||
dwLocalAddressLength, dwRemoteAddressLength,
|
dwLocalAddressLength, dwRemoteAddressLength,
|
||||||
addr localSockaddr, addr localLen,
|
addr localSockaddr, addr localLen,
|
||||||
addr remoteSockaddr, addr remoteLen)
|
addr remoteSockaddr, addr remoteLen)
|
||||||
|
try:
|
||||||
|
let address = getAddrString(remoteSockAddr)
|
||||||
register(clientSock.AsyncFD)
|
register(clientSock.AsyncFD)
|
||||||
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
|
retFuture.complete((address: address, client: clientSock.AsyncFD))
|
||||||
retFuture.complete(
|
except:
|
||||||
(address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr),
|
# getAddrString may raise
|
||||||
client: clientSock.AsyncFD)
|
clientSock.close()
|
||||||
)
|
retFuture.fail(getCurrentException())
|
||||||
|
|
||||||
var ol = PCustomOverlapped()
|
var ol = PCustomOverlapped()
|
||||||
GC_ref(ol)
|
GC_ref(ol)
|
||||||
|
|
@ -823,20 +763,6 @@ when defined(windows) or defined(nimdoc):
|
||||||
|
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain, sockType, protocol: cint): AsyncFD =
|
|
||||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain: Domain = nativesockets.AF_INET,
|
|
||||||
sockType: SockType = SOCK_STREAM,
|
|
||||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
|
||||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc closeSocket*(socket: AsyncFD) =
|
proc closeSocket*(socket: AsyncFD) =
|
||||||
## Closes a socket and ensures that it is unregistered.
|
## Closes a socket and ensures that it is unregistered.
|
||||||
socket.SocketHandle.close()
|
socket.SocketHandle.close()
|
||||||
|
|
@ -1015,23 +941,6 @@ else:
|
||||||
var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
|
var data = PData(fd: fd, readCBs: @[], writeCBs: @[])
|
||||||
p.selector.register(fd.SocketHandle, {}, data.RootRef)
|
p.selector.register(fd.SocketHandle, {}, data.RootRef)
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain: cint, sockType: cint,
|
|
||||||
protocol: cint): AsyncFD =
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
when defined(macosx):
|
|
||||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain: Domain = AF_INET,
|
|
||||||
sockType: SockType = SOCK_STREAM,
|
|
||||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
when defined(macosx):
|
|
||||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc closeSocket*(sock: AsyncFD) =
|
proc closeSocket*(sock: AsyncFD) =
|
||||||
let disp = getGlobalDispatcher()
|
let disp = getGlobalDispatcher()
|
||||||
disp.selector.unregister(sock.SocketHandle)
|
disp.selector.unregister(sock.SocketHandle)
|
||||||
|
|
@ -1115,50 +1024,6 @@ else:
|
||||||
# Callback queue processing
|
# Callback queue processing
|
||||||
processPendingCallbacks(p)
|
processPendingCallbacks(p)
|
||||||
|
|
||||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
|
||||||
domain = AF_INET): Future[void] =
|
|
||||||
var retFuture = newFuture[void]("connect")
|
|
||||||
|
|
||||||
proc cb(fd: AsyncFD): bool =
|
|
||||||
var ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR))
|
|
||||||
if ret == 0:
|
|
||||||
# We have connected.
|
|
||||||
retFuture.complete()
|
|
||||||
return true
|
|
||||||
elif ret == EINTR:
|
|
||||||
# interrupted, keep waiting
|
|
||||||
return false
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
|
|
||||||
return true
|
|
||||||
|
|
||||||
assert getSockDomain(socket.SocketHandle) == domain
|
|
||||||
var aiList = getAddrInfo(address, port, domain)
|
|
||||||
var success = false
|
|
||||||
var lastError: OSErrorCode
|
|
||||||
var it = aiList
|
|
||||||
while it != nil:
|
|
||||||
var ret = connect(socket.SocketHandle, it.ai_addr, it.ai_addrlen.Socklen)
|
|
||||||
if ret == 0:
|
|
||||||
# Request to connect completed immediately.
|
|
||||||
success = true
|
|
||||||
retFuture.complete()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
lastError = osLastError()
|
|
||||||
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
|
||||||
success = true
|
|
||||||
addWrite(socket, cb)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
success = false
|
|
||||||
it = it.ai_next
|
|
||||||
|
|
||||||
freeAddrInfo(aiList)
|
|
||||||
if not success:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc recv*(socket: AsyncFD, size: int,
|
proc recv*(socket: AsyncFD, size: int,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||||
var retFuture = newFuture[string]("recv")
|
var retFuture = newFuture[string]("recv")
|
||||||
|
|
@ -1320,11 +1185,20 @@ else:
|
||||||
else:
|
else:
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
|
let address = getAddrString(cast[ptr SockAddr](addr sockAddress))
|
||||||
register(client.AsyncFD)
|
register(client.AsyncFD)
|
||||||
retFuture.complete((getAddrString(cast[ptr SockAddr](addr sockAddress)), client.AsyncFD))
|
retFuture.complete((address, client.AsyncFD))
|
||||||
|
except:
|
||||||
|
# getAddrString may raise
|
||||||
|
client.close()
|
||||||
|
retFuture.fail(getCurrentException())
|
||||||
addRead(socket, cb)
|
addRead(socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
|
# Common procedures between current and upcoming asyncdispatch
|
||||||
|
include includes.asynccommon
|
||||||
|
|
||||||
proc sleepAsync*(ms: int): Future[void] =
|
proc sleepAsync*(ms: int): Future[void] =
|
||||||
## Suspends the execution of the current async procedure for the next
|
## Suspends the execution of the current async procedure for the next
|
||||||
## ``ms`` milliseconds.
|
## ``ms`` milliseconds.
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,17 @@ when defineSsl:
|
||||||
else:
|
else:
|
||||||
raiseSSLError("Socket has been disconnected")
|
raiseSSLError("Socket has been disconnected")
|
||||||
|
|
||||||
|
proc dial*(address: string, port: Port, protocol = IPPROTO_TCP,
|
||||||
|
buffered = true): Future[AsyncSocket] {.async.} =
|
||||||
|
## Establishes connection to the specified ``address``:``port`` pair via the
|
||||||
|
## specified protocol. The procedure iterates through possible
|
||||||
|
## resolutions of the ``address`` until it succeeds, meaning that it
|
||||||
|
## seamlessly works with both IPv4 and IPv6.
|
||||||
|
## Returns AsyncSocket ready to send or receive data.
|
||||||
|
let asyncFd = await asyncdispatch.dial(address, port, protocol)
|
||||||
|
let sockType = protocol.toSockType()
|
||||||
|
let domain = getSockDomain(asyncFd.SocketHandle)
|
||||||
|
result = newAsyncSocket(asyncFd, domain, sockType, protocol, buffered)
|
||||||
|
|
||||||
proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
|
proc connect*(socket: AsyncSocket, address: string, port: Port) {.async.} =
|
||||||
## Connects ``socket`` to server at ``address:port``.
|
## Connects ``socket`` to server at ``address:port``.
|
||||||
|
|
|
||||||
|
|
@ -44,21 +44,23 @@
|
||||||
const
|
const
|
||||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
||||||
template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
|
template encodeInternal(s: typed, lineLen: int, newLine: string): untyped =
|
||||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||||
## `newline` is added.
|
## `newline` is added.
|
||||||
var total = ((len(s) + 2) div 3) * 4
|
var total = ((len(s) + 2) div 3) * 4
|
||||||
var numLines = (total + lineLen - 1) div lineLen
|
let numLines = (total + lineLen - 1) div lineLen
|
||||||
if numLines > 0: inc(total, (numLines - 1) * newLine.len)
|
if numLines > 0: inc(total, (numLines - 1) * newLine.len)
|
||||||
|
|
||||||
result = newString(total)
|
result = newString(total)
|
||||||
var i = 0
|
var
|
||||||
var r = 0
|
i = 0
|
||||||
var currLine = 0
|
r = 0
|
||||||
|
currLine = 0
|
||||||
while i < s.len - 2:
|
while i < s.len - 2:
|
||||||
var a = ord(s[i])
|
let
|
||||||
var b = ord(s[i+1])
|
a = ord(s[i])
|
||||||
var c = ord(s[i+2])
|
b = ord(s[i+1])
|
||||||
|
c = ord(s[i+2])
|
||||||
result[r] = cb64[a shr 2]
|
result[r] = cb64[a shr 2]
|
||||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||||
|
|
@ -74,8 +76,9 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
|
||||||
currLine = 0
|
currLine = 0
|
||||||
|
|
||||||
if i < s.len-1:
|
if i < s.len-1:
|
||||||
var a = ord(s[i])
|
let
|
||||||
var b = ord(s[i+1])
|
a = ord(s[i])
|
||||||
|
b = ord(s[i+1])
|
||||||
result[r] = cb64[a shr 2]
|
result[r] = cb64[a shr 2]
|
||||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||||
|
|
@ -83,7 +86,7 @@ template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediat
|
||||||
if r+4 != result.len:
|
if r+4 != result.len:
|
||||||
setLen(result, r+4)
|
setLen(result, r+4)
|
||||||
elif i < s.len:
|
elif i < s.len:
|
||||||
var a = ord(s[i])
|
let a = ord(s[i])
|
||||||
result[r] = cb64[a shr 2]
|
result[r] = cb64[a shr 2]
|
||||||
result[r+1] = cb64[(a and 3) shl 4]
|
result[r+1] = cb64[(a and 3) shl 4]
|
||||||
result[r+2] = '='
|
result[r+2] = '='
|
||||||
|
|
@ -127,15 +130,17 @@ proc decode*(s: string): string =
|
||||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||||
result = newString(total)
|
result = newString(total)
|
||||||
|
|
||||||
var i = 0
|
var
|
||||||
var r = 0
|
i = 0
|
||||||
|
r = 0
|
||||||
while true:
|
while true:
|
||||||
while s[i] in Whitespace: inc(i)
|
while s[i] in Whitespace: inc(i)
|
||||||
if i < s.len-3:
|
if i < s.len-3:
|
||||||
var a = s[i].decodeByte
|
let
|
||||||
var b = s[i+1].decodeByte
|
a = s[i].decodeByte
|
||||||
var c = s[i+2].decodeByte
|
b = s[i+1].decodeByte
|
||||||
var d = s[i+3].decodeByte
|
c = s[i+2].decodeByte
|
||||||
|
d = s[i+3].decodeByte
|
||||||
|
|
||||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||||
|
|
|
||||||
|
|
@ -785,7 +785,7 @@ proc sort*[A, B](t: OrderedTableRef[A, B],
|
||||||
t[].sort(cmp)
|
t[].sort(cmp)
|
||||||
|
|
||||||
proc del*[A, B](t: var OrderedTable[A, B], key: A) =
|
proc del*[A, B](t: var OrderedTable[A, B], key: A) =
|
||||||
## deletes `key` from ordered hash table `t`. O(n) comlexity.
|
## deletes `key` from ordered hash table `t`. O(n) complexity.
|
||||||
var n: OrderedKeyValuePairSeq[A, B]
|
var n: OrderedKeyValuePairSeq[A, B]
|
||||||
newSeq(n, len(t.data))
|
newSeq(n, len(t.data))
|
||||||
var h = t.first
|
var h = t.first
|
||||||
|
|
@ -804,7 +804,7 @@ proc del*[A, B](t: var OrderedTable[A, B], key: A) =
|
||||||
h = nxt
|
h = nxt
|
||||||
|
|
||||||
proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
|
proc del*[A, B](t: var OrderedTableRef[A, B], key: A) =
|
||||||
## deletes `key` from ordered hash table `t`. O(n) comlexity.
|
## deletes `key` from ordered hash table `t`. O(n) complexity.
|
||||||
t[].del(key)
|
t[].del(key)
|
||||||
|
|
||||||
# ------------------------------ count tables -------------------------------
|
# ------------------------------ count tables -------------------------------
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ proc setCookie*(key, value: string, domain = "", path = "",
|
||||||
if domain != "": result.add("; Domain=" & domain)
|
if domain != "": result.add("; Domain=" & domain)
|
||||||
if path != "": result.add("; Path=" & path)
|
if path != "": result.add("; Path=" & path)
|
||||||
if expires != "": result.add("; Expires=" & expires)
|
if expires != "": result.add("; Expires=" & expires)
|
||||||
if secure: result.add("; secure")
|
if secure: result.add("; Secure")
|
||||||
if httpOnly: result.add("; HttpOnly")
|
if httpOnly: result.add("; HttpOnly")
|
||||||
|
|
||||||
proc setCookie*(key, value: string, expires: TimeInfo,
|
proc setCookie*(key, value: string, expires: TimeInfo,
|
||||||
|
|
@ -50,7 +50,7 @@ proc setCookie*(key, value: string, expires: TimeInfo,
|
||||||
##
|
##
|
||||||
## **Note:** UTC is assumed as the timezone for ``expires``.
|
## **Note:** UTC is assumed as the timezone for ``expires``.
|
||||||
return setCookie(key, value, domain, path,
|
return setCookie(key, value, domain, path,
|
||||||
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'UTC'"),
|
format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"),
|
||||||
noname, secure, httpOnly)
|
noname, secure, httpOnly)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
|
||||||
|
|
@ -434,7 +434,7 @@ proc `[]=`*(p: var MultipartData, name: string,
|
||||||
## "<html><head></head><body><p>test</p></body></html>")
|
## "<html><head></head><body><p>test</p></body></html>")
|
||||||
p.add(name, file.content, file.name, file.contentType)
|
p.add(name, file.content, file.name, file.contentType)
|
||||||
|
|
||||||
proc format(p: MultipartData): tuple[header, body: string] =
|
proc format(p: MultipartData): tuple[contentType, body: string] =
|
||||||
if p == nil or p.content == nil or p.content.len == 0:
|
if p == nil or p.content == nil or p.content.len == 0:
|
||||||
return ("", "")
|
return ("", "")
|
||||||
|
|
||||||
|
|
@ -449,7 +449,7 @@ proc format(p: MultipartData): tuple[header, body: string] =
|
||||||
if not found:
|
if not found:
|
||||||
break
|
break
|
||||||
|
|
||||||
result.header = "Content-Type: multipart/form-data; boundary=" & bound & "\c\L"
|
result.contentType = "multipart/form-data; boundary=" & bound
|
||||||
result.body = ""
|
result.body = ""
|
||||||
for s in p.content:
|
for s in p.content:
|
||||||
result.body.add("--" & bound & "\c\L" & s)
|
result.body.add("--" & bound & "\c\L" & s)
|
||||||
|
|
@ -640,7 +640,7 @@ proc post*(url: string, extraHeaders = "", body = "",
|
||||||
## ``multipart/form-data`` POSTs comfortably.
|
## ``multipart/form-data`` POSTs comfortably.
|
||||||
##
|
##
|
||||||
## **Deprecated since version 0.15.0**: use ``HttpClient.post`` instead.
|
## **Deprecated since version 0.15.0**: use ``HttpClient.post`` instead.
|
||||||
let (mpHeaders, mpBody) = format(multipart)
|
let (mpContentType, mpBody) = format(multipart)
|
||||||
|
|
||||||
template withNewLine(x): untyped =
|
template withNewLine(x): untyped =
|
||||||
if x.len > 0 and not x.endsWith("\c\L"):
|
if x.len > 0 and not x.endsWith("\c\L"):
|
||||||
|
|
@ -650,9 +650,12 @@ proc post*(url: string, extraHeaders = "", body = "",
|
||||||
|
|
||||||
var xb = mpBody.withNewLine() & body
|
var xb = mpBody.withNewLine() & body
|
||||||
|
|
||||||
var xh = extraHeaders.withNewLine() & mpHeaders.withNewLine() &
|
var xh = extraHeaders.withNewLine() &
|
||||||
withNewLine("Content-Length: " & $len(xb))
|
withNewLine("Content-Length: " & $len(xb))
|
||||||
|
|
||||||
|
if not multipart.isNil:
|
||||||
|
xh.add(withNewLine("Content-Type: " & mpContentType))
|
||||||
|
|
||||||
result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent,
|
result = request(url, httpPOST, xh, xb, sslContext, timeout, userAgent,
|
||||||
proxy)
|
proxy)
|
||||||
var lastURL = url
|
var lastURL = url
|
||||||
|
|
@ -1030,32 +1033,38 @@ proc newConnection(client: HttpClient | AsyncHttpClient,
|
||||||
if client.currentURL.hostname != url.hostname or
|
if client.currentURL.hostname != url.hostname or
|
||||||
client.currentURL.scheme != url.scheme or
|
client.currentURL.scheme != url.scheme or
|
||||||
client.currentURL.port != url.port:
|
client.currentURL.port != url.port:
|
||||||
|
let isSsl = url.scheme.toLowerAscii() == "https"
|
||||||
|
|
||||||
|
if isSsl and not defined(ssl):
|
||||||
|
raise newException(HttpRequestError,
|
||||||
|
"SSL support is not available. Cannot connect over SSL.")
|
||||||
|
|
||||||
if client.connected:
|
if client.connected:
|
||||||
client.close()
|
client.close()
|
||||||
|
|
||||||
when client is HttpClient:
|
|
||||||
client.socket = newSocket()
|
|
||||||
elif client is AsyncHttpClient:
|
|
||||||
client.socket = newAsyncSocket()
|
|
||||||
else: {.fatal: "Unsupported client type".}
|
|
||||||
|
|
||||||
# TODO: I should be able to write 'net.Port' here...
|
# TODO: I should be able to write 'net.Port' here...
|
||||||
let port =
|
let port =
|
||||||
if url.port == "":
|
if url.port == "":
|
||||||
if url.scheme.toLower() == "https":
|
if isSsl:
|
||||||
nativesockets.Port(443)
|
nativesockets.Port(443)
|
||||||
else:
|
else:
|
||||||
nativesockets.Port(80)
|
nativesockets.Port(80)
|
||||||
else: nativesockets.Port(url.port.parseInt)
|
else: nativesockets.Port(url.port.parseInt)
|
||||||
|
|
||||||
if url.scheme.toLower() == "https":
|
when client is HttpClient:
|
||||||
when defined(ssl):
|
client.socket = await net.dial(url.hostname, port)
|
||||||
client.sslContext.wrapSocket(client.socket)
|
elif client is AsyncHttpClient:
|
||||||
else:
|
client.socket = await asyncnet.dial(url.hostname, port)
|
||||||
raise newException(HttpRequestError,
|
else: {.fatal: "Unsupported client type".}
|
||||||
"SSL support is not available. Cannot connect over SSL.")
|
|
||||||
|
when defined(ssl):
|
||||||
|
if isSsl:
|
||||||
|
try:
|
||||||
|
client.sslContext.wrapConnectedSocket(client.socket, handshakeAsClient)
|
||||||
|
except:
|
||||||
|
client.socket.close()
|
||||||
|
raise getCurrentException()
|
||||||
|
|
||||||
await client.socket.connect(url.hostname, port)
|
|
||||||
client.currentURL = url
|
client.currentURL = url
|
||||||
client.connected = true
|
client.connected = true
|
||||||
|
|
||||||
|
|
@ -1188,7 +1197,7 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "",
|
||||||
##
|
##
|
||||||
## This procedure will follow redirects up to a maximum number of redirects
|
## This procedure will follow redirects up to a maximum number of redirects
|
||||||
## specified in ``client.maxRedirects``.
|
## specified in ``client.maxRedirects``.
|
||||||
let (mpHeader, mpBody) = format(multipart)
|
let (mpContentType, mpBody) = format(multipart)
|
||||||
# TODO: Support FutureStream for `body` parameter.
|
# TODO: Support FutureStream for `body` parameter.
|
||||||
template withNewLine(x): untyped =
|
template withNewLine(x): untyped =
|
||||||
if x.len > 0 and not x.endsWith("\c\L"):
|
if x.len > 0 and not x.endsWith("\c\L"):
|
||||||
|
|
@ -1199,7 +1208,7 @@ proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "",
|
||||||
|
|
||||||
var headers = newHttpHeaders()
|
var headers = newHttpHeaders()
|
||||||
if multipart != nil:
|
if multipart != nil:
|
||||||
headers["Content-Type"] = mpHeader.split(": ")[1]
|
headers["Content-Type"] = mpContentType
|
||||||
headers["Content-Length"] = $len(xb)
|
headers["Content-Length"] = $len(xb)
|
||||||
|
|
||||||
result = await client.requestAux(url, $HttpPOST, xb, headers)
|
result = await client.requestAux(url, $HttpPOST, xb, headers)
|
||||||
|
|
|
||||||
201
lib/pure/includes/asynccommon.nim
Normal file
201
lib/pure/includes/asynccommon.nim
Normal file
|
|
@ -0,0 +1,201 @@
|
||||||
|
template newAsyncNativeSocketImpl(domain, sockType, protocol) =
|
||||||
|
let handle = newNativeSocket(domain, sockType, protocol)
|
||||||
|
if handle == osInvalidSocket:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
handle.setBlocking(false)
|
||||||
|
when defined(macosx):
|
||||||
|
handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
||||||
|
result = handle.AsyncFD
|
||||||
|
register(result)
|
||||||
|
|
||||||
|
proc newAsyncNativeSocket*(domain: cint, sockType: cint,
|
||||||
|
protocol: cint): AsyncFD =
|
||||||
|
newAsyncNativeSocketImpl(domain, sockType, protocol)
|
||||||
|
|
||||||
|
proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
|
||||||
|
sockType: SockType = SOCK_STREAM,
|
||||||
|
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
||||||
|
newAsyncNativeSocketImpl(domain, sockType, protocol)
|
||||||
|
|
||||||
|
when defined(windows) or defined(nimdoc):
|
||||||
|
proc bindToDomain(handle: SocketHandle, domain: Domain) =
|
||||||
|
# Extracted into a separate proc, because connect() on Windows requires
|
||||||
|
# the socket to be initially bound.
|
||||||
|
template doBind(saddr) =
|
||||||
|
if bindAddr(handle, cast[ptr SockAddr](addr(saddr)),
|
||||||
|
sizeof(saddr).SockLen) < 0'i32:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
|
if domain == Domain.AF_INET6:
|
||||||
|
var saddr: Sockaddr_in6
|
||||||
|
saddr.sin6_family = int16(toInt(domain))
|
||||||
|
doBind(saddr)
|
||||||
|
else:
|
||||||
|
var saddr: Sockaddr_in
|
||||||
|
saddr.sin_family = int16(toInt(domain))
|
||||||
|
doBind(saddr)
|
||||||
|
|
||||||
|
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
|
||||||
|
let retFuture = newFuture[void]("doConnect")
|
||||||
|
result = retFuture
|
||||||
|
|
||||||
|
var ol = PCustomOverlapped()
|
||||||
|
GC_ref(ol)
|
||||||
|
ol.data = CompletionData(fd: socket, cb:
|
||||||
|
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
||||||
|
if not retFuture.finished:
|
||||||
|
if errcode == OSErrorCode(-1):
|
||||||
|
retFuture.complete()
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
||||||
|
)
|
||||||
|
|
||||||
|
let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr,
|
||||||
|
cint(addrInfo.ai_addrlen), nil, 0, nil,
|
||||||
|
cast[POVERLAPPED](ol))
|
||||||
|
if ret:
|
||||||
|
# Request to connect completed immediately.
|
||||||
|
retFuture.complete()
|
||||||
|
# We don't deallocate ``ol`` here because even though this completed
|
||||||
|
# immediately poll will still be notified about its completion and it
|
||||||
|
# will free ``ol``.
|
||||||
|
else:
|
||||||
|
let lastError = osLastError()
|
||||||
|
if lastError.int32 != ERROR_IO_PENDING:
|
||||||
|
# With ERROR_IO_PENDING ``ol`` will be deallocated in ``poll``,
|
||||||
|
# and the future will be completed/failed there, too.
|
||||||
|
GC_unref(ol)
|
||||||
|
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||||
|
else:
|
||||||
|
proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): Future[void] =
|
||||||
|
let retFuture = newFuture[void]("doConnect")
|
||||||
|
result = retFuture
|
||||||
|
|
||||||
|
proc cb(fd: AsyncFD): bool =
|
||||||
|
let ret = SocketHandle(fd).getSockOptInt(
|
||||||
|
cint(SOL_SOCKET), cint(SO_ERROR))
|
||||||
|
if ret == 0:
|
||||||
|
# We have connected.
|
||||||
|
retFuture.complete()
|
||||||
|
return true
|
||||||
|
elif ret == EINTR:
|
||||||
|
# interrupted, keep waiting
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
|
||||||
|
return true
|
||||||
|
|
||||||
|
let ret = connect(socket.SocketHandle,
|
||||||
|
addrInfo.ai_addr,
|
||||||
|
addrInfo.ai_addrlen.Socklen)
|
||||||
|
if ret == 0:
|
||||||
|
# Request to connect completed immediately.
|
||||||
|
retFuture.complete()
|
||||||
|
else:
|
||||||
|
let lastError = osLastError()
|
||||||
|
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
||||||
|
addWrite(socket, cb)
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||||
|
|
||||||
|
template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped,
|
||||||
|
protocol: Protocol = IPPROTO_RAW) =
|
||||||
|
## Iterates through the AddrInfo linked list asynchronously
|
||||||
|
## until the connection can be established.
|
||||||
|
const shouldCreateFd = not declared(fd)
|
||||||
|
|
||||||
|
when shouldCreateFd:
|
||||||
|
let sockType = protocol.toSockType()
|
||||||
|
|
||||||
|
var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD]
|
||||||
|
for i in low(fdPerDomain)..high(fdPerDomain):
|
||||||
|
fdPerDomain[i] = osInvalidSocket.AsyncFD
|
||||||
|
template closeUnusedFds(domainToKeep = -1) {.dirty.} =
|
||||||
|
for i, fd in fdPerDomain:
|
||||||
|
if fd != osInvalidSocket.AsyncFD and i != domainToKeep:
|
||||||
|
fd.closeSocket()
|
||||||
|
|
||||||
|
var lastException: ref Exception
|
||||||
|
var curAddrInfo = addrInfo
|
||||||
|
var domain: Domain
|
||||||
|
when shouldCreateFd:
|
||||||
|
var curFd: AsyncFD
|
||||||
|
else:
|
||||||
|
var curFd = fd
|
||||||
|
proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} =
|
||||||
|
if fut == nil or fut.failed:
|
||||||
|
if fut != nil:
|
||||||
|
lastException = fut.readError()
|
||||||
|
|
||||||
|
while curAddrInfo != nil:
|
||||||
|
let domainOpt = curAddrInfo.ai_family.toKnownDomain()
|
||||||
|
if domainOpt.isSome:
|
||||||
|
domain = domainOpt.unsafeGet()
|
||||||
|
break
|
||||||
|
curAddrInfo = curAddrInfo.ai_next
|
||||||
|
|
||||||
|
if curAddrInfo == nil:
|
||||||
|
freeAddrInfo(addrInfo)
|
||||||
|
when shouldCreateFd:
|
||||||
|
closeUnusedFds()
|
||||||
|
if lastException != nil:
|
||||||
|
retFuture.fail(lastException)
|
||||||
|
else:
|
||||||
|
retFuture.fail(newException(
|
||||||
|
IOError, "Couldn't resolve address: " & address))
|
||||||
|
return
|
||||||
|
|
||||||
|
when shouldCreateFd:
|
||||||
|
curFd = fdPerDomain[ord(domain)]
|
||||||
|
if curFd == osInvalidSocket.AsyncFD:
|
||||||
|
try:
|
||||||
|
curFd = newAsyncNativeSocket(domain, sockType, protocol)
|
||||||
|
except:
|
||||||
|
freeAddrInfo(addrInfo)
|
||||||
|
closeUnusedFds()
|
||||||
|
raise getCurrentException()
|
||||||
|
when defined(windows):
|
||||||
|
curFd.SocketHandle.bindToDomain(domain)
|
||||||
|
fdPerDomain[ord(domain)] = curFd
|
||||||
|
|
||||||
|
doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo
|
||||||
|
curAddrInfo = curAddrInfo.ai_next
|
||||||
|
else:
|
||||||
|
freeAddrInfo(addrInfo)
|
||||||
|
when shouldCreateFd:
|
||||||
|
closeUnusedFds(ord(domain))
|
||||||
|
retFuture.complete(curFd)
|
||||||
|
else:
|
||||||
|
retFuture.complete()
|
||||||
|
|
||||||
|
tryNextAddrInfo(nil)
|
||||||
|
|
||||||
|
proc dial*(address: string, port: Port,
|
||||||
|
protocol: Protocol = IPPROTO_TCP): Future[AsyncFD] =
|
||||||
|
## Establishes connection to the specified ``address``:``port`` pair via the
|
||||||
|
## specified protocol. The procedure iterates through possible
|
||||||
|
## resolutions of the ``address`` until it succeeds, meaning that it
|
||||||
|
## seamlessly works with both IPv4 and IPv6.
|
||||||
|
## Returns the async file descriptor, registered in the dispatcher of
|
||||||
|
## the current thread, ready to send or receive data.
|
||||||
|
let retFuture = newFuture[AsyncFD]("dial")
|
||||||
|
result = retFuture
|
||||||
|
let sockType = protocol.toSockType()
|
||||||
|
|
||||||
|
let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol)
|
||||||
|
asyncAddrInfoLoop(aiList, noFD, protocol)
|
||||||
|
|
||||||
|
proc connect*(socket: AsyncFD, address: string, port: Port,
|
||||||
|
domain = Domain.AF_INET): Future[void] =
|
||||||
|
let retFuture = newFuture[void]("connect")
|
||||||
|
result = retFuture
|
||||||
|
|
||||||
|
when defined(windows):
|
||||||
|
verifyPresence(socket)
|
||||||
|
else:
|
||||||
|
assert getSockDomain(socket.SocketHandle) == domain
|
||||||
|
|
||||||
|
let aiList = getAddrInfo(address, port, domain)
|
||||||
|
when defined(windows):
|
||||||
|
socket.SocketHandle.bindToDomain(domain)
|
||||||
|
asyncAddrInfoLoop(aiList, socket)
|
||||||
|
|
@ -1213,22 +1213,22 @@ when not defined(js):
|
||||||
proc parseJson*(s: Stream, filename: string): JsonNode =
|
proc parseJson*(s: Stream, filename: string): JsonNode =
|
||||||
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
## Parses from a stream `s` into a `JsonNode`. `filename` is only needed
|
||||||
## for nice error messages.
|
## for nice error messages.
|
||||||
## If `s` contains extra data, it will raising `JsonParsingError`.
|
## If `s` contains extra data, it will raise `JsonParsingError`.
|
||||||
var p: JsonParser
|
var p: JsonParser
|
||||||
p.open(s, filename)
|
p.open(s, filename)
|
||||||
defer: p.close()
|
defer: p.close()
|
||||||
discard getTok(p) # read first token
|
discard getTok(p) # read first token
|
||||||
result = p.parseJson()
|
result = p.parseJson()
|
||||||
eat(p, tkEof) # check there are no exstra data
|
eat(p, tkEof) # check if there is no extra data
|
||||||
|
|
||||||
proc parseJson*(buffer: string): JsonNode =
|
proc parseJson*(buffer: string): JsonNode =
|
||||||
## Parses JSON from `buffer`.
|
## Parses JSON from `buffer`.
|
||||||
## If `buffer` contains extra data, it will raising `JsonParsingError`.
|
## If `buffer` contains extra data, it will raise `JsonParsingError`.
|
||||||
result = parseJson(newStringStream(buffer), "input")
|
result = parseJson(newStringStream(buffer), "input")
|
||||||
|
|
||||||
proc parseFile*(filename: string): JsonNode =
|
proc parseFile*(filename: string): JsonNode =
|
||||||
## Parses `file` into a `JsonNode`.
|
## Parses `file` into a `JsonNode`.
|
||||||
## If `file` contains extra data, it will raising `JsonParsingError`.
|
## If `file` contains extra data, it will raise `JsonParsingError`.
|
||||||
var stream = newFileStream(filename, fmRead)
|
var stream = newFileStream(filename, fmRead)
|
||||||
if stream == nil:
|
if stream == nil:
|
||||||
raise newException(IOError, "cannot read from file: " & filename)
|
raise newException(IOError, "cannot read from file: " & filename)
|
||||||
|
|
@ -1502,7 +1502,7 @@ proc processObjField(field, jsonNode: NimNode): seq[NimNode] =
|
||||||
doAssert result.len > 0
|
doAssert result.len > 0
|
||||||
|
|
||||||
proc processType(typeName: NimNode, obj: NimNode,
|
proc processType(typeName: NimNode, obj: NimNode,
|
||||||
jsonNode: NimNode): NimNode {.compileTime.} =
|
jsonNode: NimNode, isRef: bool): NimNode {.compileTime.} =
|
||||||
## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
|
## Process a type such as ``Sym "float"`` or ``ObjectTy ...``.
|
||||||
##
|
##
|
||||||
## Sample ``ObjectTy``:
|
## Sample ``ObjectTy``:
|
||||||
|
|
@ -1524,6 +1524,20 @@ proc processType(typeName: NimNode, obj: NimNode,
|
||||||
for field in obj[2]:
|
for field in obj[2]:
|
||||||
let nodes = processObjField(field, jsonNode)
|
let nodes = processObjField(field, jsonNode)
|
||||||
result.add(nodes)
|
result.add(nodes)
|
||||||
|
|
||||||
|
# Object might be null. So we need to check for that.
|
||||||
|
if isRef:
|
||||||
|
result = quote do:
|
||||||
|
verifyJsonKind(`jsonNode`, {JObject, JNull}, astToStr(`jsonNode`))
|
||||||
|
if `jsonNode`.kind == JNull:
|
||||||
|
nil
|
||||||
|
else:
|
||||||
|
`result`
|
||||||
|
else:
|
||||||
|
result = quote do:
|
||||||
|
verifyJsonKind(`jsonNode`, {JObject}, astToStr(`jsonNode`));
|
||||||
|
`result`
|
||||||
|
|
||||||
of nnkEnumTy:
|
of nnkEnumTy:
|
||||||
let instType = toIdentNode(getTypeInst(typeName))
|
let instType = toIdentNode(getTypeInst(typeName))
|
||||||
let getEnumCall = createGetEnumCall(jsonNode, instType)
|
let getEnumCall = createGetEnumCall(jsonNode, instType)
|
||||||
|
|
@ -1536,8 +1550,8 @@ proc processType(typeName: NimNode, obj: NimNode,
|
||||||
of "float":
|
of "float":
|
||||||
result = quote do:
|
result = quote do:
|
||||||
(
|
(
|
||||||
verifyJsonKind(`jsonNode`, {JFloat}, astToStr(`jsonNode`));
|
verifyJsonKind(`jsonNode`, {JFloat, JInt}, astToStr(`jsonNode`));
|
||||||
`jsonNode`.fnum
|
if `jsonNode`.kind == JFloat: `jsonNode`.fnum else: `jsonNode`.num.float
|
||||||
)
|
)
|
||||||
of "string":
|
of "string":
|
||||||
result = quote do:
|
result = quote do:
|
||||||
|
|
@ -1551,6 +1565,12 @@ proc processType(typeName: NimNode, obj: NimNode,
|
||||||
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
||||||
`jsonNode`.num.int
|
`jsonNode`.num.int
|
||||||
)
|
)
|
||||||
|
of "biggestint":
|
||||||
|
result = quote do:
|
||||||
|
(
|
||||||
|
verifyJsonKind(`jsonNode`, {JInt}, astToStr(`jsonNode`));
|
||||||
|
`jsonNode`.num
|
||||||
|
)
|
||||||
of "bool":
|
of "bool":
|
||||||
result = quote do:
|
result = quote do:
|
||||||
(
|
(
|
||||||
|
|
@ -1585,7 +1605,7 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
typeName = typeName[0 .. ^12]
|
typeName = typeName[0 .. ^12]
|
||||||
|
|
||||||
let obj = getType(typeSym[1])
|
let obj = getType(typeSym[1])
|
||||||
result = processType(newIdentNode(typeName), obj, jsonNode)
|
result = processType(newIdentNode(typeName), obj, jsonNode, true)
|
||||||
of "seq":
|
of "seq":
|
||||||
let seqT = typeSym[1]
|
let seqT = typeSym[1]
|
||||||
let forLoopI = newIdentNode("i")
|
let forLoopI = newIdentNode("i")
|
||||||
|
|
@ -1605,17 +1625,21 @@ proc createConstructor(typeSym, jsonNode: NimNode): NimNode =
|
||||||
else:
|
else:
|
||||||
# Generic type.
|
# Generic type.
|
||||||
let obj = getType(typeSym)
|
let obj = getType(typeSym)
|
||||||
result = processType(typeSym, obj, jsonNode)
|
result = processType(typeSym, obj, jsonNode, false)
|
||||||
of nnkSym:
|
of nnkSym:
|
||||||
let obj = getType(typeSym)
|
let obj = getType(typeSym)
|
||||||
result = processType(typeSym, obj, jsonNode)
|
if obj.kind == nnkBracketExpr:
|
||||||
|
# When `Sym "Foo"` turns out to be a `ref object`.
|
||||||
|
result = createConstructor(obj, jsonNode)
|
||||||
|
else:
|
||||||
|
result = processType(typeSym, obj, jsonNode, false)
|
||||||
else:
|
else:
|
||||||
doAssert false, "Unable to create constructor for: " & $typeSym.kind
|
doAssert false, "Unable to create constructor for: " & $typeSym.kind
|
||||||
|
|
||||||
doAssert(not result.isNil(), "Constructor not initialised.")
|
doAssert(not result.isNil(), "Constructor not initialised.")
|
||||||
|
|
||||||
proc postProcess(node: NimNode): NimNode
|
proc postProcess(node: NimNode): NimNode
|
||||||
proc postProcessValue(value: NimNode, depth=0): NimNode =
|
proc postProcessValue(value: NimNode): NimNode =
|
||||||
## Looks for object constructors and calls the ``postProcess`` procedure
|
## Looks for object constructors and calls the ``postProcess`` procedure
|
||||||
## on them. Otherwise it just returns the node as-is.
|
## on them. Otherwise it just returns the node as-is.
|
||||||
case value.kind
|
case value.kind
|
||||||
|
|
@ -1736,9 +1760,10 @@ macro to*(node: JsonNode, T: typedesc): untyped =
|
||||||
doAssert(($typeNode[0]).normalize == "typedesc")
|
doAssert(($typeNode[0]).normalize == "typedesc")
|
||||||
|
|
||||||
result = createConstructor(typeNode[1], node)
|
result = createConstructor(typeNode[1], node)
|
||||||
result = postProcess(result)
|
# TODO: Rename postProcessValue and move it (?)
|
||||||
|
result = postProcessValue(result)
|
||||||
|
|
||||||
#echo(toStrLit(result))
|
# echo(toStrLit(result))
|
||||||
|
|
||||||
when false:
|
when false:
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
# TODO: Clean up the exports a bit and everything else in general.
|
# TODO: Clean up the exports a bit and everything else in general.
|
||||||
|
|
||||||
import os
|
import os, options
|
||||||
|
|
||||||
when hostOS == "solaris":
|
when hostOS == "solaris":
|
||||||
{.passl: "-lsocket -lnsl".}
|
{.passl: "-lsocket -lnsl".}
|
||||||
|
|
@ -52,9 +52,11 @@ type
|
||||||
Domain* = enum ## domain, which specifies the protocol family of the
|
Domain* = enum ## domain, which specifies the protocol family of the
|
||||||
## created socket. Other domains than those that are listed
|
## created socket. Other domains than those that are listed
|
||||||
## here are unsupported.
|
## here are unsupported.
|
||||||
AF_UNIX, ## for local socket (using a file). Unsupported on Windows.
|
AF_UNSPEC = 0, ## unspecified domain (can be detected automatically by
|
||||||
|
## some procedures, such as getaddrinfo)
|
||||||
|
AF_UNIX = 1, ## for local socket (using a file). Unsupported on Windows.
|
||||||
AF_INET = 2, ## for network protocol IPv4 or
|
AF_INET = 2, ## for network protocol IPv4 or
|
||||||
AF_INET6 = 23 ## for network protocol IPv6.
|
AF_INET6 = when defined(macosx): 30 else: 23 ## for network protocol IPv6.
|
||||||
|
|
||||||
SockType* = enum ## second argument to `socket` proc
|
SockType* = enum ## second argument to `socket` proc
|
||||||
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
SOCK_STREAM = 1, ## reliable stream-oriented service or Stream Sockets
|
||||||
|
|
@ -125,10 +127,19 @@ proc toInt*(p: Protocol): cint
|
||||||
when not useWinVersion:
|
when not useWinVersion:
|
||||||
proc toInt(domain: Domain): cshort =
|
proc toInt(domain: Domain): cshort =
|
||||||
case domain
|
case domain
|
||||||
|
of AF_UNSPEC: result = posix.AF_UNSPEC.cshort
|
||||||
of AF_UNIX: result = posix.AF_UNIX.cshort
|
of AF_UNIX: result = posix.AF_UNIX.cshort
|
||||||
of AF_INET: result = posix.AF_INET.cshort
|
of AF_INET: result = posix.AF_INET.cshort
|
||||||
of AF_INET6: result = posix.AF_INET6.cshort
|
of AF_INET6: result = posix.AF_INET6.cshort
|
||||||
else: discard
|
|
||||||
|
proc toKnownDomain*(family: cint): Option[Domain] =
|
||||||
|
## Converts the platform-dependent ``cint`` to the Domain or none(),
|
||||||
|
## if the ``cint`` is not known.
|
||||||
|
result = if family == posix.AF_UNSPEC: some(Domain.AF_UNSPEC)
|
||||||
|
elif family == posix.AF_UNIX: some(Domain.AF_UNIX)
|
||||||
|
elif family == posix.AF_INET: some(Domain.AF_INET)
|
||||||
|
elif family == posix.AF_INET6: some(Domain.AF_INET6)
|
||||||
|
else: none(Domain)
|
||||||
|
|
||||||
proc toInt(typ: SockType): cint =
|
proc toInt(typ: SockType): cint =
|
||||||
case typ
|
case typ
|
||||||
|
|
@ -136,7 +147,6 @@ when not useWinVersion:
|
||||||
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
of SOCK_DGRAM: result = posix.SOCK_DGRAM
|
||||||
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
|
of SOCK_SEQPACKET: result = posix.SOCK_SEQPACKET
|
||||||
of SOCK_RAW: result = posix.SOCK_RAW
|
of SOCK_RAW: result = posix.SOCK_RAW
|
||||||
else: discard
|
|
||||||
|
|
||||||
proc toInt(p: Protocol): cint =
|
proc toInt(p: Protocol): cint =
|
||||||
case p
|
case p
|
||||||
|
|
@ -146,18 +156,33 @@ when not useWinVersion:
|
||||||
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
|
of IPPROTO_IPV6: result = posix.IPPROTO_IPV6
|
||||||
of IPPROTO_RAW: result = posix.IPPROTO_RAW
|
of IPPROTO_RAW: result = posix.IPPROTO_RAW
|
||||||
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
|
of IPPROTO_ICMP: result = posix.IPPROTO_ICMP
|
||||||
else: discard
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc toInt(domain: Domain): cshort =
|
proc toInt(domain: Domain): cshort =
|
||||||
result = toU16(ord(domain))
|
result = toU16(ord(domain))
|
||||||
|
|
||||||
|
proc toKnownDomain*(family: cint): Option[Domain] =
|
||||||
|
## Converts the platform-dependent ``cint`` to the Domain or none(),
|
||||||
|
## if the ``cint`` is not known.
|
||||||
|
result = if family == winlean.AF_UNSPEC: some(Domain.AF_UNSPEC)
|
||||||
|
elif family == winlean.AF_INET: some(Domain.AF_INET)
|
||||||
|
elif family == winlean.AF_INET6: some(Domain.AF_INET6)
|
||||||
|
else: none(Domain)
|
||||||
|
|
||||||
proc toInt(typ: SockType): cint =
|
proc toInt(typ: SockType): cint =
|
||||||
result = cint(ord(typ))
|
result = cint(ord(typ))
|
||||||
|
|
||||||
proc toInt(p: Protocol): cint =
|
proc toInt(p: Protocol): cint =
|
||||||
result = cint(ord(p))
|
result = cint(ord(p))
|
||||||
|
|
||||||
|
proc toSockType*(protocol: Protocol): SockType =
|
||||||
|
result = case protocol
|
||||||
|
of IPPROTO_TCP:
|
||||||
|
SOCK_STREAM
|
||||||
|
of IPPROTO_UDP:
|
||||||
|
SOCK_DGRAM
|
||||||
|
of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP:
|
||||||
|
SOCK_RAW
|
||||||
|
|
||||||
proc newNativeSocket*(domain: Domain = AF_INET,
|
proc newNativeSocket*(domain: Domain = AF_INET,
|
||||||
sockType: SockType = SOCK_STREAM,
|
sockType: SockType = SOCK_STREAM,
|
||||||
|
|
@ -392,14 +417,14 @@ proc getHostname*(): string {.tags: [ReadIOEffect].} =
|
||||||
|
|
||||||
proc getSockDomain*(socket: SocketHandle): Domain =
|
proc getSockDomain*(socket: SocketHandle): Domain =
|
||||||
## returns the socket's domain (AF_INET or AF_INET6).
|
## returns the socket's domain (AF_INET or AF_INET6).
|
||||||
var name: SockAddr
|
var name: Sockaddr_in6
|
||||||
var namelen = sizeof(name).SockLen
|
var namelen = sizeof(name).SockLen
|
||||||
if getsockname(socket, cast[ptr SockAddr](addr(name)),
|
if getsockname(socket, cast[ptr SockAddr](addr(name)),
|
||||||
addr(namelen)) == -1'i32:
|
addr(namelen)) == -1'i32:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
if name.sa_family == nativeAfInet:
|
if name.sin6_family == nativeAfInet:
|
||||||
result = AF_INET
|
result = AF_INET
|
||||||
elif name.sa_family == nativeAfInet6:
|
elif name.sin6_family == nativeAfInet6:
|
||||||
result = AF_INET6
|
result = AF_INET6
|
||||||
else:
|
else:
|
||||||
raiseOSError(osLastError(), "unknown socket family in getSockFamily")
|
raiseOSError(osLastError(), "unknown socket family in getSockFamily")
|
||||||
|
|
@ -410,17 +435,23 @@ proc getAddrString*(sockAddr: ptr SockAddr): string =
|
||||||
if sockAddr.sa_family == nativeAfInet:
|
if sockAddr.sa_family == nativeAfInet:
|
||||||
result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
|
result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr)
|
||||||
elif sockAddr.sa_family == nativeAfInet6:
|
elif sockAddr.sa_family == nativeAfInet6:
|
||||||
when not useWinVersion:
|
let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN
|
||||||
# TODO: Windows
|
else: 46 # it's actually 46 in both cases
|
||||||
result = newString(posix.INET6_ADDRSTRLEN)
|
result = newString(addrLen)
|
||||||
let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
|
let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr
|
||||||
discard posix.inet_ntop(posix.AF_INET6, addr6, result.cstring,
|
when not useWinVersion:
|
||||||
result.len.int32)
|
if posix.inet_ntop(posix.AF_INET6, addr6, addr result[0],
|
||||||
|
result.len.int32) == nil:
|
||||||
|
raiseOSError(osLastError())
|
||||||
if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
|
if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0:
|
||||||
result = result.substr("::ffff:".len)
|
result = result.substr("::ffff:".len)
|
||||||
else:
|
else:
|
||||||
raiseOSError(osLastError(), "unknown socket family in getAddrString")
|
if winlean.inet_ntop(winlean.AF_INET6, addr6, addr result[0],
|
||||||
|
result.len.int32) == nil:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
setLen(result, len(cstring(result)))
|
||||||
|
else:
|
||||||
|
raise newException(IOError, "Unknown socket family in getAddrString")
|
||||||
|
|
||||||
proc getSockName*(socket: SocketHandle): Port =
|
proc getSockName*(socket: SocketHandle): Port =
|
||||||
## returns the socket's associated port number.
|
## returns the socket's associated port number.
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
##
|
##
|
||||||
|
|
||||||
{.deadCodeElim: on.}
|
{.deadCodeElim: on.}
|
||||||
import nativesockets, os, strutils, parseutils, times, sets
|
import nativesockets, os, strutils, parseutils, times, sets, options
|
||||||
export Port, `$`, `==`
|
export Port, `$`, `==`
|
||||||
export Domain, SockType, Protocol
|
export Domain, SockType, Protocol
|
||||||
|
|
||||||
|
|
@ -669,7 +669,7 @@ proc close*(socket: Socket) =
|
||||||
## Closes a socket.
|
## Closes a socket.
|
||||||
try:
|
try:
|
||||||
when defineSsl:
|
when defineSsl:
|
||||||
if socket.isSSL:
|
if socket.isSSL and socket.sslHandle != nil:
|
||||||
ErrClearError()
|
ErrClearError()
|
||||||
# As we are closing the underlying socket immediately afterwards,
|
# As we are closing the underlying socket immediately afterwards,
|
||||||
# it is valid, under the TLS standard, to perform a unidirectional
|
# it is valid, under the TLS standard, to perform a unidirectional
|
||||||
|
|
@ -1477,6 +1477,63 @@ proc isIpAddress*(address_str: string): bool {.tags: [].} =
|
||||||
return false
|
return false
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
proc dial*(address: string, port: Port,
|
||||||
|
protocol = IPPROTO_TCP, buffered = true): Socket
|
||||||
|
{.tags: [ReadIOEffect, WriteIOEffect].} =
|
||||||
|
## Establishes connection to the specified ``address``:``port`` pair via the
|
||||||
|
## specified protocol. The procedure iterates through possible
|
||||||
|
## resolutions of the ``address`` until it succeeds, meaning that it
|
||||||
|
## seamlessly works with both IPv4 and IPv6.
|
||||||
|
## Returns Socket ready to send or receive data.
|
||||||
|
let sockType = protocol.toSockType()
|
||||||
|
|
||||||
|
let aiList = getAddrInfo(address, port, AF_UNSPEC, sockType, protocol)
|
||||||
|
|
||||||
|
var fdPerDomain: array[low(Domain).ord..high(Domain).ord, SocketHandle]
|
||||||
|
for i in low(fdPerDomain)..high(fdPerDomain):
|
||||||
|
fdPerDomain[i] = osInvalidSocket
|
||||||
|
template closeUnusedFds(domainToKeep = -1) {.dirty.} =
|
||||||
|
for i, fd in fdPerDomain:
|
||||||
|
if fd != osInvalidSocket and i != domainToKeep:
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
var success = false
|
||||||
|
var lastError: OSErrorCode
|
||||||
|
var it = aiList
|
||||||
|
var domain: Domain
|
||||||
|
var lastFd: SocketHandle
|
||||||
|
while it != nil:
|
||||||
|
let domainOpt = it.ai_family.toKnownDomain()
|
||||||
|
if domainOpt.isNone:
|
||||||
|
it = it.ai_next
|
||||||
|
continue
|
||||||
|
domain = domainOpt.unsafeGet()
|
||||||
|
lastFd = fdPerDomain[ord(domain)]
|
||||||
|
if lastFd == osInvalidSocket:
|
||||||
|
lastFd = newNativeSocket(domain, sockType, protocol)
|
||||||
|
if lastFd == osInvalidSocket:
|
||||||
|
# we always raise if socket creation failed, because it means a
|
||||||
|
# network system problem (e.g. not enough FDs), and not an unreachable
|
||||||
|
# address.
|
||||||
|
let err = osLastError()
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
closeUnusedFds()
|
||||||
|
raiseOSError(err)
|
||||||
|
fdPerDomain[ord(domain)] = lastFd
|
||||||
|
if connect(lastFd, it.ai_addr, it.ai_addrlen.SockLen) == 0'i32:
|
||||||
|
success = true
|
||||||
|
break
|
||||||
|
lastError = osLastError()
|
||||||
|
it = it.ai_next
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
closeUnusedFds(ord(domain))
|
||||||
|
|
||||||
|
if success:
|
||||||
|
result = newSocket(lastFd, domain, sockType, protocol)
|
||||||
|
elif lastError != 0.OSErrorCode:
|
||||||
|
raiseOSError(lastError)
|
||||||
|
else:
|
||||||
|
raise newException(IOError, "Couldn't resolve address: " & address)
|
||||||
|
|
||||||
proc connect*(socket: Socket, address: string,
|
proc connect*(socket: Socket, address: string,
|
||||||
port = Port(0)) {.tags: [ReadIOEffect].} =
|
port = Port(0)) {.tags: [ReadIOEffect].} =
|
||||||
|
|
|
||||||
|
|
@ -69,10 +69,9 @@ var
|
||||||
proc genOid*(): Oid =
|
proc genOid*(): Oid =
|
||||||
## generates a new OID.
|
## generates a new OID.
|
||||||
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
|
||||||
proc gettime(dummy: ptr cint): cint {.importc: "time", header: "<time.h>".}
|
|
||||||
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
|
proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
|
||||||
|
|
||||||
var t = gettime(nil)
|
var t = getTime().int32
|
||||||
|
|
||||||
var i = int32(atomicInc(incr))
|
var i = int32(atomicInc(incr))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -569,7 +569,7 @@ when declared(getEnv) or defined(nimscript):
|
||||||
## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
|
## ``["exe", "cmd", "bat"]``, on Posix ``[""]``.
|
||||||
|
|
||||||
proc findExe*(exe: string, followSymlinks: bool = true;
|
proc findExe*(exe: string, followSymlinks: bool = true;
|
||||||
extensions=ExeExts): string {.
|
extensions: openarray[string]=ExeExts): string {.
|
||||||
tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} =
|
tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect].} =
|
||||||
## Searches for `exe` in the current working directory and then
|
## Searches for `exe` in the current working directory and then
|
||||||
## in directories listed in the ``PATH`` environment variable.
|
## in directories listed in the ``PATH`` environment variable.
|
||||||
|
|
|
||||||
|
|
@ -209,9 +209,16 @@ proc waitForExit*(p: Process, timeout: int = -1): int {.rtl,
|
||||||
##
|
##
|
||||||
## **Warning**: Be careful when using waitForExit for processes created without
|
## **Warning**: Be careful when using waitForExit for processes created without
|
||||||
## poParentStreams because they may fill output buffers, causing deadlock.
|
## poParentStreams because they may fill output buffers, causing deadlock.
|
||||||
|
##
|
||||||
|
## On posix, if the process has exited because of a signal, 128 + signal
|
||||||
|
## number will be returned.
|
||||||
|
|
||||||
|
|
||||||
proc peekExitCode*(p: Process): int {.tags: [].}
|
proc peekExitCode*(p: Process): int {.tags: [].}
|
||||||
## return -1 if the process is still running. Otherwise the process' exit code
|
## return -1 if the process is still running. Otherwise the process' exit code
|
||||||
|
##
|
||||||
|
## On posix, if the process has exited because of a signal, 128 + signal
|
||||||
|
## number will be returned.
|
||||||
|
|
||||||
proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
|
proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].}
|
||||||
## returns ``p``'s input stream for writing to.
|
## returns ``p``'s input stream for writing to.
|
||||||
|
|
@ -679,6 +686,16 @@ elif not defined(useNimRtl):
|
||||||
readIdx = 0
|
readIdx = 0
|
||||||
writeIdx = 1
|
writeIdx = 1
|
||||||
|
|
||||||
|
proc isExitStatus(status: cint): bool =
|
||||||
|
WIFEXITED(status) or WIFSIGNALED(status)
|
||||||
|
|
||||||
|
proc exitStatus(status: cint): cint =
|
||||||
|
if WIFSIGNALED(status):
|
||||||
|
# like the shell!
|
||||||
|
128 + WTERMSIG(status)
|
||||||
|
else:
|
||||||
|
WEXITSTATUS(status)
|
||||||
|
|
||||||
proc envToCStringArray(t: StringTableRef): cstringArray =
|
proc envToCStringArray(t: StringTableRef): cstringArray =
|
||||||
result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
|
result = cast[cstringArray](alloc0((t.len + 1) * sizeof(cstring)))
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -967,7 +984,7 @@ elif not defined(useNimRtl):
|
||||||
var status : cint = 1
|
var status : cint = 1
|
||||||
ret = waitpid(p.id, status, WNOHANG)
|
ret = waitpid(p.id, status, WNOHANG)
|
||||||
if ret == int(p.id):
|
if ret == int(p.id):
|
||||||
if WIFEXITED(status):
|
if isExitStatus(status):
|
||||||
p.exitStatus = status
|
p.exitStatus = status
|
||||||
return false
|
return false
|
||||||
else:
|
else:
|
||||||
|
|
@ -990,7 +1007,9 @@ elif not defined(useNimRtl):
|
||||||
import kqueue, times
|
import kqueue, times
|
||||||
|
|
||||||
proc waitForExit(p: Process, timeout: int = -1): int =
|
proc waitForExit(p: Process, timeout: int = -1): int =
|
||||||
if p.exitStatus != -3: return((p.exitStatus and 0xFF00) shr 8)
|
if p.exitStatus != -3:
|
||||||
|
return exitStatus(p.exitStatus)
|
||||||
|
|
||||||
if timeout == -1:
|
if timeout == -1:
|
||||||
var status : cint = 1
|
var status : cint = 1
|
||||||
if waitpid(p.id, status, 0) < 0:
|
if waitpid(p.id, status, 0) < 0:
|
||||||
|
|
@ -1041,7 +1060,7 @@ elif not defined(useNimRtl):
|
||||||
finally:
|
finally:
|
||||||
discard posix.close(kqFD)
|
discard posix.close(kqFD)
|
||||||
|
|
||||||
result = ((p.exitStatus and 0xFF00) shr 8)
|
result = exitStatus(p.exitStatus)
|
||||||
else:
|
else:
|
||||||
import times
|
import times
|
||||||
|
|
||||||
|
|
@ -1077,7 +1096,9 @@ elif not defined(useNimRtl):
|
||||||
# ``waitPid`` fails if the process is not running anymore. But then
|
# ``waitPid`` fails if the process is not running anymore. But then
|
||||||
# ``running`` probably set ``p.exitStatus`` for us. Since ``p.exitStatus`` is
|
# ``running`` probably set ``p.exitStatus`` for us. Since ``p.exitStatus`` is
|
||||||
# initialized with -3, wrong success exit codes are prevented.
|
# initialized with -3, wrong success exit codes are prevented.
|
||||||
if p.exitStatus != -3: return((p.exitStatus and 0xFF00) shr 8)
|
if p.exitStatus != -3:
|
||||||
|
return exitStatus(p.exitStatus)
|
||||||
|
|
||||||
if timeout == -1:
|
if timeout == -1:
|
||||||
var status : cint = 1
|
var status : cint = 1
|
||||||
if waitpid(p.id, status, 0) < 0:
|
if waitpid(p.id, status, 0) < 0:
|
||||||
|
|
@ -1151,17 +1172,19 @@ elif not defined(useNimRtl):
|
||||||
if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
|
if sigprocmask(SIG_UNBLOCK, nmask, omask) == -1:
|
||||||
raiseOSError(osLastError())
|
raiseOSError(osLastError())
|
||||||
|
|
||||||
result = ((p.exitStatus and 0xFF00) shr 8)
|
result = exitStatus(p.exitStatus)
|
||||||
|
|
||||||
proc peekExitCode(p: Process): int =
|
proc peekExitCode(p: Process): int =
|
||||||
var status = cint(0)
|
var status = cint(0)
|
||||||
result = -1
|
result = -1
|
||||||
if p.exitStatus != -3: return((p.exitStatus and 0xFF00) shr 8)
|
if p.exitStatus != -3:
|
||||||
|
return exitStatus(p.exitStatus)
|
||||||
|
|
||||||
var ret = waitpid(p.id, status, WNOHANG)
|
var ret = waitpid(p.id, status, WNOHANG)
|
||||||
if ret > 0:
|
if ret > 0:
|
||||||
if WIFEXITED(status):
|
if isExitStatus(status):
|
||||||
p.exitStatus = status
|
p.exitStatus = status
|
||||||
result = (status and 0xFF00) shr 8
|
result = exitStatus(status)
|
||||||
|
|
||||||
proc createStream(stream: var Stream, handle: var FileHandle,
|
proc createStream(stream: var Stream, handle: var FileHandle,
|
||||||
fileMode: FileMode) =
|
fileMode: FileMode) =
|
||||||
|
|
@ -1189,7 +1212,8 @@ elif not defined(useNimRtl):
|
||||||
|
|
||||||
proc execCmd(command: string): int =
|
proc execCmd(command: string): int =
|
||||||
when defined(linux):
|
when defined(linux):
|
||||||
result = csystem(command) shr 8
|
let tmp = csystem(command)
|
||||||
|
result = if tmp == -1: tmp else: exitStatus(tmp)
|
||||||
else:
|
else:
|
||||||
result = csystem(command)
|
result = csystem(command)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ proc add*(url: var Url, a: Url) {.deprecated.} =
|
||||||
proc parseAuthority(authority: string, result: var Uri) =
|
proc parseAuthority(authority: string, result: var Uri) =
|
||||||
var i = 0
|
var i = 0
|
||||||
var inPort = false
|
var inPort = false
|
||||||
|
var inIPv6 = false
|
||||||
while true:
|
while true:
|
||||||
case authority[i]
|
case authority[i]
|
||||||
of '@':
|
of '@':
|
||||||
|
|
@ -59,7 +60,14 @@ proc parseAuthority(authority: string, result: var Uri) =
|
||||||
result.hostname.setLen(0)
|
result.hostname.setLen(0)
|
||||||
inPort = false
|
inPort = false
|
||||||
of ':':
|
of ':':
|
||||||
|
if inIPv6:
|
||||||
|
result.hostname.add(authority[i])
|
||||||
|
else:
|
||||||
inPort = true
|
inPort = true
|
||||||
|
of '[':
|
||||||
|
inIPv6 = true
|
||||||
|
of ']':
|
||||||
|
inIPv6 = false
|
||||||
of '\0': break
|
of '\0': break
|
||||||
else:
|
else:
|
||||||
if inPort:
|
if inPort:
|
||||||
|
|
@ -345,6 +353,17 @@ when isMainModule:
|
||||||
doAssert test.anchor == "nose"
|
doAssert test.anchor == "nose"
|
||||||
doAssert($test == str)
|
doAssert($test == str)
|
||||||
|
|
||||||
|
block:
|
||||||
|
# IPv6 address
|
||||||
|
let str = "foo://[::1]:1234/bar?baz=true&qux#quux"
|
||||||
|
let uri = parseUri(str)
|
||||||
|
doAssert uri.scheme == "foo"
|
||||||
|
doAssert uri.hostname == "::1"
|
||||||
|
doAssert uri.port == "1234"
|
||||||
|
doAssert uri.path == "/bar"
|
||||||
|
doAssert uri.query == "baz=true&qux"
|
||||||
|
doAssert uri.anchor == "quux"
|
||||||
|
|
||||||
block:
|
block:
|
||||||
let str = "urn:example:animal:ferret:nose"
|
let str = "urn:example:animal:ferret:nose"
|
||||||
let test = parseUri(str)
|
let test = parseUri(str)
|
||||||
|
|
|
||||||
|
|
@ -2016,6 +2016,12 @@ proc min*(x, y: float): float {.magic: "MinF64", noSideEffect.} =
|
||||||
if x <= y: x else: y
|
if x <= y: x else: y
|
||||||
proc max*(x, y: float): float {.magic: "MaxF64", noSideEffect.} =
|
proc max*(x, y: float): float {.magic: "MaxF64", noSideEffect.} =
|
||||||
if y <= x: x else: y
|
if y <= x: x else: y
|
||||||
|
|
||||||
|
proc min*[T](x, y: T): T =
|
||||||
|
if x <= y: x else: y
|
||||||
|
|
||||||
|
proc max*[T](x, y: T): T =
|
||||||
|
if y <= x: x else: y
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
||||||
proc clamp*[T](x, a, b: T): T =
|
proc clamp*[T](x, a, b: T): T =
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ proc raiseIndexError() {.compilerproc, noreturn.} =
|
||||||
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
|
||||||
raise newException(FieldError, f & " is not accessible")
|
raise newException(FieldError, f & " is not accessible")
|
||||||
|
|
||||||
proc SetConstr() {.varargs, asmNoStackFrame, compilerproc.} =
|
proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} =
|
||||||
when defined(nimphp):
|
when defined(nimphp):
|
||||||
asm """
|
asm """
|
||||||
$args = func_get_args();
|
$args = func_get_args();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
include "system/inclrtl"
|
include "system/inclrtl"
|
||||||
|
|
||||||
import os, oids, tables, strutils, times, heapqueue, lists
|
import os, oids, tables, strutils, times, heapqueue, lists, options
|
||||||
|
|
||||||
import nativesockets, net, deques
|
import nativesockets, net, deques
|
||||||
|
|
||||||
|
|
@ -325,68 +325,6 @@ when defined(windows) or defined(nimdoc):
|
||||||
getAcceptExSockAddrs = cast[WSAPROC_GETACCEPTEXSOCKADDRS](fun)
|
getAcceptExSockAddrs = cast[WSAPROC_GETACCEPTEXSOCKADDRS](fun)
|
||||||
close(dummySock)
|
close(dummySock)
|
||||||
|
|
||||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
|
||||||
domain = nativesockets.AF_INET): Future[void] =
|
|
||||||
## Connects ``socket`` to server at ``address:port``.
|
|
||||||
##
|
|
||||||
## Returns a ``Future`` which will complete when the connection succeeds
|
|
||||||
## or an error occurs.
|
|
||||||
verifyPresence(socket)
|
|
||||||
var retFuture = newFuture[void]("connect")
|
|
||||||
# Apparently ``ConnectEx`` expects the socket to be initially bound:
|
|
||||||
var saddr: Sockaddr_in
|
|
||||||
saddr.sin_family = int16(toInt(domain))
|
|
||||||
saddr.sin_port = 0
|
|
||||||
saddr.sin_addr.s_addr = INADDR_ANY
|
|
||||||
if bindAddr(socket.SocketHandle, cast[ptr SockAddr](addr(saddr)),
|
|
||||||
sizeof(saddr).SockLen) < 0'i32:
|
|
||||||
raiseOSError(osLastError())
|
|
||||||
|
|
||||||
var aiList = getAddrInfo(address, port, domain)
|
|
||||||
var success = false
|
|
||||||
var lastError: OSErrorCode
|
|
||||||
var it = aiList
|
|
||||||
while it != nil:
|
|
||||||
# "the OVERLAPPED structure must remain valid until the I/O completes"
|
|
||||||
# http://blogs.msdn.com/b/oldnewthing/archive/2011/02/02/10123392.aspx
|
|
||||||
var ol = PCustomOverlapped()
|
|
||||||
GC_ref(ol)
|
|
||||||
ol.data = CompletionData(fd: socket, cb:
|
|
||||||
proc (fd: AsyncFD, bytesCount: Dword, errcode: OSErrorCode) =
|
|
||||||
if not retFuture.finished:
|
|
||||||
if errcode == OSErrorCode(-1):
|
|
||||||
retFuture.complete()
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(errcode)))
|
|
||||||
)
|
|
||||||
|
|
||||||
var ret = connectEx(socket.SocketHandle, it.ai_addr,
|
|
||||||
sizeof(Sockaddr_in).cint, nil, 0, nil,
|
|
||||||
cast[POVERLAPPED](ol))
|
|
||||||
if ret:
|
|
||||||
# Request to connect completed immediately.
|
|
||||||
success = true
|
|
||||||
retFuture.complete()
|
|
||||||
# We don't deallocate ``ol`` here because even though this completed
|
|
||||||
# immediately poll will still be notified about its completion and it will
|
|
||||||
# free ``ol``.
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
lastError = osLastError()
|
|
||||||
if lastError.int32 == ERROR_IO_PENDING:
|
|
||||||
# In this case ``ol`` will be deallocated in ``poll``.
|
|
||||||
success = true
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
GC_unref(ol)
|
|
||||||
success = false
|
|
||||||
it = it.ai_next
|
|
||||||
|
|
||||||
freeAddrInfo(aiList)
|
|
||||||
if not success:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc recv*(socket: AsyncFD, size: int,
|
proc recv*(socket: AsyncFD, size: int,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||||
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
## Reads **up to** ``size`` bytes from ``socket``. Returned future will
|
||||||
|
|
@ -739,8 +677,8 @@ when defined(windows) or defined(nimdoc):
|
||||||
var lpOutputBuf = newString(lpOutputLen)
|
var lpOutputBuf = newString(lpOutputLen)
|
||||||
var dwBytesReceived: Dword
|
var dwBytesReceived: Dword
|
||||||
let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
|
let dwReceiveDataLength = 0.Dword # We don't want any data to be read.
|
||||||
let dwLocalAddressLength = Dword(sizeof(Sockaddr_in) + 16)
|
let dwLocalAddressLength = Dword(sizeof(Sockaddr_in6) + 16)
|
||||||
let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in) + 16)
|
let dwRemoteAddressLength = Dword(sizeof(Sockaddr_in6) + 16)
|
||||||
|
|
||||||
template failAccept(errcode) =
|
template failAccept(errcode) =
|
||||||
if flags.isDisconnectionError(errcode):
|
if flags.isDisconnectionError(errcode):
|
||||||
|
|
@ -770,12 +708,14 @@ when defined(windows) or defined(nimdoc):
|
||||||
dwLocalAddressLength, dwRemoteAddressLength,
|
dwLocalAddressLength, dwRemoteAddressLength,
|
||||||
addr localSockaddr, addr localLen,
|
addr localSockaddr, addr localLen,
|
||||||
addr remoteSockaddr, addr remoteLen)
|
addr remoteSockaddr, addr remoteLen)
|
||||||
|
try:
|
||||||
|
let address = getAddrString(remoteSockAddr)
|
||||||
register(clientSock.AsyncFD)
|
register(clientSock.AsyncFD)
|
||||||
# TODO: IPv6. Check ``sa_family``. http://stackoverflow.com/a/9212542/492186
|
retFuture.complete((address: address, client: clientSock.AsyncFD))
|
||||||
retFuture.complete(
|
except:
|
||||||
(address: $inet_ntoa(cast[ptr Sockaddr_in](remoteSockAddr).sin_addr),
|
# getAddrString may raise
|
||||||
client: clientSock.AsyncFD)
|
clientSock.close()
|
||||||
)
|
retFuture.fail(getCurrentException())
|
||||||
|
|
||||||
var ol = PCustomOverlapped()
|
var ol = PCustomOverlapped()
|
||||||
GC_ref(ol)
|
GC_ref(ol)
|
||||||
|
|
@ -808,20 +748,6 @@ when defined(windows) or defined(nimdoc):
|
||||||
|
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain, sockType, protocol: cint): AsyncFD =
|
|
||||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain: Domain = nativesockets.AF_INET,
|
|
||||||
sockType: SockType = SOCK_STREAM,
|
|
||||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
|
||||||
## Creates a new socket and registers it with the dispatcher implicitly.
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc closeSocket*(socket: AsyncFD) =
|
proc closeSocket*(socket: AsyncFD) =
|
||||||
## Closes a socket and ensures that it is unregistered.
|
## Closes a socket and ensures that it is unregistered.
|
||||||
socket.SocketHandle.close()
|
socket.SocketHandle.close()
|
||||||
|
|
@ -1159,23 +1085,6 @@ else:
|
||||||
var data = newAsyncData()
|
var data = newAsyncData()
|
||||||
p.selector.registerHandle(fd.SocketHandle, {}, data)
|
p.selector.registerHandle(fd.SocketHandle, {}, data)
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain: cint, sockType: cint,
|
|
||||||
protocol: cint): AsyncFD =
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
when defined(macosx):
|
|
||||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc newAsyncNativeSocket*(domain: Domain = AF_INET,
|
|
||||||
sockType: SockType = SOCK_STREAM,
|
|
||||||
protocol: Protocol = IPPROTO_TCP): AsyncFD =
|
|
||||||
result = newNativeSocket(domain, sockType, protocol).AsyncFD
|
|
||||||
result.SocketHandle.setBlocking(false)
|
|
||||||
when defined(macosx):
|
|
||||||
result.SocketHandle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1)
|
|
||||||
register(result)
|
|
||||||
|
|
||||||
proc closeSocket*(sock: AsyncFD) =
|
proc closeSocket*(sock: AsyncFD) =
|
||||||
let disp = getGlobalDispatcher()
|
let disp = getGlobalDispatcher()
|
||||||
disp.selector.unregister(sock.SocketHandle)
|
disp.selector.unregister(sock.SocketHandle)
|
||||||
|
|
@ -1331,50 +1240,6 @@ else:
|
||||||
# Callback queue processing
|
# Callback queue processing
|
||||||
processPendingCallbacks(p)
|
processPendingCallbacks(p)
|
||||||
|
|
||||||
proc connect*(socket: AsyncFD, address: string, port: Port,
|
|
||||||
domain = AF_INET): Future[void] =
|
|
||||||
var retFuture = newFuture[void]("connect")
|
|
||||||
|
|
||||||
proc cb(fd: AsyncFD): bool =
|
|
||||||
var ret = SocketHandle(fd).getSockOptInt(cint(SOL_SOCKET), cint(SO_ERROR))
|
|
||||||
if ret == 0:
|
|
||||||
# We have connected.
|
|
||||||
retFuture.complete()
|
|
||||||
return true
|
|
||||||
elif ret == EINTR:
|
|
||||||
# interrupted, keep waiting
|
|
||||||
return false
|
|
||||||
else:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret))))
|
|
||||||
return true
|
|
||||||
|
|
||||||
assert getSockDomain(socket.SocketHandle) == domain
|
|
||||||
var aiList = getAddrInfo(address, port, domain)
|
|
||||||
var success = false
|
|
||||||
var lastError: OSErrorCode
|
|
||||||
var it = aiList
|
|
||||||
while it != nil:
|
|
||||||
var ret = connect(socket.SocketHandle, it.ai_addr, it.ai_addrlen.Socklen)
|
|
||||||
if ret == 0:
|
|
||||||
# Request to connect completed immediately.
|
|
||||||
success = true
|
|
||||||
retFuture.complete()
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
lastError = osLastError()
|
|
||||||
if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS:
|
|
||||||
success = true
|
|
||||||
addWrite(socket, cb)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
success = false
|
|
||||||
it = it.ai_next
|
|
||||||
|
|
||||||
freeAddrInfo(aiList)
|
|
||||||
if not success:
|
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc recv*(socket: AsyncFD, size: int,
|
proc recv*(socket: AsyncFD, size: int,
|
||||||
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
flags = {SocketFlag.SafeDisconn}): Future[string] =
|
||||||
var retFuture = newFuture[string]("recv")
|
var retFuture = newFuture[string]("recv")
|
||||||
|
|
@ -1568,9 +1433,14 @@ else:
|
||||||
else:
|
else:
|
||||||
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
retFuture.fail(newException(OSError, osErrorMsg(lastError)))
|
||||||
else:
|
else:
|
||||||
|
try:
|
||||||
|
let address = getAddrString(cast[ptr SockAddr](addr sockAddress))
|
||||||
register(client.AsyncFD)
|
register(client.AsyncFD)
|
||||||
retFuture.complete((getAddrString(cast[ptr SockAddr](addr sockAddress)),
|
retFuture.complete((address, client.AsyncFD))
|
||||||
client.AsyncFD))
|
except:
|
||||||
|
# getAddrString may raise
|
||||||
|
client.close()
|
||||||
|
retFuture.fail(getCurrentException())
|
||||||
addRead(socket, cb)
|
addRead(socket, cb)
|
||||||
return retFuture
|
return retFuture
|
||||||
|
|
||||||
|
|
@ -1623,6 +1493,9 @@ else:
|
||||||
data.readList.add(cb)
|
data.readList.add(cb)
|
||||||
p.selector.registerEvent(SelectEvent(ev), data)
|
p.selector.registerEvent(SelectEvent(ev), data)
|
||||||
|
|
||||||
|
# Common procedures between current and upcoming asyncdispatch
|
||||||
|
include includes.asynccommon
|
||||||
|
|
||||||
proc sleepAsync*(ms: int): Future[void] =
|
proc sleepAsync*(ms: int): Future[void] =
|
||||||
## Suspends the execution of the current async procedure for the next
|
## Suspends the execution of the current async procedure for the next
|
||||||
## ``ms`` milliseconds.
|
## ``ms`` milliseconds.
|
||||||
|
|
|
||||||
|
|
@ -495,7 +495,7 @@ type
|
||||||
ai_family*: cint ## Address family of socket.
|
ai_family*: cint ## Address family of socket.
|
||||||
ai_socktype*: cint ## Socket type.
|
ai_socktype*: cint ## Socket type.
|
||||||
ai_protocol*: cint ## Protocol of socket.
|
ai_protocol*: cint ## Protocol of socket.
|
||||||
ai_addrlen*: int ## Length of socket address.
|
ai_addrlen*: csize ## Length of socket address.
|
||||||
ai_canonname*: cstring ## Canonical name of service location.
|
ai_canonname*: cstring ## Canonical name of service location.
|
||||||
ai_addr*: ptr SockAddr ## Socket address of socket.
|
ai_addr*: ptr SockAddr ## Socket address of socket.
|
||||||
ai_next*: ptr AddrInfo ## Pointer to next in list.
|
ai_next*: ptr AddrInfo ## Pointer to next in list.
|
||||||
|
|
@ -803,6 +803,7 @@ const
|
||||||
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
|
SIO_GET_EXTENSION_FUNCTION_POINTER* = WSAIORW(IOC_WS2,6).DWORD
|
||||||
SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
|
SO_UPDATE_ACCEPT_CONTEXT* = 0x700B
|
||||||
AI_V4MAPPED* = 0x0008
|
AI_V4MAPPED* = 0x0008
|
||||||
|
AF_UNSPEC* = 0
|
||||||
AF_INET* = 2
|
AF_INET* = 2
|
||||||
AF_INET6* = 23
|
AF_INET6* = 23
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
# ![Logo][image-nim-logo] Nim [![Build Status][badge-nim-travisci]][nim-travisci]
|
# <img src="https://raw.githubusercontent.com/nim-lang/assets/master/Art/logo-crown.png" height="28px"/> Nim [![Build Status][badge-nim-travisci]][nim-travisci]
|
||||||
|
|
||||||
This repository contains the Nim compiler, Nim's stdlib, tools and documentation.
|
This repository contains the Nim compiler, Nim's stdlib, tools and documentation.
|
||||||
For more information about Nim, including downloads and documentation for
|
For more information about Nim, including downloads and documentation for
|
||||||
|
|
@ -171,5 +171,4 @@ Copyright © 2006-2017 Andreas Rumpf, all rights reserved.
|
||||||
[badge-nim-gratipay]: https://img.shields.io/gratipay/team/nim.svg?style=flat-square
|
[badge-nim-gratipay]: https://img.shields.io/gratipay/team/nim.svg?style=flat-square
|
||||||
[badge-nim-bountysource]: https://img.shields.io/bountysource/team/nim/activity.svg?style=flat-square
|
[badge-nim-bountysource]: https://img.shields.io/bountysource/team/nim/activity.svg?style=flat-square
|
||||||
[badge-nim-bitcoin]: https://img.shields.io/badge/bitcoin-1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ-D69134.svg?style=flat-square
|
[badge-nim-bitcoin]: https://img.shields.io/badge/bitcoin-1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ-D69134.svg?style=flat-square
|
||||||
[image-nim-logo]: https://images1-focus-opensocial.googleusercontent.com/gadgets/proxy?url=https://raw.githubusercontent.com/nim-lang/assets/master/Art/logo-crown.png&container=focus&resize_w=36&refresh=21600
|
|
||||||
[pull-request-instructions]: https://help.github.com/articles/using-pull-requests/
|
[pull-request-instructions]: https://help.github.com/articles/using-pull-requests/
|
||||||
|
|
|
||||||
53
tests/async/tasyncdial.nim
Normal file
53
tests/async/tasyncdial.nim
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
discard """
|
||||||
|
file: "tasyncdial.nim"
|
||||||
|
output: '''
|
||||||
|
OK AF_INET
|
||||||
|
OK AF_INET6
|
||||||
|
'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
import
|
||||||
|
nativesockets, os, asyncdispatch
|
||||||
|
|
||||||
|
proc setupServerSocket(hostname: string, port: Port, domain: Domain): AsyncFD =
|
||||||
|
## Creates a socket, binds it to the specified address, and starts listening for connecitons.
|
||||||
|
## Registers the descriptor with the dispatcher of the current thread
|
||||||
|
## Raises OSError in case of an error.
|
||||||
|
let fd = newNativeSocket(domain)
|
||||||
|
setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
|
||||||
|
var aiList = getAddrInfo(hostname, port, domain)
|
||||||
|
if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
if listen(fd) != 0:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
setBlocking(fd, false)
|
||||||
|
result = fd.AsyncFD
|
||||||
|
register(result)
|
||||||
|
|
||||||
|
proc doTest(domain: static[Domain]) {.async.} =
|
||||||
|
const
|
||||||
|
testHost = when domain == Domain.AF_INET6: "::1" else: "127.0.0.1"
|
||||||
|
testPort = Port(17384)
|
||||||
|
let serverFd = setupServerSocket(testHost, testPort, domain)
|
||||||
|
let acceptFut = serverFd.accept()
|
||||||
|
let clientFdFut = dial(testHost, testPort)
|
||||||
|
|
||||||
|
let serverClientFd = await acceptFut
|
||||||
|
serverFd.closeSocket()
|
||||||
|
|
||||||
|
let clientFd = await clientFdFut
|
||||||
|
|
||||||
|
let recvFut = serverClientFd.recv(2)
|
||||||
|
await clientFd.send("Hi")
|
||||||
|
let msg = await recvFut
|
||||||
|
|
||||||
|
serverClientFd.closeSocket()
|
||||||
|
clientFd.closeSocket()
|
||||||
|
|
||||||
|
if msg == "Hi":
|
||||||
|
echo "OK ", domain
|
||||||
|
|
||||||
|
waitFor(doTest(Domain.AF_INET))
|
||||||
|
waitFor(doTest(Domain.AF_INET6))
|
||||||
36
tests/osproc/texitsignal.nim
Normal file
36
tests/osproc/texitsignal.nim
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
discard """
|
||||||
|
output: '''true
|
||||||
|
true'''
|
||||||
|
targets: "c"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, osproc
|
||||||
|
when not defined(windows):
|
||||||
|
import posix
|
||||||
|
|
||||||
|
# Checks that the environment is passed correctly in startProcess
|
||||||
|
# To do that launches a copy of itself with a new environment.
|
||||||
|
|
||||||
|
if paramCount() == 0:
|
||||||
|
# Parent process
|
||||||
|
|
||||||
|
let p = startProcess(
|
||||||
|
getAppFilename(),
|
||||||
|
args = @["child"],
|
||||||
|
options = {poStdErrToStdOut, poUsePath, poParentStreams}
|
||||||
|
)
|
||||||
|
|
||||||
|
echo p.running()
|
||||||
|
|
||||||
|
p.kill()
|
||||||
|
|
||||||
|
when defined(windows):
|
||||||
|
# windows kill happens using TerminateProcess(h, 0), so we should get a
|
||||||
|
# 0 here
|
||||||
|
echo p.waitForExit() == 0
|
||||||
|
else:
|
||||||
|
# on posix (non-windows), kill sends SIGKILL
|
||||||
|
echo p.waitForExit() == 128 + SIGKILL
|
||||||
|
|
||||||
|
else:
|
||||||
|
sleep(5000) # should get killed before this
|
||||||
6
tests/parser/twrongcmdsyntax.nim
Normal file
6
tests/parser/twrongcmdsyntax.nim
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
discard """
|
||||||
|
errormsg: '''identifier expected, but found 'echo 4'''
|
||||||
|
line: 6
|
||||||
|
"""
|
||||||
|
|
||||||
|
echo 4 +2
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
discard """
|
discard """
|
||||||
cmd: "nim c --threads:on -d:ssl $file"
|
cmd: "nim c --threads:on -d:ssl $file"
|
||||||
|
exitcode: 0
|
||||||
|
output: "OK"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import strutils
|
import strutils
|
||||||
from net import TimeoutError
|
from net import TimeoutError
|
||||||
|
|
||||||
import httpclient, asyncdispatch
|
import nativesockets, os, httpclient, asyncdispatch
|
||||||
|
|
||||||
const manualTests = false
|
const manualTests = false
|
||||||
|
|
||||||
|
|
@ -112,6 +114,40 @@ proc syncTest() =
|
||||||
except:
|
except:
|
||||||
doAssert false, "TimeoutError should have been raised."
|
doAssert false, "TimeoutError should have been raised."
|
||||||
|
|
||||||
syncTest()
|
proc makeIPv6HttpServer(hostname: string, port: Port): AsyncFD =
|
||||||
|
let fd = newNativeSocket(AF_INET6)
|
||||||
|
setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
|
||||||
|
var aiList = getAddrInfo(hostname, port, AF_INET6)
|
||||||
|
if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
if listen(fd) != 0:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
setBlocking(fd, false)
|
||||||
|
|
||||||
|
var serverFd = fd.AsyncFD
|
||||||
|
register(serverFd)
|
||||||
|
result = serverFd
|
||||||
|
|
||||||
|
proc onAccept(fut: Future[AsyncFD]) {.gcsafe.} =
|
||||||
|
if not fut.failed:
|
||||||
|
let clientFd = fut.read()
|
||||||
|
clientFd.send("HTTP/1.1 200 OK\r\LContent-Length: 0\r\LConnection: Closed\r\L\r\L").callback = proc() =
|
||||||
|
clientFd.closeSocket()
|
||||||
|
serverFd.accept().callback = onAccept
|
||||||
|
serverFd.accept().callback = onAccept
|
||||||
|
|
||||||
|
proc ipv6Test() =
|
||||||
|
var client = newAsyncHttpClient()
|
||||||
|
let serverFd = makeIPv6HttpServer("::1", Port(18473))
|
||||||
|
var resp = waitFor client.request("http://[::1]:18473/")
|
||||||
|
doAssert(resp.status == "200 OK")
|
||||||
|
serverFd.closeSocket()
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
syncTest()
|
||||||
waitFor(asyncTest())
|
waitFor(asyncTest())
|
||||||
|
ipv6Test()
|
||||||
|
|
||||||
|
echo "OK"
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,11 @@ when isMainModule:
|
||||||
name: string
|
name: string
|
||||||
age: int
|
age: int
|
||||||
|
|
||||||
Data = object
|
Data1 = object # TODO: Codegen bug when changed to ``Data``.
|
||||||
person: Person
|
person: Person
|
||||||
list: seq[int]
|
list: seq[int]
|
||||||
|
|
||||||
var data = to(jsonNode, Data)
|
var data = to(jsonNode, Data1)
|
||||||
doAssert data.person.name == "Nimmer"
|
doAssert data.person.name == "Nimmer"
|
||||||
doAssert data.person.age == 21
|
doAssert data.person.age == 21
|
||||||
doAssert data.list == @[1, 2, 3, 4]
|
doAssert data.list == @[1, 2, 3, 4]
|
||||||
|
|
@ -183,3 +183,47 @@ when isMainModule:
|
||||||
|
|
||||||
var result = to(node, TestEnum)
|
var result = to(node, TestEnum)
|
||||||
doAssert result.field == Bar
|
doAssert result.field == Bar
|
||||||
|
|
||||||
|
# Test ref type in field.
|
||||||
|
block:
|
||||||
|
var jsonNode = parseJson("""
|
||||||
|
{
|
||||||
|
"person": {
|
||||||
|
"name": "Nimmer",
|
||||||
|
"age": 21
|
||||||
|
},
|
||||||
|
"list": [1, 2, 3, 4]
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
|
||||||
|
type
|
||||||
|
Person = ref object
|
||||||
|
name: string
|
||||||
|
age: int
|
||||||
|
|
||||||
|
Data = object
|
||||||
|
person: Person
|
||||||
|
list: seq[int]
|
||||||
|
|
||||||
|
var data = to(jsonNode, Data)
|
||||||
|
doAssert data.person.name == "Nimmer"
|
||||||
|
doAssert data.person.age == 21
|
||||||
|
doAssert data.list == @[1, 2, 3, 4]
|
||||||
|
|
||||||
|
jsonNode = parseJson("""
|
||||||
|
{
|
||||||
|
"person": null,
|
||||||
|
"list": [1, 2, 3, 4]
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
data = to(jsonNode, Data)
|
||||||
|
doAssert data.person.isNil
|
||||||
|
|
||||||
|
block:
|
||||||
|
type
|
||||||
|
FooBar = object
|
||||||
|
field: float
|
||||||
|
|
||||||
|
let x = parseJson("""{ "field": 5}""")
|
||||||
|
let data = to(x, FooBar)
|
||||||
|
doAssert data.field == 5.0
|
||||||
60
tests/stdlib/tnetdial.nim
Normal file
60
tests/stdlib/tnetdial.nim
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
discard """
|
||||||
|
cmd: "nim c --threads:on $file"
|
||||||
|
exitcode: 0
|
||||||
|
output: "OK"
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, net, nativesockets, asyncdispatch
|
||||||
|
|
||||||
|
## Test for net.dial
|
||||||
|
|
||||||
|
const port = Port(28431)
|
||||||
|
|
||||||
|
proc initIPv6Server(hostname: string, port: Port): AsyncFD =
|
||||||
|
let fd = newNativeSocket(AF_INET6)
|
||||||
|
setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1)
|
||||||
|
var aiList = getAddrInfo(hostname, port, AF_INET6)
|
||||||
|
if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32:
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
freeAddrInfo(aiList)
|
||||||
|
if listen(fd) != 0:
|
||||||
|
raiseOSError(osLastError())
|
||||||
|
setBlocking(fd, false)
|
||||||
|
|
||||||
|
var serverFd = fd.AsyncFD
|
||||||
|
register(serverFd)
|
||||||
|
result = serverFd
|
||||||
|
|
||||||
|
# Since net.dial is synchronous, we use main thread to setup server,
|
||||||
|
# and dial to it from another thread.
|
||||||
|
|
||||||
|
proc testThread() {.thread.} =
|
||||||
|
let fd = net.dial("::1", port)
|
||||||
|
var s = newString(5)
|
||||||
|
doAssert fd.recv(addr s[0], 5) == 5
|
||||||
|
if s == "Hello":
|
||||||
|
echo "OK"
|
||||||
|
fd.close()
|
||||||
|
|
||||||
|
proc test() =
|
||||||
|
var t: Thread[void]
|
||||||
|
createThread(t, testThread)
|
||||||
|
|
||||||
|
let serverFd = initIPv6Server("::1", port)
|
||||||
|
var done = false
|
||||||
|
|
||||||
|
serverFd.accept().callback = proc(fut: Future[AsyncFD]) =
|
||||||
|
serverFd.closeSocket()
|
||||||
|
if not fut.failed:
|
||||||
|
let fd = fut.read()
|
||||||
|
fd.send("Hello").callback = proc() =
|
||||||
|
fd.closeSocket()
|
||||||
|
done = true
|
||||||
|
|
||||||
|
while not done:
|
||||||
|
poll()
|
||||||
|
|
||||||
|
joinThread(t)
|
||||||
|
|
||||||
|
test()
|
||||||
|
|
@ -21,6 +21,8 @@ proc pg[T](x, y: var T) =
|
||||||
|
|
||||||
# test as a top level statement:
|
# test as a top level statement:
|
||||||
var x, y, a, b: int
|
var x, y, a, b: int
|
||||||
|
# test for regression:
|
||||||
|
(x, y) = (1, 2)
|
||||||
(x, y) = fooBar()
|
(x, y) = fooBar()
|
||||||
|
|
||||||
echo x, " ", y
|
echo x, " ", y
|
||||||
|
|
|
||||||
40
tests/tuples/tuple_subscript.nim
Normal file
40
tests/tuples/tuple_subscript.nim
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
discard """
|
||||||
|
output: '''5
|
||||||
|
5
|
||||||
|
str2
|
||||||
|
str2
|
||||||
|
4'''
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc`[]` (t: tuple, key: string): string =
|
||||||
|
for name, field in fieldPairs(t):
|
||||||
|
if name == key:
|
||||||
|
return $field
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
proc`[]` [A,B](t: tuple, key: string, op: (proc(x: A): B)): B =
|
||||||
|
for name, field in fieldPairs(t):
|
||||||
|
when field is A:
|
||||||
|
if name == key:
|
||||||
|
return op(field)
|
||||||
|
|
||||||
|
proc`[]=`[T](t: var tuple, key: string, val: T) =
|
||||||
|
for name, field in fieldPairs(t):
|
||||||
|
when field is T:
|
||||||
|
if name == key:
|
||||||
|
field = val
|
||||||
|
|
||||||
|
var tt = (a: 1, b: "str1")
|
||||||
|
|
||||||
|
# test built in operator
|
||||||
|
tt[0] = 5
|
||||||
|
echo tt[0]
|
||||||
|
echo `[]`(tt, 0)
|
||||||
|
|
||||||
|
|
||||||
|
# test overloaded operator
|
||||||
|
tt["b"] = "str2"
|
||||||
|
echo tt["b"]
|
||||||
|
echo `[]`(tt, "b")
|
||||||
|
echo tt["b", proc(s: string) : int = s.len]
|
||||||
|
|
@ -13,6 +13,9 @@ Changelog
|
||||||
Changes affecting backwards compatibility
|
Changes affecting backwards compatibility
|
||||||
-----------------------------------------
|
-----------------------------------------
|
||||||
|
|
||||||
|
- There are now two different HTTP response types, ``Response`` and
|
||||||
|
``AsyncResponse``. ``AsyncResponse``'s ``body`` accessor returns a
|
||||||
|
``Future[string]``!
|
||||||
- ``httpclient.request`` now respects ``maxRedirects`` option. Previously
|
- ``httpclient.request`` now respects ``maxRedirects`` option. Previously
|
||||||
redirects were handled only by ``get`` and ``post`` procs.
|
redirects were handled only by ``get`` and ``post`` procs.
|
||||||
- The IO routines now raise ``EOFError`` for the "end of file" condition.
|
- The IO routines now raise ``EOFError`` for the "end of file" condition.
|
||||||
|
|
@ -56,12 +59,17 @@ Changes affecting backwards compatibility
|
||||||
checks. When fields within case objects are initialiazed, the compiler will
|
checks. When fields within case objects are initialiazed, the compiler will
|
||||||
now demand that the respective discriminator field has a matching known
|
now demand that the respective discriminator field has a matching known
|
||||||
compile-time value.
|
compile-time value.
|
||||||
|
- On posix, the results of `waitForExit`, `peekExitCode`, `execCmd` will return
|
||||||
|
128 + signal number if the application terminates via signal.
|
||||||
|
|
||||||
Library Additions
|
Library Additions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
- Added ``system.onThreadDestruction``.
|
- Added ``system.onThreadDestruction``.
|
||||||
|
- Added ``dial`` procedure to networking modules: ``net``, ``asyncdispatch``,
|
||||||
|
``asyncnet``. It merges socket creation, address resolution, and connection
|
||||||
|
into single step. When using ``dial``, you don't have to worry about
|
||||||
|
IPv4 vs IPv6 problem. ``httpclient`` now supports IPv6.
|
||||||
|
|
||||||
Tool Additions
|
Tool Additions
|
||||||
--------------
|
--------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue