Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jshttpclient #17373

Closed
wants to merge 41 commits into from
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2ffffaa
Add jshttpclient
juancarlospaco Mar 14, 2021
4996be6
Add jshttpclient
juancarlospaco Mar 14, 2021
3b51e02
Add jshttpclient
juancarlospaco Mar 14, 2021
07773b9
Add jshttpclient
juancarlospaco Mar 14, 2021
ad901fc
Add jshttpclient
juancarlospaco Mar 14, 2021
7d8ddcf
Add jshttpclient
juancarlospaco Mar 14, 2021
ebd885b
Add jshttpclient
juancarlospaco Mar 14, 2021
3b6c838
Add jshttpclient
juancarlospaco Mar 14, 2021
72a5a19
Add jshttpclient
juancarlospaco Mar 14, 2021
776d73c
Add jshttpclient
juancarlospaco Mar 14, 2021
3e67862
How is that path
juancarlospaco Mar 14, 2021
dc1866b
doc
juancarlospaco Mar 14, 2021
b53c403
doc
juancarlospaco Mar 14, 2021
673d4f3
ReSync
juancarlospaco Mar 19, 2021
1c5b97a
Update changelog.md
juancarlospaco Mar 19, 2021
c45deae
Merge branch 'devel' of https://github.com/nim-lang/Nim into jshttpcl…
juancarlospaco Mar 19, 2021
37059c6
Typo
juancarlospaco Mar 19, 2021
c908664
Changelog
juancarlospaco Mar 19, 2021
05990b4
Changelog
juancarlospaco Mar 19, 2021
ae2ae76
Merge branch 'devel' of https://github.com/nim-lang/Nim into jshttpcl…
juancarlospaco Mar 22, 2021
86534fa
ReSync again
juancarlospaco Mar 22, 2021
700a826
Merge branch 'devel' of https://github.com/nim-lang/Nim into jshttpcl…
juancarlospaco Mar 23, 2021
0a0c6fe
update a runnable example
juancarlospaco Mar 23, 2021
8e3a720
feedback
juancarlospaco Mar 23, 2021
46783d7
Merge branch 'devel' of https://github.com/nim-lang/Nim into jshttpcl…
juancarlospaco Mar 23, 2021
a7064e3
Moar fixes
juancarlospaco Mar 23, 2021
a2f4ffc
Moar fixes
juancarlospaco Mar 23, 2021
e2d48a9
Moar fixes
juancarlospaco Mar 23, 2021
437583c
Simplify
juancarlospaco Mar 23, 2021
2ebfee5
https://github.com/nim-lang/Nim/pull/17373#discussion_r599280280
juancarlospaco Mar 23, 2021
90292a4
Fix doc
juancarlospaco Mar 23, 2021
691a320
Merge branch 'devel' of https://github.com/nim-lang/Nim into jshttpcl…
juancarlospaco Apr 18, 2021
81dfeee
https://github.com/nim-lang/Nim/pull/17373#issuecomment-816516217
juancarlospaco Apr 18, 2021
bf0c2ce
https://github.com/nim-lang/Nim/pull/17373#issuecomment-816516217
juancarlospaco Apr 18, 2021
7fb5957
https://github.com/nim-lang/Nim/pull/17373#issuecomment-816516217
juancarlospaco Apr 18, 2021
1cf2696
Fix example
juancarlospaco Apr 18, 2021
daf311c
minor
juancarlospaco Apr 18, 2021
e17b274
minor
juancarlospaco Apr 18, 2021
ad7e60f
Merge branch 'devel' of https://github.com/nim-lang/Nim into jshttpcl…
juancarlospaco Apr 18, 2021
f64e467
Make examples work
juancarlospaco Apr 18, 2021
9b4d6e4
Merge branch 'devel' of https://github.com/nim-lang/Nim into jshttpcl…
juancarlospaco May 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@

- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`.

- Added `jshttpclient` module for JavaScript that provides
juancarlospaco marked this conversation as resolved.
Show resolved Hide resolved
asyncronous `HttpClient` implemented on top of `jsfetch` and
synchronous `HttpClient` implemented on top of `jsxmlhttprequest`.
Migrated `jsxmlhttprequest` to stdlib.

- Added dollar `$` and `len` for `jsre.RegExp`.


Expand Down
93 changes: 93 additions & 0 deletions lib/std/js/jsasynchttpclient.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
## `Async <asyncjs.html>`_ `HttpClient <httpclient.html>`_ for JavaScript implemented on top of `jsfetch <jsfetch.html>`_
##
## .. Note:: jsasynchttpclient module requires `-d:nimExperimentalJsfetch`
when not defined(js):
{.fatal: "Module jsasynchttpclient is designed to be used with the JavaScript backend.".}

import std/[jsfetch, asyncjs]
from std/uri import Uri

type JsAsyncHttpClient* = ref object of JsRoot

func newJsAsyncHttpClient*(): JsAsyncHttpClient = discard
juancarlospaco marked this conversation as resolved.
Show resolved Hide resolved

func fetchOptionsImpl(body: cstring; metod: static[cstring]): FetchOptions =
unsafeNewFetchOptions(metod = metod, body = body, mode = "cors".cstring, credentials = "include".cstring,
cache = "default".cstring, referrerPolicy = "unsafe-url".cstring, keepalive = false)

proc getContent*(self: JsAsyncHttpClient; url: Uri | string): Future[cstring] {.async.} =
text(await fetch(cstring($url)))

proc deleteContent*(self: JsAsyncHttpClient; url: Uri | string): Future[cstring] {.async.} =
text(await fetch(cstring($url), fetchOptionsImpl("".cstring, "DELETE".cstring)))

proc postContent*(self: JsAsyncHttpClient; url: Uri | string; body = ""): Future[cstring] {.async.} =
text(await fetch(cstring($url), fetchOptionsImpl(body.cstring, "POST".cstring)))

proc putContent*(self: JsAsyncHttpClient; url: Uri | string; body = ""): Future[cstring] {.async.} =
text(await fetch(cstring($url), fetchOptionsImpl(body.cstring, "PUT".cstring)))

proc patchContent*(self: JsAsyncHttpClient; url: Uri | string; body = ""): Future[cstring] {.async.} =
text(await fetch(cstring($url), fetchOptionsImpl(body.cstring, "PATCH".cstring)))

proc get*(self: JsAsyncHttpClient; url: Uri | string): Future[Response] {.async.} =
fetch(cstring($url))

proc delete*(self: JsAsyncHttpClient; url: Uri | string): Future[Response] {.async.} =
fetch(cstring($url), fetchOptionsImpl("".cstring, "DELETE".cstring))

proc post*(self: JsAsyncHttpClient; url: Uri | string; body = ""): Future[Response] {.async.} =
fetch(cstring($url), fetchOptionsImpl(body.cstring, "POST".cstring))

proc put*(self: JsAsyncHttpClient; url: Uri | string; body = ""): Future[Response] {.async.} =
fetch(cstring($url), fetchOptionsImpl(body.cstring, "PUT".cstring))

proc patch*(self: JsAsyncHttpClient; url: Uri | string; body = ""): Future[Response] {.async.} =
fetch(cstring($url), fetchOptionsImpl(body.cstring, "PATCH".cstring))

proc head*(self: JsAsyncHttpClient; url: Uri | string): Future[Response] {.async.} =
fetch(cstring($url), fetchOptionsImpl("".cstring, "HEAD".cstring))


runnableExamples("-d:nimExperimentalJsfetch -r:off"):
import std/asyncjs
Copy link
Member

@timotheecour timotheecour Mar 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this example doesn't work in a browser

I have a WIP PR to make nim doc -b:js -r --browser main which will simplify testing but until it's merged, these things must be tested manually

from std/jsfetch import Response
from std/uri import parseUri, Uri

let client: JsAsyncHttpClient = newJsAsyncHttpClient()
const data: string = """{"key": "value"}"""

block:
proc example() {.async.} =
let url: Uri = parseUri("http://nim-lang.org")
let content: cstring = await client.getContent(url)
let response: Response = await client.get(url)
discard example()
juancarlospaco marked this conversation as resolved.
Show resolved Hide resolved

block:
proc example() {.async.} =
let url: Uri = parseUri("http://httpbin.org/delete")
let content: cstring = await client.deleteContent(url)
let response: Response = await client.delete(url)
discard example()

block:
proc example() {.async.} =
let url: Uri = parseUri("http://httpbin.org/post")
let content: cstring = await client.postContent(url, data)
let response: Response = await client.post(url, data)
discard example()

block:
proc example() {.async.} =
let url: Uri = parseUri("http://httpbin.org/put")
let content: cstring = await client.putContent(url, data)
let response: Response = await client.put(url, data)
discard example()

block:
proc example() {.async.} =
let url: Uri = parseUri("http://httpbin.org/patch")
let content: cstring = await client.patchContent(url, data)
let response: Response = await client.patch(url, data)
discard example()
juancarlospaco marked this conversation as resolved.
Show resolved Hide resolved
5 changes: 5 additions & 0 deletions lib/std/js/jshttpclient.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## HttpClient for JavaScript targets.
## * Asyncronous `HttpClient` implemented on top of `jsfetch`.
## * Syncronous `HttpClient` implemented on top of `jsxmlhttprequest`.
import std/js/[jsasynchttpclient, jssynchttpclient]
export jsasynchttpclient, jssynchttpclient
60 changes: 60 additions & 0 deletions lib/std/js/jssynchttpclient.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
## Synchronous `HttpClient <httpclient.html>`_ for JavaScript implemented on top of `jsxmlhttprequest <jsxmlhttprequest.html>`_
when not defined(js):
{.fatal: "Module jssynchttpclient is designed to be used with the JavaScript backend.".}

import std/uri, std/js/jsxmlhttprequest
from std/uri import Uri

type JsHttpClient* = ref object of XMLHttpRequest

func newJsHttpClient*(): JsHttpClient = discard

proc xmlHttpRequestImpl(self: JsHttpClient; url: Uri | string; body: string; metod: static[cstring]): cstring =
self.open(metod = metod, url = cstring($url), false)
self.send(body = body.cstring)
self.responseText

proc getContent*(self: JsHttpClient; url: Uri | string): cstring =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this API is completely new, should we only allow Uri and not string ?.

xmlHttpRequestImpl(self, url, "", "GET".cstring)

proc deleteContent*(self: JsHttpClient; url: Uri | string): cstring =
xmlHttpRequestImpl(self, url, "", "DELETE".cstring)

proc postContent*(self: JsHttpClient; url: Uri | string; body = ""): cstring =
xmlHttpRequestImpl(self, url, body, "POST".cstring)

proc putContent*(self: JsHttpClient; url: Uri | string; body = ""): cstring =
xmlHttpRequestImpl(self, url, body, "PUT".cstring)

proc patchContent*(self: JsHttpClient; url: Uri | string; body = ""): cstring =
xmlHttpRequestImpl(self, url, body, "PATCH".cstring)

proc head*(self: JsHttpClient; url: Uri | string): cstring =
xmlHttpRequestImpl(self, url, "", "HEAD".cstring)


runnableExamples("-r:off"):
from std/uri import parseUri, Uri

let client: JsHttpClient = newJsHttpClient()
const data: string = """{"key": "value"}"""

block:
let url: Uri = parseUri("http://nim-lang.org")
let content: cstring = client.getContent(url)

block:
let url: Uri = parseUri("http://httpbin.org/delete")
let content: cstring = client.deleteContent(url)

block:
let url: Uri = parseUri("http://httpbin.org/post")
let content: cstring = client.postContent(url, data)

block:
let url: Uri = parseUri("http://httpbin.org/put")
let content: cstring = client.putContent(url, data)

block:
let url: Uri = parseUri("http://httpbin.org/patch")
let content: cstring = client.patchContent(url, data)
57 changes: 57 additions & 0 deletions lib/std/js/jsxmlhttprequest.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
## `XMLHttpRequest` for the JavaScript target: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
when not defined(js):
{.fatal: "Module jsxmlhttprequest is designed to be used with the JavaScript backend.".}

from std/dom import Node

type XMLHttpRequest* = ref object of JsRoot ## https://xhr.spec.whatwg.org
responseXML*: Node
withCredentials*: bool
status*, timeout*, readyState*: cint
responseText*, responseURL*, statusText*: cstring

func newXMLHttpRequest*(): XMLHttpRequest {.importjs: "new XMLHttpRequest()".}
## Constructor for `XMLHttpRequest`.

func open*(this: XMLHttpRequest; metod, url: cstring; async = true; user = cstring.default; password = cstring.default) {.importjs: "#.$1(#, #, #, #, #)".}
## https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open

func send*(this: XMLHttpRequest; body: cstring | Node = cstring.default) {.importjs: "#.$1(#)".}
## https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/send

func abort*(this: XMLHttpRequest) {.importjs: "#.$1()".}
## https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/abort

func getAllResponseHeaders*(this: XMLHttpRequest): cstring {.importjs: "#.$1()".}
## https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders

func overrideMimeType*(this: XMLHttpRequest; mimeType: cstring) {.importjs: "#.$1(#)".}
## https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/overrideMimeType

func setRequestHeader*(this: XMLHttpRequest; key, value: cstring) {.importjs: "#.$1(#, #)".}
## https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader

func setRequestHeader*(this: XMLHttpRequest; keyValuePairs: openArray[tuple[key, val: cstring]]) {.importjs:
"(() => { const rqst = #; #.forEach((item) => rqst.$1(item[0], item[1])) })()".}
## Same as `setRequestHeader` but takes `openArray[tuple[key, val: cstring]]`.


runnableExamples:
from std/dom import Node
if defined(fusionJsXmlhttprequestTests):
juancarlospaco marked this conversation as resolved.
Show resolved Hide resolved
let request: XMLHttpRequest = newXMLHttpRequest()
request.open("GET".cstring, "http://localhost:8000/".cstring, false)
request.setRequestHeader("mode".cstring, "no-cors".cstring)
request.setRequestHeader([(key: "mode".cstring, val: "no-cors".cstring)])
request.overrideMimeType("text/plain".cstring)
request.send()
echo request.getAllResponseHeaders()
echo "responseText\t", request.responseText
echo "responseURL\t", request.responseURL
echo "statusText\t", request.statusText
echo "responseXML\t", request.responseXML is Node
echo "status\t", request.status
echo "timeout\t", request.timeout
echo "withCredentials\t", request.withCredentials
echo "readyState\t", request.readyState
request.abort()
5 changes: 4 additions & 1 deletion tools/kochdocs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ const
webUploadOutput = "web/upload"

var nimExe*: string
const allowList = ["jsbigints.nim", "jsheaders.nim", "jsformdata.nim", "jsfetch.nim", "jsutils.nim"]
const allowList = [
juancarlospaco marked this conversation as resolved.
Show resolved Hide resolved
"jsbigints.nim", "jsheaders.nim", "jsformdata.nim", "jsfetch.nim", "jshttpclient.nim",
"jssynchttpclient.nim", "jsasynchttpclient.nim", "jsxmlhttprequest.nim", "jsutils.nim",
]

template isJsOnly(file: string): bool =
file.isRelativeTo("lib/js") or
Expand Down