diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config/config.nim | 6 | ||||
-rw-r--r-- | src/display/pager.nim | 2 | ||||
-rw-r--r-- | src/html/dom.nim | 5 | ||||
-rw-r--r-- | src/io/about.nim | 6 | ||||
-rw-r--r-- | src/io/file.nim | 6 | ||||
-rw-r--r-- | src/io/http.nim | 4 | ||||
-rw-r--r-- | src/io/loader.nim | 2 | ||||
-rw-r--r-- | src/io/request.nim | 131 | ||||
-rw-r--r-- | src/ips/forkserver.nim | 4 | ||||
-rw-r--r-- | src/js/javascript.nim | 27 |
10 files changed, 143 insertions, 50 deletions
diff --git a/src/config/config.nim b/src/config/config.nim index 45c177ad..d2650a2e 100644 --- a/src/config/config.nim +++ b/src/config/config.nim @@ -109,7 +109,7 @@ type userstyle*: string filter*: URLFilter cookiejar*: CookieJar - headers*: HeaderList + headers*: Headers refererfrom*: bool referrerpolicy*: ReferrerPolicy scripting*: bool @@ -125,7 +125,7 @@ const DefaultHeaders* = { "Accept-Language": "en;q=1.0", "Pragma": "no-cache", "Cache-Control": "no-cache", -}.toTable().newHeaderList()[] +}.toTable().newHeaders()[] func getForkServerConfig*(config: Config): ForkServerConfig = return ForkServerConfig( @@ -134,7 +134,7 @@ func getForkServerConfig*(config: Config): ForkServerConfig = ) proc getBufferConfig*(config: Config, location: URL, cookiejar: CookieJar = nil, - headers: HeaderList = nil, refererfrom = false, scripting = false, + headers: Headers = nil, refererfrom = false, scripting = false, charsets = config.encoding.document_charset): BufferConfig = result = BufferConfig( userstyle: config.css.stylesheet, diff --git a/src/display/pager.nim b/src/display/pager.nim index 1767f09b..e79df4c7 100644 --- a/src/display/pager.nim +++ b/src/display/pager.nim @@ -545,7 +545,7 @@ proc applySiteconf(pager: Pager, request: Request): BufferConfig = let host = request.url.host var refererfrom: bool var cookiejar: CookieJar - var headers: HeaderList + var headers: Headers var scripting: bool var charsets = pager.config.encoding.document_charset for sc in pager.siteconf: diff --git a/src/html/dom.nim b/src/html/dom.nim index 7dbe39f1..fef66e2d 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -128,7 +128,7 @@ type localName: string Node* = ref object of EventTarget - nodeType* {.jsget.}: NodeType + nodeType*: NodeType childList*: seq[Node] parentNode* {.jsget.}: Node parentElement* {.jsget.}: Element @@ -565,6 +565,9 @@ func newCollection[T: Collection](root: Node, match: proc(node: Node): bool {.no inc root.document.colln result.populateCollection() +func nodeType(node: Node): uint16 {.jsfget.} = + return uint16(node.nodeType) + func isElement(node: Node): bool = return node.nodeType == ELEMENT_NODE diff --git a/src/io/about.nim b/src/io/about.nim index a3ecb1fe..b852c556 100644 --- a/src/io/about.nim +++ b/src/io/about.nim @@ -6,7 +6,7 @@ import ips/serialize import types/url const chawan = staticRead"res/chawan.html" -const Headers = { +const HeaderTable = { "Content-Type": "text/html" }.toTable() @@ -14,12 +14,12 @@ proc loadAbout*(request: Request, ostream: Stream) = if request.url.pathname == "blank": ostream.swrite(0) ostream.swrite(200) # ok - let headers = newHeaderList(Headers) + let headers = newHeaders(HeaderTable) ostream.swrite(headers) elif request.url.pathname == "chawan": ostream.swrite(0) ostream.swrite(200) # ok - let headers = newHeaderList(Headers) + let headers = newHeaders(HeaderTable) ostream.swrite(headers) ostream.write(chawan) else: diff --git a/src/io/file.nim b/src/io/file.nim index 092810e2..24dea51c 100644 --- a/src/io/file.nim +++ b/src/io/file.nim @@ -10,7 +10,7 @@ import types/url proc loadDir(url: URL, path: string, ostream: Stream) = ostream.swrite(0) ostream.swrite(200) # ok - ostream.swrite(newHeaderList({"Content-Type": "text/html"}.toTable())) + ostream.swrite(newHeaders({"Content-Type": "text/html"}.toTable())) ostream.write(""" <HTML> <HEAD> @@ -48,7 +48,7 @@ proc loadDir(url: URL, path: string, ostream: Stream) = proc loadSymlink(path: string, ostream: Stream) = ostream.swrite(0) ostream.swrite(200) # ok - ostream.swrite(newHeaderList({"Content-Type": "text/html"}.toTable())) + ostream.swrite(newHeaders({"Content-Type": "text/html"}.toTable())) let sl = expandSymlink(path) ostream.write(""" <HTML> @@ -79,7 +79,7 @@ proc loadFile*(url: URL, ostream: Stream) = else: ostream.swrite(0) ostream.swrite(200) # ok - ostream.swrite(newHeaderList()) + ostream.swrite(newHeaders()) while not istream.atEnd: const bufferSize = 4096 var buffer {.noinit.}: array[bufferSize, char] diff --git a/src/io/http.nim b/src/io/http.nim index 2eefb9ce..984cae7c 100644 --- a/src/io/http.nim +++ b/src/io/http.nim @@ -13,7 +13,7 @@ type HandleDataObj = object curl*: CURL statusline: bool - headers: HeaderList + headers: Headers request: Request ostream*: Stream mime: curl_mime @@ -21,7 +21,7 @@ type func newHandleData(curl: CURL, request: Request, ostream: Stream): HandleData = let handleData = HandleData( - headers: newHeaderList(), + headers: newHeaders(), curl: curl, ostream: ostream, request: request diff --git a/src/io/loader.nim b/src/io/loader.nim index 746c760d..1102b4b5 100644 --- a/src/io/loader.nim +++ b/src/io/loader.nim @@ -67,7 +67,7 @@ type handleList: seq[HandleData] LoaderConfig* = object - defaultheaders*: HeaderList + defaultheaders*: Headers filter*: URLFilter cookiejar*: CookieJar referrerpolicy*: ReferrerPolicy diff --git a/src/io/request.nim b/src/io/request.nim index f6fe5a73..531a2fc3 100644 --- a/src/io/request.nim +++ b/src/io/request.nim @@ -1,5 +1,6 @@ import options import streams +import strutils import tables import bindings/quickjs @@ -20,32 +21,57 @@ type HTTP_TRACE = "TRACE" RequestMode* = enum - NO_CORS, SAME_ORIGIN, CORS, NAVIGATE, WEBSOCKET + NO_CORS = "no-cors" + SAME_ORIGIN = "same-origin" + CORS = "cors" + NAVIGATE = "navigate" + WEBSOCKET = "websocket" RequestDestination* = enum - NO_DESTINATION, AUDIO, AUDIOWORKLET, DOCUMENT, EMBED, FONT, FRAME, IFRAME, - IMAGE, MANIFEST, OBJECT, PAINTWORKLET, REPORT, SCRIPT, SERVICEWORKER, - SHAREDWORKER, STYLE, TRACK, WORKER, XSLT + NO_DESTINATION = "" + AUDIO = "audio" + AUDIOWORKLET = "audioworklet" + DOCUMENT = "document" + EMBED = "embed" + FONT = "font" + FRAME = "frame" + IFRAME = "iframe" + IMAGE = "image" + MANIFEST = "manifest" + OBJECT = "object" + PAINTWORKLET = "paintworklet" + REPORT = "report" + SCRIPT = "script" + SERVICEWORKER = "serviceworker" + SHAREDWORKER = "sharedworker" + STYLE = "style" + TRACK = "track" + WORKER = "worker" + XSLT = "xslt" CredentialsMode* = enum - SAME_ORIGIN, OMIT, INCLUDE + SAME_ORIGIN = "same-origin" + OMIT = "omit" + INCLUDE = "include" CORSAttribute* = enum - NO_CORS, ANONYMOUS, USE_CREDENTIALS + NO_CORS = "no-cors" + ANONYMOUS = "anonymous" + USE_CREDENTIALS = "use-credentials" type Request* = ref RequestObj RequestObj* = object httpmethod*: HttpMethod url*: Url - headers*: HeaderList + headers* {.jsget.}: Headers body*: Option[string] multipart*: Option[MimeData] referer*: URL - mode*: RequestMode - destination*: RequestDestination - credentialsMode*: CredentialsMode - proxy*: URL #TODO: this should definitely be a different API. + mode* {.jsget.}: RequestMode + destination* {.jsget.}: RequestDestination + credentialsMode* {.jsget.}: CredentialsMode + proxy*: URL #TODO do something with this Response* = ref object body*: Stream @@ -53,7 +79,7 @@ type res* {.jsget.}: int contenttype* {.jsget.}: string status* {.jsget.}: int - headers* {.jsget.}: HeaderList + headers* {.jsget.}: Headers redirect*: Request url*: URL #TODO should be urllist? unregisterFun*: proc() @@ -63,7 +89,7 @@ type buf: string isend: bool - HeaderList* = ref object + Headers* = ref object table* {.jsget.}: Table[string, seq[string]] # Originally from the stdlib @@ -86,7 +112,14 @@ proc Request_url(ctx: JSContext, this: JSValue, magic: cint): JSValue {.cdecl.} let request = cast[Request](op) return toJS(ctx, $request.url) -iterator pairs*(headers: HeaderList): (string, string) = +proc Request_referrer(ctx: JSContext, this: JSValue, magic: cint): JSValue {.cdecl.} = + let op = getOpaque0(this) + if unlikely(not ctx.isInstanceOf(this, "Request") or op == nil): + return JS_ThrowTypeError(ctx, "Value is not an instance of %s", "Request") + let request = cast[Request](op) + return toJS(ctx, $request.referer) + +iterator pairs*(headers: Headers): (string, string) = for k, vs in headers.table: for v in vs: yield (k, v) @@ -147,10 +180,30 @@ proc newReadableStream*(isource: Stream): ReadableStream = else: result.isource.readStr(len, result.buf) -func newHeaderList*(): HeaderList = +proc fill(headers: Headers, ctx: JSContext, val: JSValue) = + if isSequence(ctx, val): + let x = fromJS[seq[(string, string)]](ctx, val) + if x.isSome: + for (k, v) in x.get: + if k in headers.table: + headers.table[k].add(v) + else: + headers.table[k] = @[v] + else: + let x = fromJS[Table[string, string]](ctx, val) + if x.isSome: + for k, v in x.get: + if k in headers.table: + headers.table[k].add(v) + else: + headers.table[k] = @[v] + +func newHeaders*(obj = none(JSObject)): Headers {.jsctor.} = new(result) + if obj.isSome: + result.fill(obj.get.ctx, obj.get.val) -func newHeaderList*(table: Table[string, string]): HeaderList = +func newHeaders*(table: Table[string, string]): Headers = new(result) for k, v in table: let k = k.toHeaderCase() @@ -159,7 +212,7 @@ func newHeaderList*(table: Table[string, string]): HeaderList = else: result.table[k] = @[v] -func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaderList(), +func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaders(), body = none(string), # multipart = none(MimeData), mode = RequestMode.NO_CORS, credentialsMode = CredentialsMode.SAME_ORIGIN, @@ -179,8 +232,8 @@ func newRequest*(url: URL, httpmethod = HTTP_GET, headers = newHeaderList(), func newRequest*(url: URL, httpmethod = HTTP_GET, headers: seq[(string, string)] = @[], body = none(string), # multipart = none(MimeData), TODO TODO TODO multipart - mode = RequestMode.NO_CORS, proxy: URL = nil): Request {.jsctor.} = - let hl = newHeaderList() + mode = RequestMode.NO_CORS, proxy: URL = nil): Request = + let hl = newHeaders() for pair in headers: let (k, v) = pair hl.table[k] = @[v] @@ -198,20 +251,47 @@ func createPotentialCORSRequest*(url: URL, destination: RequestDestination, cors else: CredentialsMode.INCLUDE return newRequest(url, destination = destination, mode = mode, credentialsMode = credentialsMode) +#TODO resource as Request +#TODO also, I'm not sure what to do with init. For now I've re-introduced +# JSObject to make this work, but I really wish we had a better solution. +func newRequest*(resource: string, init: JSObject): Request {.jserr, jsctor.} = + let x = parseURL(resource) + if x.isNone: + JS_ERR JS_TypeError, resource & " is not a valid URL" + if x.get.username != "" or x.get.password != "": + JS_ERR JS_TypeError, resource & " is not a valid URL" + let url = x.get + let ctx = init.ctx + let fallbackMode = some(RequestMode.CORS) #TODO none if resource is request + #TODO fallback mode, origin, window, request mode, ... + let httpMethod = fromJS[HttpMethod](ctx, + JS_GetPropertyStr(ctx, init.val, "method")).get(HTTP_GET) + let body = fromJS[string](ctx, JS_GetPropertyStr(ctx, init.val, "body")) + let jheaders = JS_GetPropertyStr(ctx, init.val, "headers") + let hl = newHeaders() + hl.fill(ctx, jheaders) + let credentials = fromJS[CredentialsMode](ctx, JS_GetPropertyStr(ctx, init.val, "credentials")) + .get(CredentialsMode.SAME_ORIGIN) + let mode = fromJS[RequestMode](ctx, JS_GetPropertyStr(ctx, init.val, "mode")) + .get(fallbackMode.get(RequestMode.NO_CORS)) + #TODO find a standard compatible way to implement this + let proxyUrl = fromJS[URL](ctx, JS_GetPropertyStr(ctx, init.val, "proxyUrl")) + return newRequest(url, httpMethod, hl, body, mode, credentials, proxy = proxyUrl.get(nil)) + proc `[]=`*(multipart: var MimeData, k, v: string) = multipart.content.add(MimePart(name: k, content: v)) -proc add*(headers: var HeaderList, k, v: string) = +proc add*(headers: var Headers, k, v: string) = let k = k.toHeaderCase() if k notin headers.table: headers.table[k] = @[v] else: headers.table[k].add(v) -proc `[]=`*(headers: var HeaderList, k, v: string) = +proc `[]=`*(headers: var Headers, k, v: string) = headers.table[k.toHeaderCase()] = @[v] -func getOrDefault*(headers: HeaderList, k: string, default = ""): string = +func getOrDefault*(headers: Headers, k: string, default = ""): string = let k = k.toHeaderCase() if k in headers.table: headers.table[k][0] @@ -252,6 +332,9 @@ func credentialsMode*(attribute: CORSAttribute): CredentialsMode = return INCLUDE proc addRequestModule*(ctx: JSContext) = - ctx.registerType(Request, extra_getset = [TabGetSet(name: "url", get: Request_url)]) + ctx.registerType(Request, extra_getset = [ + TabGetSet(name: "url", get: Request_url), + TabGetSet(name: "referrer", get: Request_referrer) + ]) ctx.registerType(Response, extra_funcs = [TabFunc(name: "json", fun: Response_json)]) - ctx.registerType(HeaderList) + ctx.registerType(Headers) diff --git a/src/ips/forkserver.nim b/src/ips/forkserver.nim index e7ddf1c0..c6acefc6 100644 --- a/src/ips/forkserver.nim +++ b/src/ips/forkserver.nim @@ -32,7 +32,9 @@ type ostream: Stream children: seq[(Pid, Pid)] -proc newFileLoader*(forkserver: ForkServer, defaultHeaders: HeaderList = nil, filter = newURLFilter(default = true), cookiejar: CookieJar = nil): FileLoader = +proc newFileLoader*(forkserver: ForkServer, defaultHeaders: Headers = nil, + filter = newURLFilter(default = true), + cookiejar: CookieJar = nil): FileLoader = new(result) forkserver.ostream.swrite(FORK_LOADER) var defaultHeaders = defaultHeaders diff --git a/src/js/javascript.nim b/src/js/javascript.nim index 263fa8aa..438858ef 100644 --- a/src/js/javascript.nim +++ b/src/js/javascript.nim @@ -80,6 +80,10 @@ type JSFunctionList* = openArray[JSCFunctionListEntry] + JSObject* = object + ctx*: JSContext + val*: JSValue + func getOpaque*(ctx: JSContext): JSContextOpaque = return cast[JSContextOpaque](JS_GetContextOpaque(ctx)) @@ -652,6 +656,8 @@ proc fromJS*[T](ctx: JSContext, val: JSValue): Option[T] = return none(T) elif T is JSValue: return some(val) + elif T is JSObject: + return some(JSObject(ctx: ctx, val: val)) elif T is object: doAssert false, "Dictionary case has not been implemented yet!" #TODO TODO TODO implement dictionary case @@ -751,7 +757,7 @@ proc toJS*(ctx: JSContext, obj: ref object): JSValue = return jsObj proc toJS(ctx: JSContext, e: enum): JSValue = - return toJS(ctx, int(e)) + return toJS(ctx, $e) proc toJS(ctx: JSContext, j: JSValue): JSValue = return j @@ -996,6 +1002,14 @@ proc addUnionParamBranch(gen: var JSFuncGenerator, query, newBranch: NimNode, fa gen.jsFunCallLists[i] = oldBranch gen.newBranchList.add(newBranch) +func isSequence*(ctx: JSContext, o: JSValue): bool = + if not JS_IsObject(o): + return false + let prop = JS_GetProperty(ctx, o, ctx.getOpaque().sym_iterator) + # prop can't be exception (throws_ref_error is 0 and tag is object) + result = not JS_IsUndefined(prop) + JS_FreeValue(ctx, prop) + proc addUnionParam0(gen: var JSFuncGenerator, tt: NimNode, s: NimNode, val: NimNode, fallback: NimNode = nil) = # Union types. #TODO quite a few types are still missing. @@ -1028,16 +1042,7 @@ proc addUnionParam0(gen: var JSFuncGenerator, tt: NimNode, s: NimNode, val: NimN # Sequence: if seqg.issome: let query = quote do: - ( - let o = `val` - JS_IsObject(o) and ( - let prop = JS_GetProperty(ctx, o, ctx.getOpaque().sym_iterator) - # prop can't be exception (throws_ref_error is 0 and tag is object) - let ret = not JS_IsUndefined(prop) - JS_FreeValue(ctx, prop) - ret - ) - ) + isSequence(ctx, `val`) let a = seqg.get[1] gen.addUnionParamBranch(query, quote do: let `s` = fromJS_or_die(seq[`a`], ctx, `val`, `ev`, `dl`), |