diff options
author | bptato <nincsnevem662@gmail.com> | 2022-09-06 12:33:30 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2022-09-06 13:39:26 +0200 |
commit | e38402dfa1bbc33db6b9d9736517eb45533d595c (patch) | |
tree | 9bbe6a589f1cba631198640bb167a664de69ca12 /src/io/loader.nim | |
parent | 3223a3364ae9f17dd2dc25bcbf2e644380db462f (diff) | |
download | chawan-e38402dfa1bbc33db6b9d9736517eb45533d595c.tar.gz |
Use unix domain sockets for IPC
Diffstat (limited to 'src/io/loader.nim')
-rw-r--r-- | src/io/loader.nim | 126 |
1 files changed, 74 insertions, 52 deletions
diff --git a/src/io/loader.nim b/src/io/loader.nim index 4a1e35e7..31b51930 100644 --- a/src/io/loader.nim +++ b/src/io/loader.nim @@ -1,30 +1,44 @@ +# A file loader server (?) +# The idea here is that we receive requests with a socket, then respond to each +# with a response (ideally a document.) +# For now, the protocol looks like: +# C: Request +# S: res (0 => success, _ => error) +# if success: +# S: status code +# S: headers +# S: response body +# +# The body is passed to the stream as-is, so effectively nothing can follow it. + import options import streams import tables +import net +import os when defined(posix): import posix import bindings/curl import io/http -import io/process import io/request import io/serialize -import types/mime +import io/socketstream import types/url +import utils/twtstr -const DefaultHeaders = { +const DefaultHeaders0 = { "User-Agent": "chawan", "Accept": "text/html,text/*;q=0.5", "Accept-Language": "en;q=1.0", "Pragma": "no-cache", "Cache-Control": "no-cache", -}.toTable().newHeaderList() +}.toTable() +let DefaultHeaders = DefaultHeaders0.newHeaderList() type FileLoader* = ref object defaultHeaders*: HeaderList - process*: int - istream*: Stream - ostream*: Stream + process*: Pid proc loadFile(url: Url, ostream: Stream) = when defined(windows) or defined(OS2) or defined(DOS): @@ -33,13 +47,12 @@ proc loadFile(url: Url, ostream: Stream) = let path = url.path.serialize_unicode() let istream = newFileStream(path, fmRead) if istream == nil: - ostream.swrite(1) + ostream.swrite(-1) # error ostream.flush() else: ostream.swrite(0) ostream.swrite(200) # ok - ostream.swrite(guessContentType(path)) - ostream.swrite(none(Url)) + ostream.swrite(newHeaderList()) while not istream.atEnd: const bufferSize = 4096 var buffer {.noinit.}: array[bufferSize, char] @@ -47,13 +60,10 @@ proc loadFile(url: Url, ostream: Stream) = let n = readData(istream, addr buffer[0], bufferSize) if n == 0: break - ostream.swrite(n) ostream.writeData(addr buffer[0], n) ostream.flush() if n < bufferSize: break - ostream.swrite("") - ostream.flush() proc loadResource(loader: FileLoader, request: Request, ostream: Stream) = case request.url.scheme @@ -61,14 +71,31 @@ proc loadResource(loader: FileLoader, request: Request, ostream: Stream) = loadFile(request.url, ostream) of "http", "https": loadHttp(request, ostream) + else: + ostream.swrite(-1) # error + ostream.flush() + +const SocketDirectory = "/tmp/cha/" +const SocketPathPrefix = SocketDirectory & "cha_sock_" +func getSocketPath(pid: Pid): string = + SocketPathPrefix & $pid proc runFileLoader(loader: FileLoader) = if curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK: - eprint "Failed to initialize libcurl." - quit(1) - let istream = newFileStream(stdin) - let ostream = newFileStream(stdout) + raise newException(Defect, "Failed to initialize libcurl.") + let path = getSocketPath(getpid()) + discard unlink(cstring(path)) + createDir(SocketDirectory) + let sock = newSocket(Domain.AF_UNIX, SockType.SOCK_STREAM, Protocol.IPPROTO_IP) + bindUnix(sock, path) + listen(sock) + stdout.write(char(0u8)) + stdout.flushFile() while true: + var sock2: Socket + sock.accept(sock2) + let istream = newSocketStream(sock2, nil) + let ostream = newSocketStream(nil, sock2) try: let request = istream.readRequest() for k, v in loader.defaultHeaders.table: @@ -79,62 +106,57 @@ proc runFileLoader(loader: FileLoader) = # End-of-file, quit. # TODO this should be EOFError break - istream.close() - ostream.close() + close(sock2) curl_global_cleanup() + close(sock) + discard unlink(cstring(path)) quit(0) -proc doRequest*(loader: FileLoader, request: Request): LoadResult = - if loader.istream != nil: - loader.istream.swrite(request) - loader.istream.flush() - loader.ostream.sread(result.res) - if result.res == 0: - loader.ostream.sread(result.status) - loader.ostream.sread(result.contenttype) - loader.ostream.sread(result.redirect) - result.body = newReadableStream(loader.ostream) - else: - raise newException(Defect, "Error: no loader process") +proc doRequest*(loader: FileLoader, request: Request): Response = + let sock = newSocket(Domain.AF_UNIX, SockType.SOCK_STREAM, Protocol.IPPROTO_IP) + let path = getSocketPath(loader.process) + connectUnix(sock, path) + let istream = newSocketStream(nil, sock) + let ostream = newSocketStream(sock, nil) + istream.swrite(request) + istream.flush() + ostream.sread(result.res) + if result.res == 0: + ostream.sread(result.status) + ostream.sread(result.headers) + if "Content-Type" in result.headers.table: + result.contenttype = result.headers.table["Content-Type"][0].until(';') + if "Location" in result.headers.table: + let location = result.headers.table["Location"][0] + result.redirect = parseUrl(location, some(request.url)) + # Only a stream of the response body may arrive after this point. + result.body = ostream proc newFileLoader*(defaultHeaders: HeaderList): FileLoader = new(result) result.defaultHeaders = defaultHeaders when defined(posix): - var pipefd_a: array[0..1, cint] var pipefd_b: array[0..1, cint] - if pipe(pipefd_a) == -1: - eprint "Failed to open pipe." - quit(1) if pipe(pipefd_b) == -1: - eprint "Failed to open pipe." - quit(1) - let pid = doFork() - if pid == 0: + raise newException(Defect, "Failed to open pipe.") + let pid = fork() + if pid == -1: + raise newException(Defect, "Failed to fork network process") + elif pid == 0: # child process - let readfd = pipefd_a[0] # get read a - discard close(pipefd_a[1]) # close write a let writefd = pipefd_b[1] # get write b discard close(pipefd_b[0]) # close read b - discard dup2(readfd, stdin.getFileHandle()) discard dup2(writefd, stdout.getFileHandle()) result.runFileLoader() else: result.process = pid - let writefd = pipefd_a[1] # get write a - discard close(pipefd_a[0]) # close read a let readfd = pipefd_b[0] # get read b discard close(pipefd_b[1]) # close write b var readf: File - var writef: File if not open(readf, FileHandle(readfd), fmRead): - eprint "Failed to open output handle." - quit(1) - if not open(writef, FileHandle(writefd), fmWrite): - eprint "Failed to open input handle." - quit(1) - result.ostream = newFileStream(readf) - result.istream = newFileStream(writef) + raise newException(Defect, "Failed to open output handle.") + var n: uint8 + assert newFileStream(readf).readUint8() == 0u8 proc newFileLoader*(): FileLoader = newFileLoader(DefaultHeaders) |