about summary refs log tree commit diff stats
path: root/adapter/protocol/http.nim
blob: 4a12e898e60906dbe734930a4f06a1728c6f75f6 (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
when NimMajor >= 2:
  import std/envvars
else:
  import std/os
import std/posix
import std/strutils
import utils/sandbox

import curl
import curlerrors
import curlwrap

type
  EarlyHintState = enum
    ehsNone, ehsStarted, ehsDone

  HttpHandle = ref object
    curl: CURL
    statusline: bool
    connectreport: bool
    earlyhint: EarlyHintState
    slist: curl_slist

const STDIN_FILENO = 0
const STDOUT_FILENO = 1

proc writeAll(data: pointer; size: int) =
  var n = 0
  while n < size:
    let i = write(STDOUT_FILENO, addr cast[ptr UncheckedArray[uint8]](data)[n],
      int(size) - n)
    assert i >= 0
    n += i

proc puts(s: string) =
  if s.len > 0:
    writeAll(unsafeAddr s[0], s.len)

proc curlWriteHeader(p: cstring; size, nitems: csize_t; userdata: pointer):
    csize_t {.cdecl.} =
  var line = newString(nitems)
  if nitems > 0:
    copyMem(addr line[0], p, nitems)
  let op = cast[HttpHandle](userdata)
  if not op.statusline:
    op.statusline = true
    var status: clong
    op.curl.getinfo(CURLINFO_RESPONSE_CODE, addr status)
    if status == 103:
      op.earlyhint = ehsStarted
    else:
      op.connectreport = true
      puts("Status: " & $status & "\nCha-Control: ControlDone\n")
    return nitems
  if line == "\r\n" or line == "\n":
    # empty line (last, before body)
    if op.earlyhint == ehsStarted:
      # ignore; we do not have a way to stream headers yet.
      op.earlyhint = ehsDone
      # reset statusline; we are awaiting the next line.
      op.statusline = false
      return nitems
    puts("\r\n")
    return nitems

  if op.earlyhint != ehsStarted:
    # Regrettably, we can only write early hint headers after the status
    # code is already known.
    # For now, it seems easiest to just ignore them all.
    puts(line)
  return nitems

# From the documentation: size is always 1.
proc curlWriteBody(p: cstring; size, nmemb: csize_t; userdata: pointer):
    csize_t {.cdecl.} =
  return csize_t(write(STDOUT_FILENO, p, int(nmemb)))

# From the documentation: size is always 1.
proc readFromStdin(p: pointer; size, nitems: csize_t; userdata: pointer):
    csize_t {.cdecl.} =
  return csize_t(read(STDIN_FILENO, p, int(nitems)))

proc curlPreRequest(clientp: pointer; conn_primary_ip, conn_local_ip: cstring;
    conn_primary_port, conn_local_port: cint): cint {.cdecl.} =
  let op = cast[HttpHandle](clientp)
  op.connectreport = true
  puts("Cha-Control: Connected\n")
  enterNetworkSandbox()
  return 0 # ok

func startsWithIgnoreCase(s1, s2: openArray[char]): bool =
  if s1.len < s2.len: return false
  for i in 0 ..< s2.len:
    if s1[i].toLowerAscii() != s2[i].toLowerAscii():
      return false
  return true

proc main() =
  let curl = curl_easy_init()
  doAssert curl != nil
  let url = curl_url()
  const flags = cuint(CURLU_PATH_AS_IS)
  url.set(CURLUPART_SCHEME, getEnv("MAPPED_URI_SCHEME"), flags)
  let username = getEnv("MAPPED_URI_USERNAME")
  if username != "":
    url.set(CURLUPART_USER, username, flags)
  let password = getEnv("MAPPED_URI_PASSWORD")
  if password != "":
    url.set(CURLUPART_PASSWORD, password, flags)
  url.set(CURLUPART_HOST, getEnv("MAPPED_URI_HOST"), flags)
  let port = getEnv("MAPPED_URI_PORT")
  if port != "":
    url.set(CURLUPART_PORT, port, flags)
  let path = getEnv("MAPPED_URI_PATH")
  if path != "":
    url.set(CURLUPART_PATH, path, flags)
  let query = getEnv("MAPPED_URI_QUERY")
  if query != "":
    url.set(CURLUPART_QUERY, query, flags)
  if getEnv("CHA_INSECURE_SSL_NO_VERIFY") == "1":
    curl.setopt(CURLOPT_SSL_VERIFYPEER, 0)
    curl.setopt(CURLOPT_SSL_VERIFYHOST, 0)
  curl.setopt(CURLOPT_CURLU, url)
  let op = HttpHandle(curl: curl)
  curl.setopt(CURLOPT_SUPPRESS_CONNECT_HEADERS, 1)
  curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody)
  curl.setopt(CURLOPT_HEADERDATA, op)
  curl.setopt(CURLOPT_HEADERFUNCTION, curlWriteHeader)
  curl.setopt(CURLOPT_PREREQDATA, op)
  curl.setopt(CURLOPT_PREREQFUNCTION, curlPreRequest)
  let proxy = getEnv("ALL_PROXY")
  if proxy != "":
    curl.setopt(CURLOPT_PROXY, proxy)
  case getEnv("REQUEST_METHOD")
  of "GET":
    curl.setopt(CURLOPT_HTTPGET, 1)
  of "POST":
    curl.setopt(CURLOPT_POST, 1)
    let len = parseInt(getEnv("CONTENT_LENGTH"))
    # > For any given platform/compiler curl_off_t must be typedef'ed to
    # a 64-bit
    # > wide signed integral data type. The width of this data type must remain
    # > constant and independent of any possible large file support settings.
    # >
    # > As an exception to the above, curl_off_t shall be typedef'ed to
    # a 32-bit
    # > wide signed integral data type if there is no 64-bit type.
    # It seems safe to assume that if the platform has no uint64 then Nim won't
    # compile either. In return, we are allowed to post >2G of data.
    curl.setopt(CURLOPT_POSTFIELDSIZE_LARGE, uint64(len))
    curl.setopt(CURLOPT_READFUNCTION, readFromStdin)
  else: discard #TODO
  let headers = getEnv("REQUEST_HEADERS")
  for line in headers.split("\r\n"):
    const needle = "Accept-Encoding: "
    if line.startsWithIgnoreCase(needle):
      let s = line.substr(needle.len)
      # From the CURLOPT_ACCEPT_ENCODING manpage:
      # > The application does not have to keep the string around after
      # > setting this option.
      curl.setopt(CURLOPT_ACCEPT_ENCODING, cstring(s))
    # This is OK, because curl_slist_append strdup's line.
    op.slist = curl_slist_append(op.slist, cstring(line))
  if op.slist != nil:
    curl.setopt(CURLOPT_HTTPHEADER, op.slist)
  let res = curl_easy_perform(curl)
  if res != CURLE_OK and not op.connectreport:
    puts(getCurlConnectionError(res))
    op.connectreport = true
  curl_easy_cleanup(curl)

