about summary refs log tree commit diff stats
path: root/src/types
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-12-13 12:08:05 +0100
committerbptato <nincsnevem662@gmail.com>2023-12-13 12:56:28 +0100
commitab203acf554993d15e37604773f160c84b4d8252 (patch)
tree45428aa45bc751f788cc5c52c32b15bb8a2363f1 /src/types
parentbf761bcb6dcc5288a86aa5e8c2b67df3f0df056b (diff)
downloadchawan-ab203acf554993d15e37604773f160c84b4d8252.tar.gz
Move http out of main binary
Now it is (technically) no longer mandatory to link to libcurl.

Also, Chawan is at last completely protocol and network backend
agnostic :)

* Implement multipart requests in local CGI
* Implement simultaneous download of CGI data
* Add REQUEST_HEADERS env var with all headers
* cssparser: add a missing check in consumeEscape
Diffstat (limited to 'src/types')
-rw-r--r--src/types/blob.nim13
-rw-r--r--src/types/formdata.nim66
2 files changed, 74 insertions, 5 deletions
diff --git a/src/types/blob.nim b/src/types/blob.nim
index 9ddca2b5..5da7317d 100644
--- a/src/types/blob.nim
+++ b/src/types/blob.nim
@@ -1,5 +1,6 @@
-import options
-import strutils
+import std/options
+import std/os
+import std/strutils
 
 import js/dict
 import js/fromjs
@@ -92,12 +93,14 @@ proc newWebFile(ctx: JSContext, fileBits: seq[string], fileName: string,
 
 #TODO File, Blob constructors
 
-func size*(this: WebFile): uint64 {.jsfget.} =
-  #TODO use stat instead
+proc getSize*(this: Blob): uint64 =
   if this.isfile:
-    return uint64(this.file.getFileSize())
+    return uint64(WebFile(this).path.getFileSize())
   return this.size
 
+proc size*(this: WebFile): uint64 {.jsfget.} =
+  return this.getSize()
+
 func name*(this: WebFile): string {.jsfget.} =
   if this.path.len > 0 and this.path[^1] != '/':
     return this.path.afterLast('/')
diff --git a/src/types/formdata.nim b/src/types/formdata.nim
index 2bc26e22..b1957998 100644
--- a/src/types/formdata.nim
+++ b/src/types/formdata.nim
@@ -1,5 +1,9 @@
+import std/streams
+import std/strutils
+
 import js/javascript
 import types/blob
+import utils/twtstr
 
 type
   FormDataEntry* = object
@@ -13,9 +17,71 @@ type
 
   FormData* = ref object
     entries*: seq[FormDataEntry]
+    boundary*: string
 
 jsDestructor(FormData)
 
 iterator items*(this: FormData): FormDataEntry {.inline.} =
   for entry in this.entries:
     yield entry
+
+proc calcLength*(this: FormData): int =
+  result = 0
+  for entry in this.entries:
+    result += "--\r\n".len + this.boundary.len # always have boundary
+    #TODO maybe make CRLF for name first?
+    result += entry.name.len # always have name
+    # these must be percent-encoded, with 2 char overhead:
+    result += entry.name.count({'\r', '\n', '"'}) * 2
+    if entry.isstr:
+      result += "Content-Disposition: form-data; name=\"\"\r\n".len
+      result += entry.svalue.len
+    else:
+      result += "Content-Disposition: form-data; name=\"\";".len
+      # file name
+      result += " filename=\"\"\r\n".len
+      result += entry.filename.len
+      # dquot must be quoted with 2 char overhead
+      result += entry.filename.count('"') * 2
+      # content type
+      result += "Content-Type: \r\n".len
+      result += entry.value.ctype.len
+      if entry.value.isfile:
+        result += int(WebFile(entry.value).getSize())
+      else:
+        result += int(entry.value.size)
+    result += "\r\n".len # header is always followed by \r\n
+    result += "\r\n".len # value is always followed by \r\n
+
+proc getContentType*(this: FormData): string =
+  return "multipart/form-data; boundary=" & this.boundary
+
+proc writeEntry*(stream: Stream, entry: FormDataEntry, boundary: string) =
+  stream.write("--" & boundary & "\r\n")
+  let name = percentEncode(entry.name, {'"', '\r', '\n'})
+  if entry.isstr:
+    stream.write("Content-Disposition: form-data; name=\"" & name & "\"\r\n")
+    stream.write("\r\n")
+    stream.write(entry.svalue)
+  else:
+    stream.write("Content-Disposition: form-data; name=\"" & name & "\";")
+    let filename = percentEncode(entry.filename, {'"', '\r', '\n'})
+    stream.write(" filename=\"" & filename & "\"\r\n")
+    let blob = entry.value
+    let ctype = if blob.ctype == "":
+      "application/octet-stream"
+    else:
+      blob.ctype
+    stream.write("Content-Type: " & ctype & "\r\n")
+    if blob.isfile:
+      let fs = newFileStream(WebFile(blob).path)
+      if fs != nil:
+        var buf {.noInit.}: array[4096, uint8]
+        while true:
+          let n = fs.readData(addr buf[0], 4096)
+          stream.writeData(addr buf[0], n)
+          if n != 4096: break
+    else:
+      stream.writeData(blob.buffer, int(blob.size))
+    stream.write("\r\n")
+  stream.write("\r\n")