Skip to content

Commit

Permalink
Implement Response.redirect (#62)
Browse files Browse the repository at this point in the history
This also applies some cleanups to Response creation overall.
  • Loading branch information
tschneidereit authored Jun 21, 2024
1 parent 63a0464 commit 9946868
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 133 deletions.
190 changes: 71 additions & 119 deletions builtins/web/fetch/request-response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2112,121 +2112,68 @@ bool Response::bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp) {

// https://fetch.spec.whatwg.org/#dom-response-redirect
// [NewObject] static Response redirect(USVString url, optional unsigned short status = 302);
// bool Response::redirect(JSContext *cx, unsigned argc, JS::Value *vp) {
// JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
// if (!args.requireAtLeast(cx, "redirect", 1)) {
// return false;
// }
// // auto url = args.get(0);
// // // 1. Let parsedURL be the result of parsing url with current settings object’s API base
// URL.
// // JS::RootedObject urlInstance(
// // cx, JS_NewObjectWithGivenProto(cx, &url::URL::class_, url::URL::proto_obj));
// // if (!urlInstance) {
// // return false;
// // }
// // JS::RootedObject parsedURL(
// // cx, url::URL::create(cx, urlInstance, url, worker_location::WorkerLocation::url));
// // // 2. If parsedURL is failure, then throw a TypeError.
// // if (!parsedURL) {
// // JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
// JSMSG_RESPONSE_REDIRECT_INVALID_URI);
// // return false;
// // }
// // JS::RootedValue url_val(cx, JS::ObjectValue(*parsedURL));
// // auto url_str = core::encode(cx, url_val);
// // if (!url_str) {
// // return false;
// // }
// // 3. If status is not a redirect status, then throw a RangeError.
// // A redirect status is a status that is 301, 302, 303, 307, or 308.
// auto statusVal = args.get(1);
// uint16_t status;
// if (statusVal.isUndefined()) {
// status = 302;
// } else {
// if (!JS::ToUint16(cx, statusVal, &status)) {
// return false;
// }
// }
// if (status != 301 && status != 302 && status != 303 && status != 307 && status != 308) {
// JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
// JSMSG_RESPONSE_REDIRECT_INVALID_STATUS); return false;
// }
// // 4. Let responseObject be the result of creating a Response object, given a new response,
// // "immutable", and this’s relevant Realm.
// auto response_handle_res = host_api::HttpResp::make();
// if (auto *err = response_handle_res.to_err()) {
// HANDLE_ERROR(cx, *err);
// return false;
// }

// auto response_handle = response_handle_res.unwrap();
// if (!response_handle.is_valid()) {
// return false;
// }
bool Response::redirect(JSContext *cx, unsigned argc, Value *vp) {
CallArgs args = JS::CallArgsFromVp(argc, vp);
if (!args.requireAtLeast(cx, "redirect", 1)) {
return false;
}

// auto make_res = host_api::HttpBody::make(response_handle);
// if (auto *err = make_res.to_err()) {
// HANDLE_ERROR(cx, *err);
// return false;
// }
// 1. Let parsedURL be the result of parsing url with current settings object’s API base
// URL.
jsurl::SpecString url_str = core::encode(cx, args.get(0));
if (!url_str.data) {
return false;
}
auto parsedURL = new_jsurl_with_base(&url_str, url::URL::url(worker_location::WorkerLocation::url));
if (!parsedURL) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_RESPONSE_REDIRECT_INVALID_URI);
return false;
}

// auto body = make_res.unwrap();
// JS::RootedObject response_instance(cx, JS_NewObjectWithGivenProto(cx, &Response::class_,
// Response::proto_obj));
// if (!response_instance) {
// return false;
// }
// JS::RootedObject response(
// cx, create(cx, response_instance, response_handle, body, false));
// if (!response) {
// return false;
// }
// 3. If status is not a redirect status, then throw a RangeError.
// A redirect status is a status that is 301, 302, 303, 307, or 308.
auto statusVal = args.get(1);
uint16_t status;
if (statusVal.isUndefined()) {
status = 302;
} else {
if (!ToUint16(cx, statusVal, &status)) {
return false;
}
}
if (status != 301 && status != 302 && status != 303 && status != 307 && status != 308) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_RESPONSE_REDIRECT_INVALID_STATUS);
return false;
}

