about summary refs log tree commit diff stats
path: root/src/loader/http.nim
blob: a767dd134403c8b452d08a1b0998971f8fab1944 (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
import options
import strutils

import bindings/curl
import loader/curlhandle
import loader/curlwrap
import loader/headers
import loader/loaderhandle
import loader/request
import types/blob
import types/formdata
import types/opt
import types/url
import utils/twtstr

proc curlWriteHeader(p: cstring, size: csize_t, nitems: csize_t,
    userdata: pointer): csize_t {.cdecl.} =
  var line = newString(nitems)
  for i in 0..<nitems:
    line[i] = p[i]

  let op = cast[CurlHandle](userdata)
  if not op.statusline:
    op.statusline = true
    if not op.handle.sendResult(int(CURLE_OK)):
      return 0
    var status: clong
    op.curl.getinfo(CURLINFO_RESPONSE_CODE, addr status)
    if not op.handle.sendStatus(cast[int](status)):
      return 0
    return nitems

  let k = line.until(':')

  if k.len == line.len:
    # empty line (last, before body) or invalid (=> error)
    if not op.handle.sendHeaders(op.headers):
      return 0
    return nitems

  let v = line.substr(k.len + 1).strip()
  op.headers.add(k, v)
  return nitems

# From the documentation: size is always 1.
proc curlWriteBody(p: cstring, size: csize_t, nmemb: csize_t,
    userdata: pointer): csize_t {.cdecl.} =
  let handleData = cast[CurlHandle](userdata)
  if nmemb > 0:
    if not handleData.handle.sendData(p, int(nmemb)):
      return 0
  return nmemb

proc applyPostBody(curl: CURL, request: Request, handleData: CurlHandle) =
  if request.multipart.isOk:
    handleData.mime = curl_mime_init(curl)
    doAssert handleData.mime != nil
    for entry in request.multipart.get:
      let part = curl_mime_addpart(handleData.mime)
      doAssert part != nil
      curl_mime_name(part, cstring(entry.name))
      if entry.isstr:
        curl_mime_data(part, cstring(entry.svalue), csize_t(entry.svalue.len))
      else:
        let blob = entry.value
        if blob.isfile: #TODO ?
          curl_mime_filedata(part, cstring(WebFile(blob).path))
        else:
          curl_mime_data(part, blob.buffer, csize_t(blob.size))
        # may be overridden by curl_mime_filedata, so set it here
        curl_mime_filename(part, cstring(entry.filename))
    curl.setopt(CURLOPT_MIMEPOST, handleData.mime)
  elif request.body.issome:
    curl.setopt(CURLOPT_POSTFIELDS, cstring(request.body.get))
    curl.setopt(CURLOPT_POSTFIELDSIZE, request.body.get.len)

proc loadHttp*(handle: LoaderHandle, curlm: CURLM,
    request: Request): CurlHandle =
  let curl = curl_easy_init()
  doAssert curl != nil
  let surl = request.url.serialize()
  curl.setopt(CURLOPT_URL, surl)
  let handleData = curl.newCurlHandle(request, handle)
  curl.setopt(CURLOPT_WRITEDATA, handleData)
  curl.setopt(CURLOPT_WRITEFUNCTION, curlWriteBody)
  curl.setopt(CURLOPT_HEADERDATA, handleData)
  curl.setopt(CURLOPT_HEADERFUNCTION, curlWriteHeader)
  if request.proxy != nil:
    let purl = request.proxy.serialize()
    curl.setopt(CURLOPT_PROXY, purl)
  case request.httpmethod
  of HTTP_GET:
    curl.setopt(CURLOPT_HTTPGET, 1)
  of HTTP_POST:
    curl.setopt(CURLOPT_POST, 1)
    curl.applyPostBody(request, handleData)
  else: discard #TODO
  for k, v in request.headers:
    let header = k & ": " & v
    handleData.slist = curl_slist_append(handleData.slist, cstring(header))
  if handleData.slist != nil:
    curl.setopt(CURLOPT_HTTPHEADER, handleData.slist)
  let res = curl_multi_add_handle(curlm, curl)
  if res != CURLM_OK:
    discard handle.sendResult(int(res))
    return nil
  return handleData