about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/html/chadombuilder.nim44
-rw-r--r--src/loader/loader.nim169
-rw-r--r--src/loader/loaderhandle.nim31
-rw-r--r--src/render/rendertext.nim18
-rw-r--r--src/server/buffer.nim39
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():