Skip to content

Commit

Permalink
feat(ext/web): implement static Response.json (#14566)
Browse files Browse the repository at this point in the history
This commit adds support for the static `Response.json` method.
  • Loading branch information
lucacasonato committed May 13, 2022
1 parent 42fec51 commit a5b50d0
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 44 deletions.
119 changes: 87 additions & 32 deletions ext/fetch/23_response.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
const { isProxy } = Deno.core;
const webidl = window.__bootstrap.webidl;
const consoleInternal = window.__bootstrap.console;
const { HTTP_TAB_OR_SPACE, regexMatcher } = window.__bootstrap.infra;
const { HTTP_TAB_OR_SPACE, regexMatcher, serializeJSValueToJSONString } =
window.__bootstrap.infra;
const { extractBody, mixinBody } = window.__bootstrap.fetchBody;
const { getLocationHref } = window.__bootstrap.location;
const { extractMimeType } = window.__bootstrap.mimesniff;
Expand Down Expand Up @@ -157,6 +158,56 @@
return resp;
}

/**
* https://fetch.spec.whatwg.org#initialize-a-response
* @param {Response} response
* @param {ResponseInit} init
* @param {{ body: __bootstrap.fetchBody.InnerBody, contentType: string | null } | null} bodyWithType
*/
function initializeAResponse(response, init, bodyWithType) {
// 1.
if ((init.status < 200 || init.status > 599) && init.status != 101) {
throw new RangeError(
`The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
);
}

// 2.
if (
init.statusText &&
!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
) {
throw new TypeError("Status text is not valid.");
}

// 3.
response[_response].status = init.status;

// 4.
response[_response].statusMessage = init.statusText;

// 5.
/** @type {__bootstrap.headers.Headers} */
const headers = response[_headers];
if (init.headers) {
fillHeaders(headers, init.headers);
}

// 6.
if (bodyWithType !== null) {
if (nullBodyStatus(response[_response].status)) {
throw new TypeError(
"Response with null body status cannot have body",
);
}
const { body, contentType } = bodyWithType;
response[_response].body = body;
if (contentType !== null && !headers.has("content-type")) {
headers.append("Content-Type", contentType);
}
}
}

class Response {
get [_mimeType]() {
const values = getDecodeSplitHeader(
Expand Down Expand Up @@ -217,6 +268,32 @@
return response;
}

/**
* @param {any} data
* @param {ResponseInit} init
* @returns {Response}
*/
static json(data, init = {}) {
const prefix = "Failed to call 'Response.json'";
data = webidl.converters.any(data);
init = webidl.converters["ResponseInit_fast"](init, {
prefix,
context: "Argument 2",
});

const str = serializeJSValueToJSONString(data);
const res = extractBody(str);
res.contentType = "application/json";
const response = webidl.createBranded(Response);
response[_response] = newInnerResponse();
response[_headers] = headersFromHeaderList(
response[_response].headerList,
"response",
);
initializeAResponse(response, init, res);
return response;
}

/**
* @param {BodyInit | null} body
* @param {ResponseInit} init
Expand All @@ -232,40 +309,18 @@
context: "Argument 2",
});

if ((init.status < 200 || init.status > 599) && init.status != 101) {
throw new RangeError(
`The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
);
}

if (
init.statusText &&
!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
) {
throw new TypeError("Status text is not valid.");
}
this[_response] = newInnerResponse();
this[_headers] = headersFromHeaderList(
this[_response].headerList,
"response",
);

this[webidl.brand] = webidl.brand;
const response = newInnerResponse(init.status, init.statusText);
/** @type {InnerResponse} */
this[_response] = response;
/** @type {Headers} */
this[_headers] = headersFromHeaderList(response.headerList, "response");
if (init.headers) {
fillHeaders(this[_headers], init.headers);
}
let bodyWithType = null;
if (body !== null) {
if (nullBodyStatus(response.status)) {
throw new TypeError(
"Response with null body status cannot have body",
);
}
const res = extractBody(body);
response.body = res.body;
if (res.contentType !== null && !this[_headers].has("content-type")) {
this[_headers].append("Content-Type", res.contentType);
}
bodyWithType = extractBody(body);
}
initializeAResponse(this, init, bodyWithType);
this[webidl.brand] = webidl.brand;
}

/**
Expand Down
34 changes: 24 additions & 10 deletions ext/web/00_infra.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,24 @@
((window) => {
const core = Deno.core;
const {
Error,
RegExp,
ArrayPrototypeJoin,
ArrayPrototypeMap,
StringPrototypeCharCodeAt,
Error,
JSONStringify,
NumberPrototypeToString,
StringPrototypePadStart,
TypeError,
ArrayPrototypeJoin,
RegExp,
SafeArrayIterator,
String,
StringPrototypeCharAt,
StringPrototypeCharCodeAt,
StringPrototypeMatch,
StringPrototypeSlice,
String,
StringPrototypePadStart,
StringPrototypeReplace,
StringPrototypeToUpperCase,
StringPrototypeToLowerCase,
StringPrototypeSlice,
StringPrototypeSubstring,
StringPrototypeToLowerCase,
StringPrototypeToUpperCase,
TypeError,
} = window.__bootstrap.primordials;

const ASCII_DIGIT = ["\u0030-\u0039"];
Expand Down Expand Up @@ -294,6 +295,18 @@
}
}

/**
* @param {unknown} value
* @returns {string}
*/
function serializeJSValueToJSONString(value) {
const result = JSONStringify(value);
if (result === undefined) {
throw new TypeError("Value is not JSON serializable.");
}
return result;
}

window.__bootstrap.infra = {
collectSequenceOfCodepoints,
ASCII_DIGIT,
Expand All @@ -320,5 +333,6 @@
forgivingBase64Decode,
AssertionError,
assert,
serializeJSValueToJSONString,
};
})(globalThis);
1 change: 1 addition & 0 deletions ext/web/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ declare namespace globalThis {
};
forgivingBase64Encode(data: Uint8Array): string;
forgivingBase64Decode(data: string): Uint8Array;
serializeJSValueToJSONString(value: unknown): string;
};

declare var domException: {
Expand Down
2 changes: 1 addition & 1 deletion test_util/wpt
4 changes: 3 additions & 1 deletion tools/wpt/expectation.json
Original file line number Diff line number Diff line change
Expand Up @@ -3215,7 +3215,9 @@
"response-consume-stream.any.html": true,
"response-consume-stream.any.worker.html": true,
"response-init-contenttype.any.html": true,
"response-init-contenttype.any.worker.html": true
"response-init-contenttype.any.worker.html": true,
"response-static-json.any.html": true,
"response-static-json.any.worker.html": true
},
"body": {
"formdata.any.html": [
Expand Down

0 comments on commit a5b50d0

Please sign in to comment.