// // 5. Set responseObject’s response’s status to status.
// auto set_res = response_handle.set_status(status);
// if (auto *err = set_res.to_err()) {
// HANDLE_ERROR(cx, *err);
// return false;
// }
// // To ensure that we really have the same status value as the host,
// // we always read it back here.
// auto get_res = response_handle.get_status();
// if (auto *err = get_res.to_err()) {
// HANDLE_ERROR(cx, *err);
// return false;
// }
// status = get_res.unwrap();
// 4. Let responseObject be the result of creating a Response object, given a new response,
// "immutable", and this’s relevant Realm.
RootedObject responseObject(cx, create(cx));
if (!responseObject) {
return false;
}

// JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::Status), JS::Int32Value(status));
// JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::StatusMessage),
// JS::StringValue(JS_GetEmptyString(cx)));
// // 6. Let value be parsedURL, serialized and isomorphic encoded.
// // 7. Append (`Location`, value) to responseObject’s response’s header list.
// JS::RootedObject headers(cx);
// JS::RootedObject headersInstance(
// cx, JS_NewObjectWithGivenProto(cx, &Headers::class_, Headers::proto_obj));
// if (!headersInstance)
// return false;
// 5. Set responseObject’s response’s status to status.
SetReservedSlot(responseObject, static_cast<uint32_t>(Slots::Status), JS::Int32Value(status));
SetReservedSlot(responseObject, static_cast<uint32_t>(Slots::StatusMessage),
JS::StringValue(JS_GetEmptyString(cx)));

// headers = Headers::create(cx, headersInstance, Headers::Mode::ProxyToResponse,
// response);
// if (!headers) {
// return false;
// }
// // TODO: support use of baseURL
// // if (!Headers::maybe_add(cx, headers, "location", url_str.begin())) {
// // return false;
// // }
// JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::Headers),
// JS::ObjectValue(*headers)); JS::SetReservedSlot(response,
// static_cast<uint32_t>(Slots::Redirected), JS::FalseValue());
// // 8. Return responseObject.
// 6. Let value be parsedURL, serialized and isomorphic encoded.
// 7. Append (`Location`, value) to responseObject’s response’s header list.
RootedObject headers(cx, RequestOrResponse::headers(cx, responseObject));
if (!headers) {
return false;
}
if (!Headers::set_if_undefined(cx, headers, "location", url_str)) {
return false;
}

// args.rval().setObjectOrNull(response);
// return true;
// }
// 8. Return responseObject.
args.rval().setObjectOrNull(responseObject);
return true;
}

// namespace {
// bool callbackCalled;
Expand Down Expand Up @@ -2386,7 +2333,7 @@ bool Response::bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp) {
// }

const JSFunctionSpec Response::static_methods[] = {
// JS_FN("redirect", redirect, 1, JSPROP_ENUMERATE),
JS_FN("redirect", redirect, 1, JSPROP_ENUMERATE),
// JS_FN("json", json, 1, JSPROP_ENUMERATE),
JS_FS_END,
};
Expand Down Expand Up @@ -2476,14 +2423,11 @@ bool Response::constructor(JSContext *cx, unsigned argc, JS::Value *vp) {
return false;
}

JS::RootedObject responseInstance(cx, JS_NewObjectForConstructor(cx, &class_, args));
if (!responseInstance) {
return false;
}
JS::RootedObject response(cx, create(cx, responseInstance));
JS::RootedObject response(cx, JS_NewObjectForConstructor(cx, &class_, args));
if (!response) {
return false;
}
init_slots(response);

JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::Headers), JS::ObjectValue(*headers));

Expand Down Expand Up @@ -2550,8 +2494,16 @@ bool Response::init_class(JSContext *cx, JS::HandleObject global) {
(type_error_atom = JS_AtomizeAndPinString(cx, "error"));
}

