import std/nativesockets import std/net import std/os import io/dynstream import io/posixstream import io/serversocket type SocketStream* = ref object of PosixStream source*: Socket method recvData*(s: SocketStream; buffer: pointer; len: int): int = let n = s.source.recv(buffer, len) if n < 0: raisePosixIOError() if n == 0: if unlikely(s.isend): raise newException(EOFError, "eof") s.isend = true return n method sendData*(s: SocketStream; buffer: pointer; len: int): int = let n = s.source.send(buffer, len) if n < 0: raisePosixIOError() return n {.compile: "sendfd.c".} proc sendfd(sock, fd: cint): int {.importc.} proc sendFileHandle*(s: SocketStream; fd: FileHandle) = assert not s.source.hasDataBuffered let n = sendfd(s.fd, cint(fd)) if n < 0: raisePosixIOError() assert n == 1 # we send a single nul byte as buf {.compile: "recvfd.c".} proc recvfd(sock: cint; fdout: ptr cint): int {.importc.} proc recvFileHandle*(s: SocketStream): FileHandle = assert not s.source.hasDataBuffered var fd: cint let n = recvfd(s.fd, addr fd) if n < 0: raisePosixIOError() return FileHandle(fd) method setBlocking*(s: SocketStream; blocking: bool) = s.blocking = blocking s.source.getFd().setBlocking(blocking) method seek*(s: SocketStream; off: int) = doAssert false method sclose*(s: SocketStream) = s.source.close() # see serversocket.nim for an explanation {.compile: "connect_unix.c".} proc connect_unix_from_c(fd: cint; path: cstring; pathlen: cint): cint {.importc.} when defined(freebsd): # for FreeBSD/capsicum proc connectat_unix_from_c(baseFd, sockFd: cint; rel_path: cstring; rel_pathlen: cint): cint {.importc.} proc connectAtSocketStream0(socketDir: string; baseFd, pid: int; blocking = true): SocketStream = let sock = newSocket(Domain.AF_UNIX, SockType.SOCK_STREAM, Protocol.IPPROTO_IP, buffered = false) if not blocking: sock.getFd().setBlocking(false) let path = getSocketPath(socketDir, pid) if baseFd == -1: if connect_unix_from_c(cint(sock.getFd()), cstring(path), cint(path.len)) != 0: raiseOSError(osLastError()) else: when defined(freebsd): doAssert baseFd != -1 let name = getSocketName(pid) if connectat_unix_from_c(cint(baseFd), cint(sock.getFd()), cstring(name), cint(name.len)) != 0: raiseOSError(osLastError()) else: # shouldn't have sockDirFd on other architectures doAssert false return SocketStream( source: sock, fd: cint(sock.getFd()), blocking: blocking ) proc connectSocketStream*(socketDir: string; baseFd, pid: int; blocking = true): SocketStream = try: return connectAtSocketStream0(socketDir, baseFd, pid, blocking) except OSError: return nil proc acceptSocketStream*(ssock: ServerSocket; blocking = true): SocketStream = var sock: Socket ssock.sock.accept(sock, inheritable = true) if not blocking: sock.getFd().setBlocking(false) return SocketStream( blocking: blocking, source: sock, fd: cint(sock.getFd()) )