Skip to content

Commit

Permalink
feat(js-runtime,serverless,cli): allow fetch() with a Request (#965)
Browse files Browse the repository at this point in the history
  • Loading branch information
QuiiBz authored Jun 17, 2023
1 parent 507e448 commit 32a2c08
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 7 deletions.
7 changes: 7 additions & 0 deletions .changeset/pretty-jeans-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@lagon/js-runtime': patch
'@lagon/cli': patch
'@lagon/serverless': patch
---

Allow `fetch()` with a `Request`
82 changes: 82 additions & 0 deletions crates/runtime/tests/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,85 @@ async fn fetch_set_content_length() {
)
.await;
}

#[tokio::test]
async fn fetch_input_request() {
utils::setup();
let server = Server::run();
server.expect(
Expectation::matching(all_of![
request::method_path("POST", "/"),
request::headers(contains(("x-token", "hello"))),
request::body("Hello!"),
])
.respond_with(status_code(200).body("Hello, World")),
);
let url = server.url("/");

let (send, receiver) = utils::create_isolate(IsolateOptions::new(format!(
"export async function handler() {{
const body = await fetch(new Request('{url}', {{
method: 'POST',
headers: {{
'x-token': 'hello'
}},
body: 'Hello!'
}})).then(res => res.text());
return new Response(body);
}}"
)));
send(Request::default());

utils::assert_response(
&receiver,
Response::builder()
.header(CONTENT_TYPE, "text/plain;charset=UTF-8")
.body("Hello, World".into())
.unwrap(),
)
.await;
}

#[tokio::test]
async fn fetch_input_request_init() {
utils::setup();
let server = Server::run();
server.expect(
Expectation::matching(all_of![
request::method_path("POST", "/"),
request::headers(contains(("x-token", "hello"))),
request::body("Hello!"),
])
.respond_with(status_code(200).body("Hello, World")),
);
let url = server.url("/");

let (send, receiver) = utils::create_isolate(IsolateOptions::new(format!(
"export async function handler() {{
const body = await fetch(new Request('{url}', {{
method: 'GET',
headers: {{
'hello': 'world'
}},
body: 'No'
}}), {{
method: 'POST',
headers: {{
'x-token': 'hello'
}},
body: 'Hello!'
}}).then(res => res.text());
return new Response(body);
}}"
)));
send(Request::default());

utils::assert_response(
&receiver,
Response::builder()
.header(CONTENT_TYPE, "text/plain;charset=UTF-8")
.body("Hello, World".into())
.unwrap(),
)
.await;
}
37 changes: 30 additions & 7 deletions packages/js-runtime/src/runtime/http/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,42 @@
const headers = new Headers(init?.headers);
let body: string | undefined;

if (init?.body) {
if (globalThis.__lagon__.isIterable(init.body)) {
body = globalThis.__lagon__.TEXT_DECODER.decode(init.body);
const isInputRequest = input instanceof Request;

if (init?.body || (isInputRequest && (input as Request).body)) {
const paramBody = init?.body || (input as Request).body;

if (globalThis.__lagon__.isIterable(paramBody)) {
body = globalThis.__lagon__.TEXT_DECODER.decode(paramBody);
} else if (paramBody instanceof ReadableStream) {
body = '';

// @ts-expect-error iterate over the stream
for await (const chunk of paramBody) {
body += globalThis.__lagon__.TEXT_DECODER.decode(chunk);
}
} else {
if (typeof init.body !== 'string') {
if (typeof paramBody !== 'string') {
// TODO: Support other body types
throw new Error('Body must be a string or an iterable');
}

body = init.body;
body = paramBody;
}
}

let method = init?.method || 'GET';
let url = input.toString();

if (isInputRequest) {
for (const [key, value] of (input as Request).headers.entries()) {
headers.set(key, value);
}

method = init?.method || (input as Request).method;
url = (input as Request).url.toString();
}

if (body === undefined && init?.method && FORCE_0_CONTENT_LENGTH_METHODS.includes(init.method)) {
headers.set('content-length', '0');
}
Expand All @@ -32,8 +55,8 @@
checkAborted();

const response = await LagonAsync.fetch({
m: init?.method || 'GET',
u: input.toString(),
m: method,
u: url,
b: body,
// @ts-expect-error private property
h: headers.h,
Expand Down

0 comments on commit 32a2c08

Please sign in to comment.