JSObject *Response::create(JSContext *cx, JS::HandleObject response) {
MOZ_ASSERT(cx);
JSObject *Response::create(JSContext *cx){
RootedObject self(cx, JS_NewObjectWithGivenProto(cx, &class_, proto_obj));
if (!self) {
return nullptr;
}
init_slots(self);
return self;
}

JSObject *Response::init_slots(HandleObject response) {
MOZ_ASSERT(is_instance(response));

JS::SetReservedSlot(response, static_cast<uint32_t>(Slots::Response), JS::PrivateValue(nullptr));
Expand All @@ -2564,8 +2516,8 @@ JSObject *Response::create(JSContext *cx, JS::HandleObject response) {
return response;
}

JSObject * Response::create_incoming(JSContext *cx, HandleObject obj, host_api::HttpIncomingResponse *response) {
RootedObject self(cx, create(cx, obj));
JSObject * Response::create_incoming(JSContext *cx, host_api::HttpIncomingResponse *response) {
RootedObject self(cx, create(cx));
if (!self) {
return nullptr;
}
Expand Down
14 changes: 4 additions & 10 deletions builtins/web/fetch/request-response.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,9 @@ class Response final : public BuiltinImpl<Response> {
static bool init_class(JSContext *cx, JS::HandleObject global);
static bool constructor(JSContext *cx, unsigned argc, JS::Value *vp);

static JSObject *create(JSContext *cx, JS::HandleObject response);
static JSObject* create_incoming(JSContext * cx, HandleObject self,
host_api::HttpIncomingResponse* response);
static JSObject *create(JSContext *cx);
static JSObject *init_slots(HandleObject response);
static JSObject* create_incoming(JSContext * cx, host_api::HttpIncomingResponse* response);

static host_api::HttpResponse *response_handle(JSObject *obj);
static uint16_t status(JSObject *obj);
Expand Down Expand Up @@ -243,13 +243,7 @@ class ResponseFutureTask final : public api::AsyncTask {
auto maybe_response = res.unwrap();
MOZ_ASSERT(maybe_response.has_value());
auto response = maybe_response.value();
RootedObject response_obj(
cx, JS_NewObjectWithGivenProto(cx, &Response::class_, Response::proto_obj));
if (!response_obj) {
return false;
}

response_obj = Response::create_incoming(cx, response_obj, response);
RootedObject response_obj(cx, Response::create_incoming(cx, response));
if (!response_obj) {
return false;
}
Expand Down
15 changes: 11 additions & 4 deletions builtins/web/url.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,9 +540,13 @@ const JSPropertySpec URL::properties[] = {
JS_PS_END,
};

const jsurl::JSUrl *URL::url(JSObject *self) {
MOZ_ASSERT(is_instance(self));
return static_cast<const jsurl::JSUrl *>(JS::GetReservedSlot(self, Url).toPrivate());
}

jsurl::SpecString URL::origin(JSContext *cx, JS::HandleObject self) {
auto *url = static_cast<const jsurl::JSUrl *>(JS::GetReservedSlot(self, Slots::Url).toPrivate());
return jsurl::origin(url);
return jsurl::origin(url(self));
}

bool URL::origin(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval) {
Expand All @@ -564,12 +568,15 @@ bool URL::searchParams_get(JSContext *cx, unsigned argc, JS::Value *vp) {
JS::Value params_val = JS::GetReservedSlot(self, Slots::Params);
JS::RootedObject params(cx);
if (params_val.isNullOrUndefined()) {
auto *url = static_cast<jsurl::JSUrl *>(JS::GetReservedSlot(self, Slots::Url).toPrivate());
JS::RootedObject url_search_params_instance(
cx, JS_NewObjectWithGivenProto(cx, &URLSearchParams::class_, URLSearchParams::proto_obj));
if (!url_search_params_instance)
return false;
params = URLSearchParams::create(cx, url_search_params_instance, url);

// The const-cast here is okay because we while normally callers of URL::url mustn't mutate
// the returned object, URLSearchParams is intended to.
params = URLSearchParams::create(cx, url_search_params_instance,
const_cast<jsurl::JSUrl *>(url(self)));
if (!params)
return false;
JS::SetReservedSlot(self, Slots::Params, JS::ObjectValue(*params));
Expand Down
2 changes: 2 additions & 0 deletions builtins/web/url.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ class URL : public BuiltinImpl<URL> {

static const unsigned ctor_length = 1;

static const jsurl::JSUrl *url(JSObject *self);
static jsurl::SpecString origin(JSContext *cx, JS::HandleObject self);

static bool origin(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval);
static bool hash(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval);
static bool host(JSContext *cx, JS::HandleObject self, JS::MutableHandleValue rval);
Expand Down
5 changes: 5 additions & 0 deletions crates/rust-url/rust-url.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ struct SpecString {
cap(cap)
{}


/// Conversion to a `jsurl::SpecString`.
operator const std::string_view() const {
return std::string_view(reinterpret_cast<char *>(this->data), this->len);
}
};

/// This type exists to transfer &str-likes over FFI.
Expand Down
5 changes: 5 additions & 0 deletions include/host_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ struct HostString final {
return jsurl::SpecString(reinterpret_cast<uint8_t *>(this->ptr.release()), this->len,
this->len);
}

/// Conversion to a `jsurl::SpecString`.
operator const jsurl::SpecString() const {
return jsurl::SpecString(reinterpret_cast<uint8_t *>(this->ptr.get()), this->len, this->len);
}
};

struct HostBytes final {
Expand Down

0 comments on commit 9946868

Please sign in to comment.