about summary refs log tree commit diff stats
path: root/src/loader/loaderhandle.nim
blob: b0c5747e76c07da13e2d3109eeac013870db0a86 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import std/deques
import std/net
import std/streams
import std/tables

import io/posixstream
import io/serialize
import io/socketstream
import loader/headers
import loader/streamid

when defined(debug):
  import types/url

const LoaderBufferPageSize = 4064 # 4096 - 32

type
  LoaderBufferObj = object
    page: ptr UncheckedArray[uint8]
    len*: int

  LoaderBuffer* = ref LoaderBufferObj

  OutputHandle* = ref object
    parent*: LoaderHandle
    currentBuffer*: LoaderBuffer
    currentBufferIdx*: int
    buffers: Deque[LoaderBuffer]
    ostream*: PosixStream
    istreamAtEnd*: bool
    sostream*: SocketStream # saved ostream when redirected
    clientId*: StreamId
    registered*: bool

  LoaderHandle* = ref object
    # Stream for taking input
    istream*: PosixStream
    # Only the first handle can be redirected, because a) mailcap can only
    # redirect the first handle and b) async redirects would result in race
    # conditions that would be difficult to untangle.
    canredir: bool
    outputs*: seq[OutputHandle]
    cached*: bool
    cacheUrl*: string
    when defined(debug):
      url*: URL

{.warning[Deprecated]:off.}:
  proc `=destroy`(buffer: var LoaderBufferObj) =
    if buffer.page != nil:
      dealloc(buffer.page)
      buffer.page = nil

# Create a new loader handle, with the output stream ostream.
proc newLoaderHandle*(ostream: PosixStream, canredir: bool, clientId: StreamId):
    LoaderHandle =
  let handle = LoaderHandle(
    canredir: canredir
  )
  handle.outputs.add(OutputHandle(
    ostream: ostream,
    parent: handle,
    clientId: clientId
  ))
  return handle

proc findOutputHandle*(handle: LoaderHandle, fd: int): OutputHandle =
  for output in handle.outputs:
    if output.ostream.fd == fd:
      return output
  return nil

func cap*(buffer: LoaderBuffer): int {.inline.} =
  return LoaderBufferPageSize

proc newLoaderBuffer*(): LoaderBuffer =
  return LoaderBuffer(
    page: cast[ptr UncheckedArray[uint8]](alloc(LoaderBufferPageSize)),
    len: 0
  )

proc addBuffer*(output: OutputHandle, buffer: LoaderBuffer) =
  if output.currentBuffer == nil:
    output.currentBuffer = buffer
  else:
    output.buffers.addLast(buffer)

proc bufferCleared*(output: OutputHandle) =
  assert output.currentBuffer != nil
  output.currentBufferIdx = 0
  if output.buffers.len > 0:
    output.currentBuffer = output.buffers.popFirst()
  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,
    ostream: ostream,
    currentBuffer: outputIn.currentBuffer,
    currentBufferIdx: outputIn.currentBufferIdx,
    buffers: outputIn.buffers,
    istreamAtEnd: outputIn.istreamAtEnd,
    clientId: clientId
  ))

template output*(handle: LoaderHandle): OutputHandle =
  handle.outputs[0]

proc sendResult*(handle: LoaderHandle, res: int, msg = "") =
  handle.output.ostream.swrite(res)
  if res == 0: # success
    assert msg == ""
  else: # error
    handle.output.ostream.swrite(msg)

proc sendStatus*(handle: LoaderHandle, status: int) =
  handle.output.ostream.swrite(status)

proc sendHeaders*(handle: LoaderHandle, headers: Headers) =
  let output = handle.output
  output.ostream.swrite(headers)
  if handle.canredir:
    var redir: bool
    output.ostream.sread(redir)
    output.ostream.sread(handle.cached)
    if redir:
      let sostream = SocketStream(output.ostream)
      let fd = sostream.recvFileHandle()
      output.sostream = sostream
      output.ostream = newPosixStream(fd)

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:
    assert output.sostream == nil
    if output.ostream != nil:
      output.ostream.close()
      output.ostream = nil
  if handle.istream != nil:
    handle.istream.close()
    handle.istream = nil