Skip to content

Commit

Permalink
fix: empty body responses (supabase#84)
Browse files Browse the repository at this point in the history
* Fix body null for null status

* Post request

* adding size

* clippy,fmt

* hasReqBody
  • Loading branch information
andreespirela authored May 10, 2023
1 parent 31180a0 commit c02987e
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 7 deletions.
5 changes: 5 additions & 0 deletions crates/base/test_cases/empty-response/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { serve } from "https://deno.land/std@0.131.0/http/server.ts"

serve(async (req: Request) => {
return new Response(null, { status: 204 })
});
71 changes: 71 additions & 0 deletions crates/base/tests/null_body_status_null_body_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use base::worker_ctx::{create_worker, WorkerRequestMsg};
use hyper::{Body, Request, Response};
use sb_worker_context::essentials::{EdgeContextInitOpts, EdgeContextOpts, EdgeUserRuntimeOpts};
use std::collections::HashMap;
use tokio::sync::oneshot;

#[tokio::test]
async fn test_null_body_with_204_status() {
let user_rt_opts = EdgeUserRuntimeOpts::default();
let opts = EdgeContextInitOpts {
service_path: "./test_cases/empty-response".into(),
no_module_cache: false,
import_map_path: None,
env_vars: HashMap::new(),
conf: EdgeContextOpts::UserWorker(user_rt_opts),
};
let worker_req_tx = create_worker(opts).await.unwrap();
let (res_tx, res_rx) = oneshot::channel::<Result<Response<Body>, hyper::Error>>();

let req = Request::builder()
.uri("/")
.method("GET")
.body(Body::empty())
.unwrap();

let msg = WorkerRequestMsg { req, res_tx };
let _ = worker_req_tx.send(msg);

let res = res_rx.await.unwrap().unwrap();
assert!(res.status().as_u16() == 204);

let body_bytes = hyper::body::to_bytes(res.into_body())
.await
.unwrap()
.to_vec();

assert_eq!(body_bytes.len(), 0);
}

#[tokio::test]
async fn test_null_body_with_204_status_post() {
let user_rt_opts = EdgeUserRuntimeOpts::default();
let opts = EdgeContextInitOpts {
service_path: "./test_cases/empty-response".into(),
no_module_cache: false,
import_map_path: None,
env_vars: HashMap::new(),
conf: EdgeContextOpts::UserWorker(user_rt_opts),
};
let worker_req_tx = create_worker(opts).await.unwrap();
let (res_tx, res_rx) = oneshot::channel::<Result<Response<Body>, hyper::Error>>();

let req = Request::builder()
.uri("/")
.method("POST")
.body(Body::empty())
.unwrap();

let msg = WorkerRequestMsg { req, res_tx };
let _ = worker_req_tx.send(msg);

let res = res_rx.await.unwrap().unwrap();
assert!(res.status().as_u16() == 204);

let body_bytes = hyper::body::to_bytes(res.into_body())
.await
.unwrap()
.to_vec();

assert_eq!(body_bytes.len(), 0);
}
1 change: 1 addition & 0 deletions crates/base/tests/oak_user_worker_tests.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod null_body_status_null_body_tests;
mod tls_invalid_data_tests;

use base::worker_ctx::{create_worker, WorkerRequestMsg};
Expand Down
4 changes: 4 additions & 0 deletions crates/sb_core/js/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const buildDomErrorClass = (name) => class extends DOMException {
}
}

const InvalidWorkerResponse = buildErrorClass('InvalidWorkerResponse');
const InvalidWorkerCreation = buildErrorClass('InvalidWorkerCreation');
const NotFound = buildErrorClass('NotFound');
const PermissionDenied = buildErrorClass('PermissionDenied');
const ConnectionRefused = buildErrorClass('ConnectionRefused');
Expand Down Expand Up @@ -52,6 +54,8 @@ const DOMExceptionInvalidCharacterError = buildDomErrorClass('InvalidCharacterEr
const DOMExceptionDataError = buildDomErrorClass('DOMExceptionDataError');

function registerErrors() {
core.registerErrorClass("InvalidWorkerResponse", InvalidWorkerResponse);
core.registerErrorClass("InvalidWorkerCreation", InvalidWorkerCreation);
core.registerErrorClass("NotFound", NotFound);
core.registerErrorClass("PermissionDenied", PermissionDenied);
core.registerErrorClass("ConnectionRefused", ConnectionRefused);
Expand Down
8 changes: 5 additions & 3 deletions crates/sb_workers/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub async fn op_user_worker_create(
let result = result_rx.await;
if result.is_err() {
return Err(custom_error(
"create_user_worker_error",
"InvalidWorkerCreation",
"failed to create worker",
));
}
Expand All @@ -99,7 +99,7 @@ pub async fn op_user_worker_create(
let result = result.unwrap();
if result.is_err() {
return Err(custom_error(
"create_user_worker_error",
"InvalidWorkerCreation",
result.unwrap_err().to_string(),
));
}
Expand Down Expand Up @@ -129,6 +129,7 @@ pub struct UserWorkerResponse {
status_text: String,
headers: Vec<(ByteString, ByteString)>,
body_rid: ResourceId,
size: Option<u64>,
}

struct UserWorkerRequestResource(Request<Body>);
Expand Down Expand Up @@ -317,7 +318,7 @@ pub async fn op_user_worker_fetch_send(
let result = result_rx.await?;
if result.is_err() {
return Err(custom_error(
"user_worker_fetch",
"InvalidWorkerResponse",
"user worker not available",
));
}
Expand Down Expand Up @@ -358,6 +359,7 @@ pub async fn op_user_worker_fetch_send(
status_text,
headers,
body_rid,
size,
};
Ok(response)
}
Expand Down
8 changes: 4 additions & 4 deletions crates/sb_workers/user_workers.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,27 @@ class UserWorker {
const { method, url, headers, body, bodyUsed } = req;

const headersArray = Array.from(headers.entries());
const hasBody = !bodyUsed && !!body && (chunkExpression.test(headers.get("transfer-encoding")) ||
const hasReqBody = !bodyUsed && !!body && (chunkExpression.test(headers.get("transfer-encoding")) ||
Number.parseInt(headers.get("content-length"), 10) > 0);

const userWorkerReq = {
method,
url,
headers: headersArray,
hasBody,
hasBody: hasReqBody,
};

const { requestRid, requestBodyRid } = await ops.op_user_worker_fetch_build(userWorkerReq);

// stream the request body
if (hasBody) {
if (hasReqBody) {
let writableStream = writableStreamForRid(requestBodyRid);
body.pipeTo(writableStream);
}

const res = await core.opAsync("op_user_worker_fetch_send", this.key, requestRid);
const bodyStream = readableStreamForRid(res.bodyRid);
return new Response(bodyStream, {
return new Response(res.size ? bodyStream : null, {
headers: res.headers,
status: res.status,
statusText: res.statusText
Expand Down
5 changes: 5 additions & 0 deletions examples/empty-response/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { serve } from "https://deno.land/std@0.131.0/http/server.ts"

serve(async (req: Request) => {
return new Response(null, { status: 204 })
});

0 comments on commit c02987e

Please sign in to comment.