diff options
author | bptato <nincsnevem662@gmail.com> | 2024-02-11 00:40:51 +0100 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2024-02-11 00:40:51 +0100 |
commit | b0583b4760a11bd062781e0ea948c61c8b66ff8f (patch) | |
tree | 5bfb46cb1aa57d62fb40d6b6869ac8a875071561 | |
parent | d0690cfea6a87c7b7d801b968b5a1c85d1e99b4f (diff) | |
download | chawan-b0583b4760a11bd062781e0ea948c61c8b66ff8f.tar.gz |
Get rid of LOAD_PIPE BufferSource
Instead, use a stream: scheme and associate hostnames with file descriptors directly from the pager.
-rw-r--r-- | src/io/serialize.nim | 5 | ||||
-rw-r--r-- | src/loader/loader.nim | 116 | ||||
-rw-r--r-- | src/loader/loaderhandle.nim | 2 | ||||
-rw-r--r-- | src/local/client.nim | 6 | ||||
-rw-r--r-- | src/local/container.nim | 33 | ||||
-rw-r--r-- | src/local/pager.nim | 24 | ||||
-rw-r--r-- | src/server/buffer.nim | 39 | ||||
-rw-r--r-- | src/server/forkserver.nim | 11 | ||||
-rw-r--r-- | src/types/buffersource.nim | 4 |
9 files changed, 130 insertions, 110 deletions
diff --git a/src/io/serialize.nim b/src/io/serialize.nim index 0c9cfd8e..f27fb40d 100644 --- a/src/io/serialize.nim +++ b/src/io/serialize.nim @@ -389,7 +389,6 @@ proc swrite*(stream: Stream, source: BufferSource) = case source.t of CLONE: stream.swrite(source.clonepid) of LOAD_REQUEST: stream.swrite(source.request) - of LOAD_PIPE: stream.swrite(source.fd) stream.swrite(source.location) stream.swrite(source.contentType) stream.swrite(source.charset) @@ -404,9 +403,6 @@ proc sread*(stream: Stream, source: var BufferSource) = of LOAD_REQUEST: source = BufferSource(t: LOAD_REQUEST) stream.sread(source.request) - of LOAD_PIPE: - source = BufferSource(t: LOAD_PIPE) - stream.sread(source.fd) stream.sread(source.location) stream.sread(source.contentType) stream.sread(source.charset) @@ -416,6 +412,5 @@ func slen*(source: BufferSource): int = case source.t of CLONE: result += slen(source.clonepid) of LOAD_REQUEST: result += slen(source.request) - of LOAD_PIPE: result += slen(source.fd) result += slen(source.location) result += slen(source.contentType) diff --git a/src/loader/loader.nim b/src/loader/loader.nim index fc409a90..3ca56cba 100644 --- a/src/loader/loader.nim +++ b/src/loader/loader.nim @@ -81,6 +81,9 @@ type ADDREF UNREF SET_REFERRER_POLICY + PASS_FD + + ClientFdMap = seq[tuple[pid, fd: int, output: OutputHandle]] LoaderContext = ref object refcount: int @@ -89,10 +92,12 @@ type config: LoaderConfig handleMap: Table[int, LoaderHandle] outputMap: Table[int, OutputHandle] - clientFdMap: seq[tuple[pid, fd: int, output: OutputHandle]] + clientFdMap: ClientFdMap referrerpolicy: ReferrerPolicy selector: Selector[int] fd: int + # List of file descriptors passed by the pager. + passedFdMap: Table[string, FileHandle] LoaderConfig* = object defaultheaders*: Headers @@ -124,6 +129,52 @@ proc rejectHandle(handle: LoaderHandle, code: ConnectErrorCode, msg = "") = handle.sendResult(code, msg) handle.close() +func findOutputIdx(clientFdMap: ClientFdMap, pid, fd: int): int = + for i, (itpid, itfd, _) in clientFdMap: + if pid == itpid and fd == itfd: + return i + return -1 + +proc delOutput(clientFdMap: var ClientFdMap, pid, fd: int) = + let i = clientFdMap.findOutputIdx(pid, fd) + if i != -1: + clientFdMap.del(i) + +func findOutput(clientFdMap: ClientFdMap, pid, fd: int): OutputHandle = + let i = clientFdMap.findOutputIdx(pid, fd) + if i != -1: + return clientFdMap[i].output + return nil + +proc addFd(ctx: LoaderContext, handle: LoaderHandle) = + let output = handle.output + output.ostream.setBlocking(false) + ctx.selector.registerHandle(handle.istream.fd, {Read}, 0) + ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) + let ofl = fcntl(handle.istream.fd, F_GETFL, 0) + discard fcntl(handle.istream.fd, F_SETFL, ofl or O_NONBLOCK) + ctx.handleMap[handle.istream.fd] = handle + if output.sostream != nil: + # replace the fd with the new one in outputMap if stream was + # redirected + # (kind of a hack, but should always work) + ctx.outputMap[output.ostream.fd] = output + ctx.outputMap.del(output.sostream.fd) + if output.clientPid != -1: + ctx.clientFdMap.delOutput(output.clientPid, output.clientFd) + output.clientFd = -1 + output.clientPid = -1 + +proc loadStream(ctx: LoaderContext, handle: LoaderHandle, request: Request) = + ctx.passedFdMap.withValue(request.url.host, fdp): + handle.sendResult(0) + handle.sendStatus(200) + handle.sendHeaders(newHeaders()) + handle.istream = newPosixStream(fdp[]) + ctx.passedFdMap.del(request.url.host) + do: + handle.rejectHandle(ERROR_FILE_NOT_FOUND, "stream not found") + proc loadResource(ctx: LoaderContext, request: Request, handle: LoaderHandle) = var redo = true var tries = 0 @@ -141,26 +192,14 @@ proc loadResource(ctx: LoaderContext, request: Request, handle: LoaderHandle) = continue if request.url.scheme == "cgi-bin": handle.loadCGI(request, ctx.config.cgiDir, ctx.config.libexecPath, prevurl) - if handle.istream == nil: - handle.close() + if handle.istream != nil: + ctx.addFd(handle) else: - let output = handle.output - output.ostream.setBlocking(false) - ctx.selector.registerHandle(handle.istream.fd, {Read}, 0) - ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) - let ofl = fcntl(handle.istream.fd, F_GETFL, 0) - discard fcntl(handle.istream.fd, F_SETFL, ofl or O_NONBLOCK) - ctx.handleMap[handle.istream.fd] = handle - if output.sostream != nil: - # replace the fd with the new one in outputMap if stream was - # redirected - # (kind of a hack, but should always work) - ctx.outputMap[output.ostream.fd] = output - ctx.outputMap.del(output.sostream.fd) - # currently only the main buffer stream can have redirects, and we - # don't suspend/resume it; if we did, we would have to put the new - # output stream's clientFd in clientFdMap too. - ctx.clientFdMap.del(output.sostream.fd) + handle.close() + elif request.url.scheme == "stream": + ctx.loadStream(handle, request) + if handle.istream != nil: + ctx.addFd(handle) else: prevurl = request.url case ctx.config.uriMethodMap.findAndRewrite(request.url) @@ -208,18 +247,6 @@ proc onLoad(ctx: LoaderContext, stream: SocketStream) = ctx.clientFdMap.add((request.clientPid, request.clientFd, handle.output)) ctx.loadResource(request, handle) -func findClientFdEntry(ctx: LoaderContext, pid, fd: int): int = - for i, (itpid, itfd, _) in ctx.clientFdMap: - if pid == itpid and fd == itfd: - return i - return -1 - -func findOutputByClientFd(ctx: LoaderContext, pid, fd: int): OutputHandle = - let i = ctx.findClientFdEntry(pid, fd) - if i != -1: - return ctx.clientFdMap[i].output - return nil - proc acceptConnection(ctx: LoaderContext) = let stream = ctx.ssock.acceptSocketStream() try: @@ -237,7 +264,7 @@ proc acceptConnection(ctx: LoaderContext) = stream.sread(fd) stream.sread(clientPid) stream.sread(clientFd) - let output = ctx.findOutputByClientFd(pid, fd) + let output = ctx.clientFdMap.findOutput(pid, fd) if output != nil: output.tee(stream, clientPid, clientFd) stream.swrite(output != nil) @@ -247,7 +274,7 @@ proc acceptConnection(ctx: LoaderContext) = stream.sread(pid) stream.sread(fds) for fd in fds: - let output = ctx.findOutputByClientFd(pid, fd) + let output = ctx.clientFdMap.findOutput(pid, fd) if output != nil: # remove from the selector, so any new reads will be just placed # in the handle's buffer @@ -258,7 +285,7 @@ proc acceptConnection(ctx: LoaderContext) = stream.sread(pid) stream.sread(fds) for fd in fds: - let output = ctx.findOutputByClientFd(pid, fd) + let output = ctx.clientFdMap.findOutput(pid, fd) if output != nil: # place the stream back into the selector, so we can write to it # again @@ -275,6 +302,12 @@ proc acceptConnection(ctx: LoaderContext) = of SET_REFERRER_POLICY: stream.sread(ctx.referrerpolicy) stream.close() + of PASS_FD: + var id: string + stream.sread(id) + let fd = stream.recvFileHandle() + ctx.passedFdMap[id] = fd + stream.close() except ErrorBrokenPipe: # receiving end died while reading the file; give up. stream.close() @@ -386,8 +419,7 @@ proc runFileLoader*(fd: cint, config: LoaderConfig) = ctx.selector.unregister(output.ostream.fd) ctx.outputMap.del(output.ostream.fd) if output.clientFd != -1: - let i = ctx.findClientFdEntry(output.clientPid, output.clientFd) - ctx.clientFdMap.del(i) + ctx.clientFdMap.delOutput(output.clientPid, output.clientFd) output.ostream.close() output.ostream = nil let handle = output.parent @@ -666,4 +698,12 @@ proc setReferrerPolicy*(loader: FileLoader, referrerpolicy: ReferrerPolicy) = if stream != nil: stream.swrite(SET_REFERRER_POLICY) stream.swrite(referrerpolicy) - stream.close() + stream.close() + +proc passFd*(pid: Pid, id: string, fd: FileHandle) = + let stream = connectSocketStream(pid, buffered = false) + if stream != nil: + stream.swrite(PASS_FD) + stream.swrite(id) + stream.sendFileHandle(fd) + stream.close() diff --git a/src/loader/loaderhandle.nim b/src/loader/loaderhandle.nim index 6cdfd19f..7f583d2b 100644 --- a/src/loader/loaderhandle.nim +++ b/src/loader/loaderhandle.nim @@ -137,8 +137,6 @@ proc sendHeaders*(handle: LoaderHandle, headers: Headers) = let fd = SocketStream(output.ostream).recvFileHandle() output.sostream = output.ostream output.ostream = newPosixStream(fd) - output.clientFd = -1 - output.clientPid = -1 proc sendData*(output: OutputHandle, p: pointer, nmemb: int): int = return output.ostream.sendData(p, nmemb) diff --git a/src/local/client.nim b/src/local/client.nim index ae838360..76cbd4e7 100644 --- a/src/local/client.nim +++ b/src/local/client.nim @@ -516,7 +516,7 @@ proc addConsole(pager: Pager, interactive: bool, clearFun, showFun, hideFun: var pipefd: array[0..1, cint] if pipe(pipefd) == -1: raise newException(Defect, "Failed to open console pipe.") - let url = newURL("javascript:console.show()").get + let url = newURL("stream:console").get let container = pager.readPipe0(some("text/plain"), CHARSET_UNKNOWN, pipefd[0], some(url), ConsoleTitle, canreinterpret = false) let err = newPosixStream(pipefd[1]) @@ -543,7 +543,7 @@ proc clearConsole(client: Client) = var pipefd: array[0..1, cint] if pipe(pipefd) == -1: raise newException(Defect, "Failed to open console pipe.") - let url = newURL("javascript:console.show()").get + let url = newURL("stream:console").get let pager = client.pager let replacement = pager.readPipe0(some("text/plain"), CHARSET_UNKNOWN, pipefd[0], some(url), ConsoleTitle, canreinterpret = false) @@ -613,7 +613,7 @@ proc launchClient*(client: Client, pages: seq[string], module = ismodule) if not stdin.isatty(): - client.pager.readPipe(contentType, cs, stdin.getFileHandle()) + client.pager.readPipe(contentType, cs, stdin.getFileHandle(), "*stdin*") for page in pages: client.pager.loadURL(page, ctype = contentType, cs = cs) diff --git a/src/local/container.nim b/src/local/container.nim index 2bf53756..770297f4 100644 --- a/src/local/container.nim +++ b/src/local/container.nim @@ -15,6 +15,7 @@ import js/javascript import js/jstypes import js/regex import loader/connecterror +import loader/loader import loader/request import local/select import server/buffer @@ -99,6 +100,7 @@ type bpos: seq[CursorPosition] highlights: seq[Highlight] process* {.jsget.}: Pid + loaderPid* {.jsget.}: Pid loadinfo*: string lines: SimpleFlexibleGrid lineshift: int @@ -129,10 +131,19 @@ jsDestructor(Container) proc newBuffer*(forkserver: ForkServer, config: BufferConfig, source: BufferSource, title = "", redirectdepth = 0, - canreinterpret = true): Container = + canreinterpret = true, fd = FileHandle(-1)): Container = let attrs = getWindowAttributes(stdout) + let (process, loaderPid) = forkserver.forkBuffer(source, config, attrs) + if fd != -1: + loaderPid.passFd(source.location.host, fd) + if fd == 0: + # We are passing stdin. + closeStdin() + else: + discard close(fd) return Container( - process: forkserver.forkBuffer(source, config, attrs), + process: process, + loaderPid: loaderPid, source: source, width: attrs.width, height: attrs.height - 1, @@ -1377,10 +1388,12 @@ proc redirectToFd*(container: Container, fdin: FileHandle, wait: bool): EmptyPromise = return container.iface.redirectToFd(fdin, wait) -proc readFromFd*(container: Container, fdout: FileHandle, ishtml: bool): - EmptyPromise = +proc readFromFd*(container: Container, fdout: FileHandle, id: string, + ishtml: bool): EmptyPromise = container.ishtml = ishtml - return container.iface.readFromFd(fdout, ishtml) + let url = newURL("stream:" & id).get + container.loaderPid.passFd(url.host, fdout) + return container.iface.readFromFd(url, ishtml) proc quit*(container: Container) = container.triggerEvent(QUIT) @@ -1510,16 +1523,6 @@ proc handleCommand(container: Container) = proc setStream*(container: Container, stream: Stream) = if not container.cloned: container.iface = newBufferInterface(stream) - if container.source.t == LOAD_PIPE: - container.iface.passFd(container.source.fd).then(proc() = - if container.source.fd == 0: - # We are closing stdin. - # Leaving the stdin fileno open to grab is a bad idea. - closeStdin() - else: - discard close(container.source.fd) - ) - stream.flush() container.load() else: container.iface = cloneInterface(stream) diff --git a/src/local/pager.nim b/src/local/pager.nim index 9633293a..6a4f3522 100644 --- a/src/local/pager.nim +++ b/src/local/pager.nim @@ -444,14 +444,16 @@ proc addContainer*(pager: Pager, container: Container) = pager.setContainer(container) proc newBuffer(pager: Pager, bufferConfig: BufferConfig, source: BufferSource, - title = "", redirectdepth = 0, canreinterpret = true): Container = + title = "", redirectdepth = 0, canreinterpret = true, + fd = FileHandle(-1)): Container = return newBuffer( pager.forkserver, bufferConfig, source, title, redirectdepth, - canreinterpret + canreinterpret, + fd ) proc dupeBuffer2(pager: Pager, container: Container, location: URL, @@ -772,21 +774,21 @@ proc loadURL*(pager: Pager, url: string, ctype = none(string), proc readPipe0*(pager: Pager, ctype: Option[string], cs: Charset, fd: FileHandle, location: Option[URL], title: string, canreinterpret: bool): Container = - var location = location.get(newURL("file://-").get) + var location = location.get(newURL("stream:-").get) let bufferconfig = pager.applySiteconf(location) let source = BufferSource( - t: LOAD_PIPE, - fd: fd, + t: LOAD_REQUEST, + request: newRequest(location), contentType: some(ctype.get("text/plain")), charset: cs, location: location ) return pager.newBuffer(bufferconfig, source, title = title, - canreinterpret = canreinterpret) + canreinterpret = canreinterpret, fd = fd) -proc readPipe*(pager: Pager, ctype: Option[string], cs: Charset, - fd: FileHandle) = - let container = pager.readPipe0(ctype, cs, fd, none(URL), "*pipe*", true) +proc readPipe*(pager: Pager, ctype: Option[string], cs: Charset, fd: FileHandle, + title: string) = + let container = pager.readPipe0(ctype, cs, fd, none(URL), title, true) pager.addContainer(container) proc command(pager: Pager) {.jsfunc.} = @@ -973,7 +975,7 @@ proc runMailcapReadPipe(pager: Pager, container: Container, let p2 = p.then(proc(): auto = discard close(fdin) let ishtml = HTMLOUTPUT in entry.flags - return container.readFromFd(fdout, ishtml) + return container.readFromFd(fdout, $pid, ishtml) ).then(proc() = discard close(fdout) ) @@ -1045,7 +1047,7 @@ proc runMailcapReadFile(pager: Pager, container: Container, discard close(pipefd[1]) let fdout = pipefd[0] let ishtml = HTMLOUTPUT in entry.flags - return container.readFromFd(fdout, ishtml).then(proc() = + return container.readFromFd(fdout, $pid, ishtml).then(proc() = discard close(fdout) ) ) diff --git a/src/server/buffer.nim b/src/server/buffer.nim index 851f04f1..817dae82 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -64,8 +64,8 @@ type LOAD, RENDER, WINDOW_CHANGE, FIND_ANCHOR, READ_SUCCESS, READ_CANCELED, CLICK, FIND_NEXT_LINK, FIND_PREV_LINK, FIND_NTH_LINK, FIND_REV_NTH_LINK, FIND_NEXT_MATCH, FIND_PREV_MATCH, GET_SOURCE, GET_LINES, UPDATE_HOVER, - PASS_FD, CONNECT, CONNECT2, GOTO_ANCHOR, CANCEL, GET_TITLE, SELECT, - REDIRECT_TO_FD, READ_FROM_FD, SET_CONTENT_TYPE, CLONE, FIND_PREV_PARAGRAPH, + CONNECT, CONNECT2, GOTO_ANCHOR, CANCEL, GET_TITLE, SELECT, REDIRECT_TO_FD, + READ_FROM_FD, SET_CONTENT_TYPE, CLONE, FIND_PREV_PARAGRAPH, FIND_NEXT_PARAGRAPH # LOADING_PAGE: istream open @@ -774,12 +774,6 @@ proc connect*(buffer: Buffer): ConnectResult {.proxy.} = return ConnectResult(code: ERROR_SOURCE_NOT_FOUND) if buffer.source.contentType.isNone: buffer.source.contentType = some("text/plain") - of LOAD_PIPE: - discard fcntl(source.fd, F_SETFL, fcntl(source.fd, F_GETFL, 0) or O_NONBLOCK) - buffer.istream = newPosixStream(source.fd) - buffer.fd = source.fd - if buffer.source.contentType.isNone: - buffer.source.contentType = some("text/plain") of LOAD_REQUEST: let request = source.request let response = buffer.loader.doRequest(request, blocking = true, canredir = true) @@ -844,37 +838,27 @@ proc redirectToFd*(buffer: Buffer, fd: FileHandle, wait: bool) {.proxy.} = ss.sread(dummy) discard close(fd) ss.close() - of LOAD_PIPE: - let ps = newPosixStream(fd) - let bfd = cint(buffer.fd) - #TODO make it work without wait - discard fcntl(bfd, F_SETFL, fcntl(bfd, F_GETFL, 0) and not O_NONBLOCK) - var buf: array[4096, uint8] - while not buffer.istream.atEnd: - let n = buffer.istream.readData(addr buf[0], buf.len) - ps.writeData(addr buf[0], n) - ps.close() - buffer.fd = -1 - buffer.istream.close() of CLONE: discard -proc readFromFd*(buffer: Buffer, fd: FileHandle, ishtml: bool) {.proxy.} = +proc readFromFd*(buffer: Buffer, url: URL, ishtml: bool) {.proxy.} = let contentType = if ishtml: "text/html" else: "text/plain" + let request = newRequest(url) buffer.source = BufferSource( - t: LOAD_PIPE, - fd: fd, + t: LOAD_REQUEST, + request: request, location: buffer.source.location, contentType: some(contentType), charset: buffer.source.charset ) buffer.setHTML(ishtml) - discard fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) or O_NONBLOCK) - buffer.istream = newPosixStream(fd) - buffer.fd = fd + let response = buffer.loader.doRequest(request, blocking = true, + canredir = false) + buffer.istream = response.body + buffer.fd = int(SocketStream(response.body).source.getFd()) buffer.selector.registerHandle(buffer.fd, {Read}, 0) proc setContentType*(buffer: Buffer, contentType: string) {.proxy.} = @@ -1635,9 +1619,6 @@ proc getLines*(buffer: Buffer, w: Slice[int]): GetLinesResult {.proxy.} = result.lines.add(line) result.numLines = buffer.lines.len -proc passFd*(buffer: Buffer, fd: FileHandle) {.proxy.} = - buffer.source.fd = fd - #TODO this is mostly broken proc getSource*(buffer: Buffer) {.proxy.} = let ssock = initServerSocket() diff --git a/src/server/forkserver.nim b/src/server/forkserver.nim index 9ad9c8a9..ced32341 100644 --- a/src/server/forkserver.nim +++ b/src/server/forkserver.nim @@ -63,15 +63,18 @@ proc removeChild*(forkserver: ForkServer, pid: Pid) = forkserver.ostream.flush() proc forkBuffer*(forkserver: ForkServer, source: BufferSource, - config: BufferConfig, attrs: WindowAttributes): Pid = + config: BufferConfig, attrs: WindowAttributes): + tuple[process, loaderPid: Pid] = forkserver.ostream.swrite(FORK_BUFFER) forkserver.ostream.swrite(source) forkserver.ostream.swrite(config) forkserver.ostream.swrite(attrs) forkserver.ostream.flush() var process: Pid + var loaderPid: Pid forkserver.istream.sread(process) - return process + forkserver.istream.sread(loaderPid) + return (process, loaderPid) proc trapSIGINT() = # trap SIGINT, so e.g. an external editor receiving an interrupt in the @@ -114,7 +117,7 @@ proc forkLoader(ctx: var ForkServerContext, config: LoaderConfig): Pid = return pid var gssock: ServerSocket -proc forkBuffer(ctx: var ForkServerContext): Pid = +proc forkBuffer(ctx: var ForkServerContext): tuple[process, loaderPid: Pid] = var source: BufferSource var config: BufferConfig var attrs: WindowAttributes @@ -164,7 +167,7 @@ proc forkBuffer(ctx: var ForkServerContext): Pid = assert c == char(0) ps.close() ctx.children.add((pid, loaderPid)) - return pid + return (pid, loaderPid) proc runForkServer() = var ctx = ForkServerContext( diff --git a/src/types/buffersource.nim b/src/types/buffersource.nim index 40b0f5a7..6377464e 100644 --- a/src/types/buffersource.nim +++ b/src/types/buffersource.nim @@ -10,7 +10,7 @@ import chakasu/charset type BufferSourceType* = enum - CLONE, LOAD_REQUEST, LOAD_PIPE + CLONE, LOAD_REQUEST BufferSource* = object location*: URL @@ -21,5 +21,3 @@ type clonepid*: Pid of LOAD_REQUEST: request*: Request - of LOAD_PIPE: - fd*: FileHandle |