about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/js/javascript.nim7
-rw-r--r--src/loader/response.nim40
-rw-r--r--src/xhr/xmlhttprequest.nim74
3 files changed, 112 insertions, 9 deletions
diff --git a/src/js/javascript.nim b/src/js/javascript.nim
index 0d49cf86..c744345d 100644
--- a/src/js/javascript.nim
+++ b/src/js/javascript.nim
@@ -816,7 +816,7 @@ proc addThisName(gen: var JSFuncGenerator, thisname: Option[string]) =
 
 func getActualMinArgs(gen: var JSFuncGenerator): int =
   var ma = gen.minArgs
-  if gen.thisname.isSome:
+  if gen.thisname.isSome and not gen.isstatic:
     dec ma
   if gen.passCtx:
     dec ma
@@ -1038,9 +1038,10 @@ macro jsfuncn*(jsname: static string, uf: static bool,
     staticname: static string, fun: typed) =
   var gen = setupGenerator(fun, FUNCTION, jsname = jsname, unforgeable = uf,
     isstatic = staticname != "", thisType = staticname)
-  if gen.minArgs == 0:
+  if gen.minArgs == 0 and not gen.isstatic:
     error("Zero-parameter functions are not supported. (Maybe pass Window or Client?)")
-  gen.addFixParam("this")
+  if not gen.isstatic:
+    gen.addFixParam("this")
   gen.addRequiredParams()
   gen.addOptionalParams()
   gen.finishFunCallList()
diff --git a/src/loader/response.nim b/src/loader/response.nim
index 92aa5c7a..8f7d18b3 100644
--- a/src/loader/response.nim
+++ b/src/loader/response.nim
@@ -15,7 +15,24 @@ import chakasu/decoderstream
 import chakasu/encoderstream
 
 type
+  ResponseType* = enum
+    TYPE_DEFAULT = "default"
+    TYPE_BASIC = "basic"
+    TYPE_CORS = "cors"
+    TYPE_ERROR = "error"
+    TYPE_OPAQUE = "opaque"
+    TYPE_OPAQUEREDIRECT = "opaqueredirect"
+
+  #TODO fully implement headers guards
+  HeadersGuard* = enum
+    GUARD_IMMUTABLE = "immutable"
+    GUARD_REQUEST = "request"
+    GUARD_REQUEST_NO_CORS = "request-no-cors"
+    GUARD_RESPONSE = "response"
+    GUARD_NONE = "none"
+
   Response* = ref object
+    responseType* {.jsget: "type".}: ResponseType
     res*: int
     fd*: int
     body*: Stream
@@ -23,6 +40,7 @@ type
     contentType*: string
     status* {.jsget.}: uint16
     headers* {.jsget.}: Headers
+    headersGuard: HeadersGuard
     redirect*: Request
     url*: URL #TODO should be urllist?
     unregisterFun*: proc()
@@ -41,10 +59,26 @@ proc newResponse*(res: int, request: Request, fd = -1, stream: Stream = nil):
     fd: fd
   )
 
+func makeNetworkError*(): Response =
+  #TODO use "create" function
+  #TODO headers immutable
+  return Response(
+    res: 0,
+    responseType: TYPE_ERROR,
+    status: 0,
+    headers: newHeaders(),
+    headersGuard: GUARD_IMMUTABLE
+  )
+
+proc error(): Response {.jsstfunc: "Response".} =
+  return makeNetworkError()
+
 func sok(response: Response): bool {.jsfget: "ok".} =
   return response.status in 200u16 .. 299u16
 
 func surl(response: Response): string {.jsfget: "url".} =
+  if response.responseType == TYPE_ERROR:
+    return ""
   return $response.url
 
 #TODO: this should be a property of body
@@ -56,7 +90,11 @@ proc close*(response: Response) {.jsfunc.} =
     response.body.close()
 
 proc text*(response: Response): Promise[JSResult[string]] {.jsfunc.} =
-  if response.bodyRead == nil:
+  if response.body == nil:
+    let p = newPromise[JSResult[string]]()
+    p.resolve(JSResult[string].ok(""))
+    return p
+  if response.bodyUsed:
     let p = newPromise[JSResult[string]]()
     let err = JSResult[string]
       .err(newTypeError("Body has already been consumed"))
diff --git a/src/xhr/xmlhttprequest.nim b/src/xhr/xmlhttprequest.nim
index ef0e6601..28fbd50a 100644
--- a/src/xhr/xmlhttprequest.nim
+++ b/src/xhr/xmlhttprequest.nim
@@ -1,5 +1,13 @@
+import std/options
+import std/strutils
+
 import html/event
