mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-05 04:27:44 +00:00
Merge branch 'master' of github.com:Araq/Nimrod
This commit is contained in:
@@ -454,7 +454,10 @@ type
|
||||
|
||||
TInPort* = int16 ## unsigned!
|
||||
TInAddrScalar* = int32 ## unsigned!
|
||||
|
||||
|
||||
TInAddrT* {.importc: "in_addr_t", pure, final,
|
||||
header: "<netinet/in.h>".} = int32 ## unsigned!
|
||||
|
||||
TInAddr* {.importc: "struct in_addr", pure, final,
|
||||
header: "<netinet/in.h>".} = object ## struct in_addr
|
||||
s_addr*: TInAddrScalar
|
||||
@@ -544,6 +547,7 @@ type
|
||||
|
||||
var
|
||||
errno* {.importc, header: "<errno.h>".}: cint ## error variable
|
||||
h_errno* {.importc, header: "<netdb.h>".}: cint
|
||||
daylight* {.importc, header: "<time.h>".}: cint
|
||||
timezone* {.importc, header: "<time.h>".}: int
|
||||
|
||||
@@ -1558,6 +1562,8 @@ var
|
||||
## Terminates a record (if supported by the protocol).
|
||||
MSG_OOB* {.importc, header: "<sys/socket.h>".}: cint
|
||||
## Out-of-band data.
|
||||
MSG_NOSIGNAL* {.importc, header: "<sys/socket.h>".}: cint
|
||||
## No SIGPIPE generated when an attempt to send is made on a stream-oriented socket that is no longer connected.
|
||||
MSG_PEEK* {.importc, header: "<sys/socket.h>".}: cint
|
||||
## Leave received data in queue.
|
||||
MSG_TRUNC* {.importc, header: "<sys/socket.h>".}: cint
|
||||
@@ -1733,8 +1739,8 @@ proc htons*(a1: int16): int16 {.importc, header: "<arpa/inet.h>".}
|
||||
proc ntohl*(a1: int32): int32 {.importc, header: "<arpa/inet.h>".}
|
||||
proc ntohs*(a1: int16): int16 {.importc, header: "<arpa/inet.h>".}
|
||||
|
||||
proc inet_addr*(a1: cstring): int32 {.importc, header: "<arpa/inet.h>".}
|
||||
proc inet_ntoa*(a1: int32): cstring {.importc, header: "<arpa/inet.h>".}
|
||||
proc inet_addr*(a1: cstring): TInAddrT {.importc, header: "<arpa/inet.h>".}
|
||||
proc inet_ntoa*(a1: TInAddr): cstring {.importc, header: "<arpa/inet.h>".}
|
||||
proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {.
|
||||
importc, header: "<arpa/inet.h>".}
|
||||
proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {.
|
||||
@@ -2315,6 +2321,7 @@ proc sched_setscheduler*(a1: tpid, a2: cint, a3: var tsched_param): cint {.
|
||||
proc sched_yield*(): cint {.importc, header: "<sched.h>".}
|
||||
|
||||
proc strerror*(errnum: cint): cstring {.importc, header: "<string.h>".}
|
||||
proc hstrerror*(herrnum: cint): cstring {.importc, header: "<netdb.h>".}
|
||||
|
||||
proc FD_CLR*(a1: cint, a2: var Tfd_set) {.importc, header: "<sys/select.h>".}
|
||||
proc FD_ISSET*(a1: cint, a2: var Tfd_set): cint {.
|
||||
|
||||
@@ -495,7 +495,7 @@ type
|
||||
JArray
|
||||
|
||||
PJsonNode* = ref TJsonNode ## JSON node
|
||||
TJsonNode {.final, pure.} = object
|
||||
TJsonNode* {.final, pure.} = object
|
||||
case kind*: TJsonNodeKind
|
||||
of JString:
|
||||
str*: String
|
||||
|
||||
@@ -88,30 +88,35 @@ proc close*(s: var TScgiState) =
|
||||
## closes the connection.
|
||||
s.server.close()
|
||||
|
||||
proc next*(s: var TScgistate) =
|
||||
## proceed to the first/next request.
|
||||
s.client = accept(s.server)
|
||||
var L = 0
|
||||
while true:
|
||||
var d = s.client.recvChar()
|
||||
if d notin strutils.digits:
|
||||
if d != ':': scgiError("':' after length expected")
|
||||
break
|
||||
L = L * 10 + ord(d) - ord('0')
|
||||
recvBuffer(s, L+1)
|
||||
s.headers = parseHeaders(s.input, L)
|
||||
if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
|
||||
L = parseInt(s.headers["CONTENT_LENGTH"])
|
||||
recvBuffer(s, L)
|
||||
proc next*(s: var TScgistate, timeout: int = -1): bool =
|
||||
## proceed to the first/next request. Waits ``timeout`` miliseconds for a
|
||||
## request, if ``timeout`` is `-1` then this function will never time out.
|
||||
## Returns `True` if a new request has been processed.
|
||||
var rsocks = @[s.server]
|
||||
if select(rsocks, timeout) == 1 and rsocks.len == 0:
|
||||
s.client = accept(s.server)
|
||||
var L = 0
|
||||
while true:
|
||||
var d = s.client.recvChar()
|
||||
if d notin strutils.digits:
|
||||
if d != ':': scgiError("':' after length expected")
|
||||
break
|
||||
L = L * 10 + ord(d) - ord('0')
|
||||
recvBuffer(s, L+1)
|
||||
s.headers = parseHeaders(s.input, L)
|
||||
if s.headers["SCGI"] != "1": scgiError("SCGI Version 1 expected")
|
||||
L = parseInt(s.headers["CONTENT_LENGTH"])
|
||||
recvBuffer(s, L)
|
||||
return True
|
||||
|
||||
proc writeStatusOkTextContent*(c: TSocket) =
|
||||
proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
|
||||
## sends the following string to the socket `c`::
|
||||
##
|
||||
## Status: 200 OK\r\LContent-Type: text/plain\r\L\r\L
|
||||
## Status: 200 OK\r\LContent-Type: text/html\r\L\r\L
|
||||
##
|
||||
## You should send this before sending your HTML page, for example.
|
||||
c.send("Status: 200 OK\r\L" &
|
||||
"Content-Type: text/plain\r\L\r\L")
|
||||
"Content-Type: $1\r\L\r\L" % contentType)
|
||||
|
||||
proc run*(handleRequest: proc (client: TSocket, input: string,
|
||||
headers: PStringTable): bool,
|
||||
@@ -121,9 +126,9 @@ proc run*(handleRequest: proc (client: TSocket, input: string,
|
||||
s.open(port)
|
||||
var stop = false
|
||||
while not stop:
|
||||
next(s)
|
||||
stop = handleRequest(s.client, s.input, s.headers)
|
||||
s.client.close()
|
||||
if next(s):
|
||||
stop = handleRequest(s.client, s.input, s.headers)
|
||||
s.client.close()
|
||||
s.close()
|
||||
|
||||
when isMainModule:
|
||||
|
||||
@@ -223,12 +223,22 @@ proc getSockName*(socket: TSocket): TPort =
|
||||
result = TPort(sockets.ntohs(name.sin_port))
|
||||
|
||||
proc accept*(server: TSocket): TSocket =
|
||||
## waits for a client and returns its socket
|
||||
## waits for a client and returns its socket. ``InvalidSocket`` is returned
|
||||
## if an error occurs, or if ``server`` is non-blocking and there are no
|
||||
## clients connecting.
|
||||
var client: Tsockaddr_in
|
||||
var clientLen: cint = sizeof(client)
|
||||
result = TSocket(accept(cint(server), cast[ptr TSockAddr](addr(client)),
|
||||
addr(clientLen)))
|
||||
|
||||
proc acceptAddr*(server: TSocket): tuple[sock: TSocket, address: string] =
|
||||
## waits for a client and returns its socket and IP address
|
||||
var address: Tsockaddr_in
|
||||
var addrLen: cint = sizeof(address)
|
||||
var sock = TSocket(accept(cint(server), cast[ptr TSockAddr](addr(address)),
|
||||
addr(addrLen)))
|
||||
return (sock, $inet_ntoa(address.sin_addr))
|
||||
|
||||
proc close*(socket: TSocket) =
|
||||
## closes a socket.
|
||||
when defined(windows):
|
||||
@@ -260,6 +270,35 @@ proc getServByPort*(port: TPort, proto: string): TServent =
|
||||
result.port = TPort(s.s_port)
|
||||
result.proto = $s.s_proto
|
||||
|
||||
proc getHostByAddr*(ip: string): THostEnt =
|
||||
## This function will lookup the hostname of an IP Address.
|
||||
var myaddr: TInAddr
|
||||
myaddr.s_addr = inet_addr(ip)
|
||||
|
||||
when defined(windows):
|
||||
var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr),
|
||||
cint(sockets.AF_INET))
|
||||
if s == nil: OSError()
|
||||
else:
|
||||
var s = posix.gethostbyaddr(addr(myaddr), sizeof(myaddr),
|
||||
cint(posix.AF_INET))
|
||||
if s == nil:
|
||||
raise newException(EOS, $hStrError(h_errno))
|
||||
|
||||
result.name = $s.h_name
|
||||
result.aliases = cstringArrayToSeq(s.h_aliases)
|
||||
when defined(windows):
|
||||
result.addrType = TDomain(s.h_addrtype)
|
||||
else:
|
||||
if s.h_addrtype == posix.AF_INET:
|
||||
result.addrType = AF_INET
|
||||
elif s.h_addrtype == posix.AF_INET6:
|
||||
result.addrType = AF_INET6
|
||||
else:
|
||||
OSError("unknown h_addrtype")
|
||||
result.addrList = cstringArrayToSeq(s.h_addr_list)
|
||||
result.length = int(s.h_length)
|
||||
|
||||
proc getHostByName*(name: string): THostEnt =
|
||||
## well-known gethostbyname proc.
|
||||
when defined(Windows):
|
||||
@@ -299,9 +338,10 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) =
|
||||
|
||||
proc connect*(socket: TSocket, name: string, port = TPort(0),
|
||||
af: TDomain = AF_INET) =
|
||||
## well-known connect operation. Already does ``htons`` on the port number,
|
||||
## so you shouldn't do it. `name` can be an IP address or a host name of the
|
||||
## form "force7.de".
|
||||
## Connects socket to ``name``:``port``. ``Name`` can be an IP address or a
|
||||
## host name. If ``name`` is a host name, this function will try each IP
|
||||
## of that host name. ``htons`` is already performed on ``port`` so you must
|
||||
## not do it.
|
||||
var hints: TAddrInfo
|
||||
var aiList: ptr TAddrInfo = nil
|
||||
hints.ai_family = toInt(af)
|
||||
@@ -316,6 +356,7 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
|
||||
success = true
|
||||
break
|
||||
it = it.ai_next
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
if not success: OSError()
|
||||
|
||||
@@ -334,6 +375,40 @@ proc connect*(socket: TSocket, name: string, port = TPort(0),
|
||||
if connect(cint(socket), cast[ptr TSockAddr](addr(s)), sizeof(s)) < 0'i32:
|
||||
OSError()
|
||||
|
||||
proc connectAsync*(socket: TSocket, name: string, port = TPort(0),
|
||||
af: TDomain = AF_INET) =
|
||||
## A variant of ``connect`` for non-blocking sockets.
|
||||
var hints: TAddrInfo
|
||||
var aiList: ptr TAddrInfo = nil
|
||||
hints.ai_family = toInt(af)
|
||||
hints.ai_socktype = toInt(SOCK_STREAM)
|
||||
hints.ai_protocol = toInt(IPPROTO_TCP)
|
||||
if getAddrInfo(name, $port, addr(hints), aiList) != 0'i32: OSError()
|
||||
# try all possibilities:
|
||||
var success = false
|
||||
var it = aiList
|
||||
while it != nil:
|
||||
var ret = connect(cint(socket), it.ai_addr, it.ai_addrlen)
|
||||
if ret == 0'i32:
|
||||
success = true
|
||||
break
|
||||
else:
|
||||
# TODO: Test on Windows.
|
||||
when defined(windows):
|
||||
var err = WSAGetLastError()
|
||||
# Windows EINTR doesn't behave same as POSIX.
|
||||
if err == WSAEWOULDBLOCK:
|
||||
return
|
||||
else:
|
||||
if errno == EINTR or errno == EINPROGRESS:
|
||||
return
|
||||
|
||||
it = it.ai_next
|
||||
|
||||
freeaddrinfo(aiList)
|
||||
if not success: OSError()
|
||||
|
||||
|
||||
#proc recvfrom*(s: TWinSocket, buf: cstring, len, flags: cint,
|
||||
# fromm: ptr TSockAddr, fromlen: ptr cint): cint
|
||||
|
||||
@@ -360,6 +435,7 @@ proc pruneSocketSet(s: var seq[TSocket], fd: var TFdSet) =
|
||||
proc select*(readfds, writefds, exceptfds: var seq[TSocket],
|
||||
timeout = 500): int =
|
||||
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
|
||||
## Specify -1 for no timeout.
|
||||
var tv: TTimeVal
|
||||
tv.tv_sec = 0
|
||||
tv.tv_usec = timeout * 1000
|
||||
@@ -370,7 +446,10 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
|
||||
createFdSet((wr), writefds, m)
|
||||
createFdSet((ex), exceptfds, m)
|
||||
|
||||
result = int(select(cint(m), addr(rd), addr(wr), addr(ex), addr(tv)))
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), addr(ex), nil))
|
||||
|
||||
pruneSocketSet(readfds, (rd))
|
||||
pruneSocketSet(writefds, (wr))
|
||||
@@ -379,6 +458,7 @@ proc select*(readfds, writefds, exceptfds: var seq[TSocket],
|
||||
proc select*(readfds, writefds: var seq[TSocket],
|
||||
timeout = 500): int =
|
||||
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
|
||||
## Specify -1 for no timeout.
|
||||
var tv: TTimeVal
|
||||
tv.tv_sec = 0
|
||||
tv.tv_usec = timeout * 1000
|
||||
@@ -388,14 +468,37 @@ proc select*(readfds, writefds: var seq[TSocket],
|
||||
createFdSet((rd), readfds, m)
|
||||
createFdSet((wr), writefds, m)
|
||||
|
||||
result = int(select(cint(m), addr(rd), addr(wr), nil, addr(tv)))
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), addr(wr), nil, nil))
|
||||
|
||||
pruneSocketSet(readfds, (rd))
|
||||
pruneSocketSet(writefds, (wr))
|
||||
|
||||
proc selectWrite*(writefds: var seq[TSocket],
|
||||
timeout = 500): int =
|
||||
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
|
||||
## Specify -1 for no timeout.
|
||||
var tv: TTimeVal
|
||||
tv.tv_sec = 0
|
||||
tv.tv_usec = timeout * 1000
|
||||
|
||||
var wr: TFdSet
|
||||
var m = 0
|
||||
createFdSet((wr), writefds, m)
|
||||
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), nil, addr(wr), nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), nil, addr(wr), nil, nil))
|
||||
|
||||
pruneSocketSet(writefds, (wr))
|
||||
|
||||
|
||||
proc select*(readfds: var seq[TSocket], timeout = 500): int =
|
||||
## select with a sensible Nimrod interface. `timeout` is in miliseconds.
|
||||
## Specify -1 for no timeout.
|
||||
var tv: TTimeVal
|
||||
tv.tv_sec = 0
|
||||
tv.tv_usec = timeout * 1000
|
||||
@@ -404,14 +507,18 @@ proc select*(readfds: var seq[TSocket], timeout = 500): int =
|
||||
var m = 0
|
||||
createFdSet((rd), readfds, m)
|
||||
|
||||
result = int(select(cint(m), addr(rd), nil, nil, addr(tv)))
|
||||
if timeout != -1:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, addr(tv)))
|
||||
else:
|
||||
result = int(select(cint(m+1), addr(rd), nil, nil, nil))
|
||||
|
||||
pruneSocketSet(readfds, (rd))
|
||||
|
||||
|
||||
proc recvLine*(socket: TSocket, line: var string): bool =
|
||||
## returns false if no further data is available. `line` must be initalized
|
||||
## and not nil!
|
||||
## returns false if no further data is available. `Line` must be initialized
|
||||
## and not nil! This does not throw an EOS exception, therefore
|
||||
## it can be used in both blocking and non-blocking sockets.
|
||||
setLen(line, 0)
|
||||
while true:
|
||||
var c: char
|
||||
@@ -431,16 +538,52 @@ proc recv*(socket: TSocket, data: pointer, size: int): int =
|
||||
result = recv(cint(socket), data, size, 0'i32)
|
||||
|
||||
proc recv*(socket: TSocket): string =
|
||||
## receives all the data from the socket
|
||||
## receives all the data from the socket.
|
||||
## Socket errors will result in an ``EOS`` error.
|
||||
## If socket is not a connectionless socket and socket is not connected
|
||||
## ``""`` will be returned.
|
||||
const bufSize = 200
|
||||
var buf = newString(bufSize)
|
||||
result = ""
|
||||
while true:
|
||||
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
||||
# Error
|
||||
if bytesRead == -1: OSError()
|
||||
|
||||
buf[bytesRead] = '\0' # might not be necessary
|
||||
setLen(buf, bytesRead)
|
||||
add(result, buf)
|
||||
if bytesRead != bufSize-1: break
|
||||
|
||||
proc recvAsync*(socket: TSocket, s: var string): bool =
|
||||
## receives all the data from a non-blocking socket. If socket is non-blocking
|
||||
## and there are no messages available, `False` will be returned.
|
||||
## Other socket errors will result in an ``EOS`` error.
|
||||
## If socket is not a connectionless socket and socket is not connected
|
||||
## ``s`` will be set to ``""``.
|
||||
const bufSize = 200
|
||||
var buf = newString(bufSize)
|
||||
s = ""
|
||||
while true:
|
||||
var bytesRead = recv(socket, cstring(buf), bufSize-1)
|
||||
# Error
|
||||
if bytesRead == -1:
|
||||
when defined(windows):
|
||||
# TODO: Test on Windows
|
||||
var err = WSAGetLastError()
|
||||
if err == WSAEWOULDBLOCK:
|
||||
return False
|
||||
else: OSError()
|
||||
else:
|
||||
if errno == EAGAIN or errno == EWOULDBLOCK:
|
||||
return False
|
||||
else: OSError()
|
||||
|
||||
buf[bytesRead] = '\0' # might not be necessary
|
||||
setLen(buf, bytesRead)
|
||||
add(s, buf)
|
||||
if bytesRead != bufSize-1: break
|
||||
result = True
|
||||
|
||||
proc skip*(socket: TSocket) =
|
||||
## skips all the data that is pending for the socket
|
||||
@@ -451,12 +594,30 @@ proc skip*(socket: TSocket) =
|
||||
|
||||
proc send*(socket: TSocket, data: pointer, size: int): int =
|
||||
## sends data to a socket.
|
||||
result = send(cint(socket), data, size, 0'i32)
|
||||
when defined(windows):
|
||||
result = send(cint(socket), data, size, 0'i32)
|
||||
else:
|
||||
result = send(cint(socket), data, size, int32(MSG_NOSIGNAL))
|
||||
|
||||
proc send*(socket: TSocket, data: string) =
|
||||
## sends data to a socket.
|
||||
if send(socket, cstring(data), data.len) != data.len: OSError()
|
||||
|
||||
proc sendAsync*(socket: TSocket, data: string) =
|
||||
## sends data to a non-blocking socket.
|
||||
var bytesSent = send(socket, cstring(data), data.len)
|
||||
if bytesSent == -1:
|
||||
when defined(windows):
|
||||
var err = WSAGetLastError()
|
||||
# TODO: Test on windows.
|
||||
if err == WSAEINPROGRESS:
|
||||
return
|
||||
else: OSError()
|
||||
else:
|
||||
if errno == EAGAIN or errno == EWOULDBLOCK:
|
||||
return
|
||||
else: OSError()
|
||||
|
||||
when defined(Windows):
|
||||
const
|
||||
SOCKET_ERROR = -1
|
||||
|
||||
@@ -215,7 +215,12 @@ const
|
||||
INADDR_NONE* = -1
|
||||
|
||||
ws2dll = "Ws2_32.dll"
|
||||
|
||||
|
||||
WSAEWOULDBLOCK* = 10035
|
||||
WSAEINPROGRESS* = 10036
|
||||
|
||||
proc WSAGetLastError*(): cint {.importc: "WSAGetLastError", dynlib: ws2dll.}
|
||||
|
||||
type
|
||||
TWSAData* {.pure, final.} = object
|
||||
wVersion, wHighVersion: int16
|
||||
@@ -297,6 +302,9 @@ proc getservbyname*(name, proto: cstring): ptr TServent {.
|
||||
proc getservbyport*(port: cint, proto: cstring): ptr TServent {.
|
||||
stdcall, importc: "getservbyport", dynlib: ws2dll.}
|
||||
|
||||
proc gethostbyaddr*(ip: ptr TInAddr, len: cint, theType: cint): ptr THostEnt {.
|
||||
stdcall, importc: "gethostbyaddr", dynlib: ws2dll.}
|
||||
|
||||
proc gethostbyname*(name: cstring): ptr THostEnt {.
|
||||
stdcall, importc: "gethostbyname", dynlib: ws2dll.}
|
||||
|
||||
@@ -373,4 +381,5 @@ proc getaddrinfo*(nodename, servname: cstring, hints: ptr TAddrInfo,
|
||||
proc freeaddrinfo*(ai: ptr TAddrInfo) {.
|
||||
stdcall, importc: "freeaddrinfo", dynlib: ws2dll.}
|
||||
|
||||
|
||||
proc inet_ntoa*(i: TInAddr): cstring {.
|
||||
stdcall, importc, dynlib: ws2dll.}
|
||||
|
||||
@@ -7469,6 +7469,9 @@ proc iter_nth_child*(tree_model: PTreeModel, iter: PTreeIter,
|
||||
proc iter_parent*(tree_model: PTreeModel, iter: PTreeIter,
|
||||
child: PTreeIter): gboolean{.cdecl, dynlib: lib,
|
||||
importc: "gtk_tree_model_iter_parent".}
|
||||
proc get_string_from_iter*(tree_model: PTreeModel, iter: PTreeIter):
|
||||
cstring{.cdecl, dynlib: lib,
|
||||
importc: "gtk_tree_model_get_string_from_iter".}
|
||||
proc ref_node*(tree_model: PTreeModel, iter: PTreeIter){.cdecl,
|
||||
dynlib: lib, importc: "gtk_tree_model_ref_node".}
|
||||
proc unref_node*(tree_model: PTreeModel, iter: PTreeIter){.cdecl,
|
||||
|
||||
Reference in New Issue
Block a user