diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/html/chadombuilder.nim | 44 | ||||
-rw-r--r-- | src/loader/loader.nim | 169 | ||||
-rw-r--r-- | src/loader/loaderhandle.nim | 31 | ||||
-rw-r--r-- | src/render/rendertext.nim | 18 | ||||
-rw-r--r-- | src/server/buffer.nim | 39 |
5 files changed, 125 insertions, 176 deletions
diff --git a/src/html/chadombuilder.nim b/src/html/chadombuilder.nim index d604f455..e202bc50 100644 --- a/src/html/chadombuilder.nim +++ b/src/html/chadombuilder.nim @@ -32,7 +32,6 @@ type stream: StringStream encoder: EncoderStream decoder: DecoderStream - rewindImpl: proc() # hack so we don't have to worry about leaks or the GC deallocating parser refs: seq[Document] stoppedFromScript: bool @@ -301,8 +300,8 @@ proc switchCharset(wrapper: HTML5ParserWrapper) = errormode = ENCODER_ERROR_MODE_FATAL) proc newHTML5ParserWrapper*(stream: StringStream, window: Window, url: URL, - factory: CAtomFactory, rewindImpl: proc(), charsets: seq[Charset], - seekable: bool): HTML5ParserWrapper = + factory: CAtomFactory, charsets: seq[Charset], seekable: bool): + HTML5ParserWrapper = let opts = HTML5ParserOpts[Node, CAtom]( isIframeSrcdoc: false, #TODO? scripting: window != nil and window.settings.scripting @@ -313,7 +312,6 @@ proc newHTML5ParserWrapper*(stream: StringStream, window: Window, url: URL, builder: builder, opts: opts, stream: stream, - rewindImpl: rewindImpl, needsBOMSniff: seekable ) builder.document.setActiveParser(wrapper) @@ -390,11 +388,11 @@ proc CDB_parseDocumentWriteChunk(wrapper: pointer) {.exportc.} = if res == PRES_STOP: wrapper.stoppedFromScript = true -proc parseAll*(wrapper: HTML5ParserWrapper) = +proc parseAll*(wrapper: HTML5ParserWrapper): bool = let builder = wrapper.builder if wrapper.needsBOMSniff: if wrapper.stream.getPosition() + 3 >= wrapper.stream.data.len: - return + return true let scs = wrapper.bomSniff() if scs != CHARSET_UNKNOWN: builder.confidence = ccCertain @@ -402,27 +400,25 @@ proc parseAll*(wrapper: HTML5ParserWrapper) = wrapper.seekable = false wrapper.switchCharset() wrapper.needsBOMSniff = false - while true: - let buffer = wrapper.encoder.readAll() - if wrapper.decoder.failed: - assert wrapper.seekable - # Retry with another charset. - builder.restart(wrapper) - wrapper.rewindImpl() - wrapper.switchCharset() - continue - if buffer.len == 0: - break - let res = wrapper.parseBuffer(buffer) - if res != PRES_STOP: - break - # res == PRES_STOP: A meta tag describing the charset has been found; force - # use of this charset. + let buffer = wrapper.encoder.readAll() + if wrapper.decoder.failed: + assert wrapper.seekable + # Retry with another charset. + builder.restart(wrapper) + wrapper.switchCharset() + return false + if buffer.len == 0: + return true + let res = wrapper.parseBuffer(buffer) + if res == PRES_STOP: + # A meta tag describing the charset has been found; force use of this + # charset. builder.restart(wrapper) - wrapper.rewindImpl() wrapper.charsetStack.add(builder.charset) wrapper.seekable = false wrapper.switchCharset() + return false + return true proc finish*(wrapper: HTML5ParserWrapper) = if wrapper.needsBOMSniff: @@ -435,7 +431,7 @@ proc finish*(wrapper: HTML5ParserWrapper) = wrapper.needsBOMSniff = false wrapper.decoder.setInhibitCheckEnd(false) wrapper.wasICE = false - wrapper.parseAll() + doAssert wrapper.parseAll() wrapper.parser.finish() wrapper.builder.finish() for r in wrapper.refs: diff --git a/src/loader/loader.nim b/src/loader/loader.nim index 6183e771..31f5be10 100644 --- a/src/loader/loader.nim +++ b/src/loader/loader.nim @@ -81,7 +81,6 @@ type TEE SUSPEND RESUME - REWIND ADDREF UNREF SET_REFERRER_POLICY @@ -141,10 +140,10 @@ func findOutput(ctx: LoaderContext, id: StreamId): OutputHandle = return nil #TODO linear search over strings :( -func findCachedHandle(ctx: LoaderContext, cachepath: string): LoaderHandle = - assert cachepath != "" +func findCachedHandle(ctx: LoaderContext, cacheUrl: string): LoaderHandle = + assert cacheUrl != "" for it in ctx.handleMap.values: - if it.cached and it.cachepath == cachepath: + if it.cached and it.cacheUrl == cacheUrl: return it return nil @@ -153,6 +152,30 @@ proc delOutput(ctx: LoaderContext, id: StreamId) = if output != nil: ctx.outputMap.del(output.ostream.fd) +type PushBufferResult = enum + pbrDone, pbrUnregister + +# Either write data to the target output, or append it to the list of buffers to +# write and register the output in our selector. +proc pushBuffer(ctx: LoaderContext, output: OutputHandle, buffer: LoaderBuffer): + PushBufferResult = + if output.currentBuffer == nil: + var n = 0 + try: + n = output.ostream.sendData(buffer) + except ErrorAgain, ErrorWouldBlock: + discard + except ErrorBrokenPipe: + return pbrUnregister + if n < buffer.len: + output.currentBuffer = buffer + output.currentBufferIdx = n + ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) + output.registered = true + else: + output.addBuffer(buffer) + return pbrDone + proc addFd(ctx: LoaderContext, handle: LoaderHandle, originalUrl: URL) = let output = handle.output output.ostream.setBlocking(false) @@ -174,9 +197,9 @@ proc addFd(ctx: LoaderContext, handle: LoaderHandle, originalUrl: URL) = let ps = newPosixStream(tmpf, O_CREAT or O_WRONLY, 0o600) if ps != nil: output.tee(ps, NullStreamId) - let path = $originalUrl - ctx.cacheMap[path] = tmpf - handle.cachepath = path + let surl = $originalUrl + ctx.cacheMap[surl] = tmpf + handle.cacheUrl = surl proc loadStream(ctx: LoaderContext, handle: LoaderHandle, request: Request) = ctx.passedFdMap.withValue(request.url.host, fdp): @@ -236,52 +259,47 @@ proc loadFromCache(ctx: LoaderContext, stream: SocketStream, request: Request) = let handle = newLoaderHandle(stream, request.canredir, request.clientId) let surl = $request.url let cachedHandle = ctx.findCachedHandle(surl) + let output = handle.output ctx.cacheMap.withValue(surl, p): let ps = newPosixStream(p[], O_RDONLY, 0) if ps == nil: handle.rejectHandle(ERROR_FILE_NOT_IN_CACHE) ctx.cacheMap.del(surl) + handle.close() return handle.sendResult(0) handle.sendStatus(200) handle.sendHeaders(newHeaders()) - var buffer {.noinit.}: array[BufferSize, uint8] - try: - while true: - let n = ps.recvData(addr buffer[0], buffer.len) - if buffer.len == 0: - break - if handle.output.sendData(addr buffer[0], n) < n: - break - if n < buffer.len: - break - except ErrorBrokenPipe: - handle.close() - raise + if handle.cached: + handle.cacheUrl = surl + output.ostream.setBlocking(false) + while true: + let buffer = newLoaderBuffer() + let n = ps.recvData(buffer) + if n == 0: + break + if ctx.pushBuffer(output, buffer) == pbrUnregister: + if output.registered: + ctx.selector.unregister(output.ostream.fd) + ps.close() + return + if n < buffer.cap: + break ps.close() do: if cachedHandle == nil: - handle.sendResult(ERROR_URL_NOT_IN_CACHE) + handle.rejectHandle(ERROR_URL_NOT_IN_CACHE) + return if cachedHandle != nil: # download is still ongoing; move output to the original handle - let output = handle.output - output.ostream.setBlocking(false) handle.outputs.setLen(0) output.parent = cachedHandle cachedHandle.outputs.add(output) + elif output.registered: + output.istreamAtEnd = true ctx.outputMap[output.ostream.fd] = output - if handle.outputs.len > 0: - let output = handle.output - if output.sostream != nil: - try: - handle.output.sostream.swrite(true) - except IOError: - # ignore error, that just means the buffer has already closed the - # stream - discard - output.sostream.close() - output.sostream = nil - handle.close() + else: + output.ostream.close() proc onLoad(ctx: LoaderContext, stream: SocketStream) = var request: Request @@ -317,33 +335,6 @@ proc onLoad(ctx: LoaderContext, stream: SocketStream) = ctx.outputMap[fd] = handle.output ctx.loadResource(request, handle) -proc rewind(ctx: LoaderContext, stream: PosixStream, clientId: StreamId) = - let output = ctx.findOutput(clientId) - if output == nil or output.ostream == nil: - stream.swrite(false) - return - let handle = output.parent - if not handle.cached: - stream.swrite(false) - return - assert handle.cachepath != "" - let ps = newPosixStream(handle.cachepath, O_RDONLY, 0) - if ps == nil: - stream.swrite(false) - return - stream.swrite(true) - output.ostream.setBlocking(true) #TODO - var buffer {.noinit.}: array[BufferSize, uint8] - while true: - let n = ps.recvData(addr buffer[0], BufferSize) - if n == 0: - break - if output.sendData(addr buffer[0], n) < n: - break - if n < BufferSize: - break - ps.close() - proc acceptConnection(ctx: LoaderContext) = let stream = ctx.ssock.acceptSocketStream() try: @@ -384,10 +375,6 @@ proc acceptConnection(ctx: LoaderContext) = # place the stream back into the selector, so we can write to it # again ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) - of REWIND: - var targetId: StreamId - stream.sread(targetId) - ctx.rewind(stream, targetId) of ADDREF: inc ctx.refcount of UNREF: @@ -446,39 +433,19 @@ proc initLoaderContext(fd: cint, config: LoaderConfig): LoaderContext = dir &= '/' return ctx -# Either write data to the target output, or append it to the list of buffers to -# write and register the output in our selector. -proc pushBuffer(ctx: LoaderContext, handle: LoaderHandle, - buffer: LoaderBuffer, unregWrite: var seq[OutputHandle]) = - for output in handle.outputs: - if output.currentBuffer == nil: - var n = 0 - try: - n = output.sendData(addr buffer[0], buffer.len) - except ErrorAgain, ErrorWouldBlock: - discard - except ErrorBrokenPipe: - unregWrite.add(output) - break - if n < buffer.len: - output.currentBuffer = buffer - output.currentBufferIdx = n - ctx.selector.registerHandle(output.ostream.fd, {Write}, 0) - output.registered = true - else: - output.addBuffer(buffer) - # Called whenever there is more data available to read. proc handleRead(ctx: LoaderContext, handle: LoaderHandle, unregRead: var seq[LoaderHandle], unregWrite: var seq[OutputHandle]) = while true: let buffer = newLoaderBuffer() try: - buffer.len = handle.istream.recvData(addr buffer[0], buffer.cap) - if buffer.len == 0: + let n = handle.istream.recvData(buffer) + if n == 0: break - ctx.pushBuffer(handle, buffer, unregWrite) - if buffer.len < buffer.cap: + for output in handle.outputs: + if ctx.pushBuffer(output, buffer) == pbrUnregister: + unregWrite.add(output) + if n < buffer.cap: break except ErrorAgain, ErrorWouldBlock: # retry later break @@ -493,9 +460,7 @@ proc handleWrite(ctx: LoaderContext, output: OutputHandle, while output.currentBuffer != nil: let buffer = output.currentBuffer try: - let i = output.currentBufferIdx - assert buffer.len - i > 0 - let n = output.sendData(addr buffer[i], buffer.len - i) + let n = output.ostream.sendData(buffer, output.currentBufferIdx) output.currentBufferIdx += n if output.currentBufferIdx < buffer.len: break @@ -714,15 +679,6 @@ proc tee*(loader: FileLoader, targetId: StreamId): SocketStream = stream.swrite(clientId) return stream -proc rewind*(loader: FileLoader, fd: int): bool = - let stream = connectSocketStream(loader.process, false, blocking = true) - stream.swrite(REWIND) - let id: StreamId = (loader.clientPid, fd) - stream.swrite(id) - var res: bool - stream.sread(res) - return res - const BufferSize = 4096 proc handleHeaders(loader: FileLoader, request: Request, response: Response, @@ -816,10 +772,7 @@ proc doRequest*(loader: FileLoader, request: Request): Response = if response.res == 0: loader.handleHeaders(request, response, stream) else: - var msg: string - stream.sread(msg) - if msg != "": - response.internalMessage = msg + stream.sread(response.internalMessage) return response proc addref*(loader: FileLoader) = diff --git a/src/loader/loaderhandle.nim b/src/loader/loaderhandle.nim index fe8c6435..b0c5747e 100644 --- a/src/loader/loaderhandle.nim +++ b/src/loader/loaderhandle.nim @@ -17,7 +17,7 @@ const LoaderBufferPageSize = 4064 # 4096 - 32 type LoaderBufferObj = object page: ptr UncheckedArray[uint8] - len: int + len*: int LoaderBuffer* = ref LoaderBufferObj @@ -41,7 +41,7 @@ type canredir: bool outputs*: seq[OutputHandle] cached*: bool - cachepath*: string + cacheUrl*: string when defined(debug): url*: URL @@ -70,18 +70,9 @@ proc findOutputHandle*(handle: LoaderHandle, fd: int): OutputHandle = return output return nil -func `[]`*(buffer: LoaderBuffer, i: int): var uint8 {.inline.} = - return buffer[].page[i] - func cap*(buffer: LoaderBuffer): int {.inline.} = return LoaderBufferPageSize -func len*(buffer: LoaderBuffer): var int {.inline.} = - return buffer[].len - -proc `len=`*(buffer: LoaderBuffer, i: int) {.inline.} = - buffer[].len = i - proc newLoaderBuffer*(): LoaderBuffer = return LoaderBuffer( page: cast[ptr UncheckedArray[uint8]](alloc(LoaderBufferPageSize)), @@ -102,6 +93,14 @@ proc bufferCleared*(output: OutputHandle) = else: output.currentBuffer = nil +proc clearBuffers*(output: OutputHandle) = + if output.currentBuffer != nil: + output.currentBuffer = nil + output.currentBufferIdx = 0 + output.buffers.clear() + else: + assert output.buffers.len == 0 + proc tee*(outputIn: OutputHandle, ostream: PosixStream, clientId: StreamId) = outputIn.parent.outputs.add(OutputHandle( parent: outputIn.parent, @@ -139,8 +138,14 @@ proc sendHeaders*(handle: LoaderHandle, headers: Headers) = output.sostream = sostream output.ostream = newPosixStream(fd) -proc sendData*(output: OutputHandle, p: pointer, nmemb: int): int = - return output.ostream.sendData(p, nmemb) +proc recvData*(ps: PosixStream, buffer: LoaderBuffer): int {.inline.} = + let n = ps.recvData(addr buffer.page[0], buffer.cap) + buffer.len = n + return n + +proc sendData*(ps: PosixStream, buffer: LoaderBuffer, si = 0): int {.inline.} = + assert buffer.len - si > 0 + return ps.sendData(addr buffer.page[si], buffer.len - si) proc close*(handle: LoaderHandle) = for output in handle.outputs: diff --git a/src/render/rendertext.nim b/src/render/rendertext.nim index bbf2ec49..bc508282 100644 --- a/src/render/rendertext.nim +++ b/src/render/rendertext.nim @@ -20,11 +20,10 @@ type StreamRenderer* = object newline: bool w: int j: int # byte in line - rewindImpl: proc() #TODO pass bool for whether we can rewind -proc newStreamRenderer*(stream: Stream, charsets0: openArray[Charset], - rewindImpl: proc()): StreamRenderer = +proc newStreamRenderer*(stream: Stream, charsets0: openArray[Charset]): + StreamRenderer = var charsets = newSeq[Charset](charsets0.len) for i in 0 ..< charsets.len: charsets[i] = charsets0[charsets.high - i] @@ -46,12 +45,10 @@ proc newStreamRenderer*(stream: Stream, charsets0: openArray[Charset], charsets: charsets, ansiparser: AnsiCodeParser( state: PARSE_DONE - ), - rewindImpl: rewindImpl + ) ) proc rewind(renderer: var StreamRenderer) = - renderer.rewindImpl() let cs = renderer.charsets.pop() let em = if renderer.charsets.len > 0: DECODER_ERROR_MODE_FATAL @@ -162,12 +159,13 @@ proc renderChunk(grid: var FlexibleGrid, renderer: var StreamRenderer, renderer.w += r.twidth(renderer.w) renderer.j += i - pi -proc renderStream*(grid: var FlexibleGrid, renderer: var StreamRenderer) = - var buf = renderer.encoder.readAll() - while renderer.decoder.failed: +proc renderStream*(grid: var FlexibleGrid, renderer: var StreamRenderer, debug = false): bool = + let buf = renderer.encoder.readAll() + if renderer.decoder.failed: renderer.rewind() grid.setLen(0) - buf = renderer.encoder.readAll() + return false if grid.len == 0: grid.addLine() grid.renderChunk(renderer, buf) + return true diff --git a/src/server/buffer.nim b/src/server/buffer.nim index ef7afc44..0a144fd4 100644 --- a/src/server/buffer.nim +++ b/src/server/buffer.nim @@ -632,14 +632,15 @@ proc do_reshape(buffer: Buffer) = buffer.lines = renderDocument(styledRoot, buffer.attrs) buffer.prevstyled = styledRoot else: - buffer.lines.renderStream(buffer.srenderer) + discard + #buffer.lines.renderStream(buffer.srenderer) TODO remove? -proc processData(buffer: Buffer) = +proc processData(buffer: Buffer): bool = if buffer.ishtml: - buffer.htmlParser.parseAll() + result = buffer.htmlParser.parseAll() buffer.document = buffer.htmlParser.builder.document else: - buffer.lines.renderStream(buffer.srenderer) + result = buffer.lines.renderStream(buffer.srenderer, debug = buffer.url.pathname != "console") proc windowChange*(buffer: Buffer, attrs: WindowAttributes) {.proxy.} = buffer.attrs = attrs @@ -706,24 +707,20 @@ type ConnectResult* = object charset*: Charset proc rewind(buffer: Buffer): bool = - if buffer.loader.rewind(buffer.fd): - return true let request = newRequest(buffer.url, fromcache = true) let response = buffer.loader.doRequest(request) - if response.body != nil: - buffer.selector.unregister(buffer.fd) - buffer.loader.unregistered.add(buffer.fd) - buffer.istream.close() - buffer.istream = response.body - buffer.fd = response.body.fd - buffer.selector.registerHandle(buffer.fd, {Read}, 0) - return true - return false + if response.body == nil: + return false + buffer.selector.unregister(buffer.fd) + buffer.loader.unregistered.add(buffer.fd) + buffer.istream.close() + buffer.istream = response.body + buffer.fd = response.body.fd + buffer.selector.registerHandle(buffer.fd, {Read}, 0) + return true proc setHTML(buffer: Buffer, ishtml: bool) = buffer.ishtml = ishtml - let rewindImpl = proc() = - doAssert buffer.rewind() if ishtml: let factory = newCAtomFactory() buffer.factory = factory @@ -752,7 +749,6 @@ proc setHTML(buffer: Buffer, ishtml: bool) = buffer.window, buffer.url, buffer.factory, - rewindImpl = rewindImpl, buffer.charsets, seekable = true ) @@ -762,8 +758,7 @@ proc setHTML(buffer: Buffer, ishtml: bool) = buffer.quirkstyle = quirk.parseStylesheet(factory) buffer.userstyle = parseStylesheet(buffer.config.userstyle, factory) else: - buffer.srenderer = newStreamRenderer(buffer.sstream, buffer.charsets, - rewindImpl) + buffer.srenderer = newStreamRenderer(buffer.sstream, buffer.charsets) proc connect*(buffer: Buffer): ConnectResult {.proxy.} = if buffer.connected: @@ -1103,7 +1098,9 @@ proc onload(buffer: Buffer) = buffer.sstream.data.setLen(n) if n != 0: buffer.available += n - buffer.processData() + if not buffer.processData(): + if buffer.rewind(): + continue res.bytes = buffer.available res.lines = buffer.lines.len if buffer.istream.atEnd(): |