+import js/domexception
 import js/javascript
+import loader/headers
+import loader/request
+import loader/response
+import types/url
 
 type
   XMLHttpRequestResponseType = enum
@@ -10,6 +18,16 @@ type
     TYPE_JSON = "json"
     TYPE_TEXT = "text"
 
+  XMLHttpRequestState = enum
+    UNSENT = 0u16
+    OPENED = 1u16
+    HEADERS_RECEIVED = 2u16
+    LOADING = 3u16
+    DONE = 4u16
+
+  XMLHttpRequestFlag = enum
+    SEND_FLAG, UPLOAD_LISTENER_FLAG, SYNC_FLAG
+
   XMLHttpRequestEventTarget = ref object of EventTarget
     onloadstart {.jsgetset.}: EventHandler
     onprogress {.jsgetset.}: EventHandler
@@ -23,8 +41,14 @@ type
 
   XMLHttpRequest = ref object of XMLHttpRequestEventTarget
     onreadystatechange {.jsgetset.}: EventHandler
-    readyState {.jsget.}: uint16
+    readyState: XMLHttpRequestState
     upload {.jsget.}: XMLHttpRequestUpload
+    flags: set[XMLHttpRequestFlag]
+    requestMethod: HttpMethod
+    requestURL: URL
+    authorRequestHeaders: Headers
+    response: Response
+    responseType {.jsget.}: XMLHttpRequestResponseType
 
 jsDestructor(XMLHttpRequestEventTarget)
 jsDestructor(XMLHttpRequestUpload)
@@ -33,14 +57,54 @@ jsDestructor(XMLHttpRequest)
 func newXMLHttpRequest(): XMLHttpRequest {.jsctor.} =
   let upload = XMLHttpRequestUpload()
   return XMLHttpRequest(
-    upload: upload
+    upload: upload,
+    authorRequestHeaders: newHeaders()
   )
 
-proc open(this: XMLHttpRequest, httpMethod, url: string) {.jsfunc.} =
-  discard #TODO implement
+func readyState(this: XMLHttpRequest): uint16 {.jsfget.} =
+  return uint16(this.readyState)
+
+proc parseMethod(s: string): DOMResult[HttpMethod] =
+  return case s.toLowerAscii()
+  of "get": ok(HTTP_GET)
+  of "delete": ok(HTTP_DELETE)
+  of "head": ok(HTTP_HEAD)
+  of "options": ok(HTTP_OPTIONS)
+  of "patch": ok(HTTP_PATCH)
+  of "post": ok(HTTP_POST)
+  of "put": ok(HTTP_PUT)
+  of "connect", "trace", "track":
+    err(newDOMException("Forbidden method", "SecurityError"))
+  else:
+    err(newDOMException("Invalid method", "SyntaxError"))
+
+proc open(this: XMLHttpRequest, httpMethod, url: string): Err[DOMException]
+    {.jsfunc.} =
+  let httpMethod = ?parseMethod(httpMethod)
+  let x = parseURL(url)
+  if x.isNone:
+    return err(newDOMException("Invalid URL", "SyntaxError"))
+  let parsedURL = x.get
+  #TODO async, username, password arguments
+  let async = true
+  #TODO if async is false... probably just throw.
+  #TODO terminate fetch controller
+  this.flags.excl(SEND_FLAG)
+  this.flags.excl(UPLOAD_LISTENER_FLAG)
+  #TODO set if not async
+  if async:
+    this.flags.excl(SYNC_FLAG)
+  else:
+    this.flags.incl(SYNC_FLAG)
+  this.requestMethod = httpMethod
+  this.authorRequestHeaders = newHeaders()
+  this.response = makeNetworkError()
+  this.requestURL = parsedURL
+  return ok()
 
 proc addXMLHttpRequestModule*(ctx: JSContext) =
   let eventTargetCID = ctx.getClass("EventTarget")
   let xhretCID = ctx.registerType(XMLHttpRequestEventTarget, eventTargetCID)
   ctx.registerType(XMLHttpRequestUpload, xhretCID)
-  ctx.registerType(XMLHttpRequest, xhretCID)
+  let xhrCID = ctx.registerType(XMLHttpRequest, xhretCID)
+  ctx.defineConsts(xhrCID, XMLHttpRequestState, uint16)