main()
s="p">><a name="TaskView-contains_point"><strong>contains_point</strong></a>(self, y, x)</dt><dd><tt>Test&nbsp;whether&nbsp;the&nbsp;point&nbsp;(with&nbsp;absolute&nbsp;coordinates)&nbsp;lies<br> within&nbsp;the&nbsp;boundaries&nbsp;of&nbsp;this&nbsp;object.</tt></dd></dl> <dl><dt><a name="TaskView-destroy"><strong>destroy</strong></a>(self)</dt><dd><tt>Called&nbsp;when&nbsp;the&nbsp;object&nbsp;is&nbsp;destroyed.<br> Override&nbsp;this!</tt></dd></dl> <dl><dt><a name="TaskView-finalize"><strong>finalize</strong></a>(self)</dt><dd><tt>Called&nbsp;after&nbsp;every&nbsp;displayable&nbsp;is&nbsp;done&nbsp;drawing.<br> Override&nbsp;this!</tt></dd></dl> <dl><dt><a name="TaskView-poke"><strong>poke</strong></a>(self)</dt><dd><tt>Called&nbsp;before&nbsp;drawing,&nbsp;even&nbsp;if&nbsp;invisible</tt></dd></dl> <dl><dt><a name="TaskView-resize"><strong>resize</strong></a>(self, y, x, hei<font color="#909090">=None</font>, wid<font color="#909090">=None</font>)</dt><dd><tt>Resize&nbsp;the&nbsp;widget</tt></dd></dl> <hr> Data and other attributes inherited from <a href="ranger.shared.html#EnvironmentAware">ranger.shared.EnvironmentAware</a>:<br> <dl><dt><strong>env</strong> = None</dl> <hr> Data and other attributes inherited from <a href="ranger.shared.html#FileManagerAware">ranger.shared.FileManagerAware</a>:<br> <dl><dt><strong>fm</strong> = None</dl> <hr> Data descriptors inherited from <a href="ranger.shared.html#Awareness">ranger.shared.Awareness</a>:<br> <dl><dt><strong>__dict__</strong></dt> <dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd> </dl> <dl><dt><strong>__weakref__</strong></dt> <dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd> </dl> <hr> Methods inherited from <a href="ranger.gui.curses_shortcuts.html#CursesShortcuts">ranger.gui.curses_shortcuts.CursesShortcuts</a>:<br> <dl><dt><a name="TaskView-addnstr"><strong>addnstr</strong></a>(self, *args)</dt></dl> <dl><dt><a name="TaskView-addstr"><strong>addstr</strong></a>(self, *args)</dt></dl> <dl><dt><a name="TaskView-color"><strong>color</strong></a>(self, keylist<font color="#909090">=None</font>, *keys)</dt><dd><tt>Change&nbsp;the&nbsp;colors&nbsp;from&nbsp;now&nbsp;on.</tt></dd></dl> <dl><dt><a name="TaskView-color_at"><strong>color_at</strong></a>(self, y, x, wid, keylist<font color="#909090">=None</font>, *keys)</dt><dd><tt>Change&nbsp;the&nbsp;colors&nbsp;at&nbsp;the&nbsp;specified&nbsp;position</tt></dd></dl> <dl><dt><a name="TaskView-color_reset"><strong>color_reset</strong></a>(self)</dt><dd><tt>Change&nbsp;the&nbsp;colors&nbsp;to&nbsp;the&nbsp;default&nbsp;colors</tt></dd></dl> <hr> Data and other attributes inherited from <a href="ranger.shared.settings.html#SettingsAware">ranger.shared.settings.SettingsAware</a>:<br> <dl><dt><strong>settings</strong> = &lt;ranger.ext.openstruct.OpenStruct object at 0x7f28d0aa5bd0&gt;</dl> <hr> Methods inherited from <a href="ranger.ext.accumulator.html#Accumulator">ranger.ext.accumulator.Accumulator</a>:<br> <dl><dt><a name="TaskView-correct_pointer"><strong>correct_pointer</strong></a>(self)</dt></dl> <dl><dt><a name="TaskView-get_height"><strong>get_height</strong></a>(self)</dt><dd><tt>OVERRIDE&nbsp;THIS</tt></dd></dl> <dl><dt><a name="TaskView-move"><strong>move</strong></a>(self, relative<font color="#909090">=0</font>, absolute<font color="#909090">=None</font>, pages<font color="#909090">=None</font>, narg<font color="#909090">=None</font>)</dt></dl> <dl><dt><a name="TaskView-move_to_obj"><strong>move_to_obj</strong></a>(self, arg, attr<font color="#909090">=None</font>)</dt></dl> <dl><dt><a name="TaskView-pointer_is_synced"><strong>pointer_is_synced</strong></a>(self)</dt></dl> <dl><dt><a name="TaskView-sync_index"><strong>sync_index</strong></a>(self, **kw)</dt></dl> </td></tr></table></td></tr></table> </body></html>