summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDivy Srivastava <dj.srivastava23@gmail.com>2022-09-07 16:21:30 +0530
committerGitHub <noreply@github.com>2022-09-07 16:21:30 +0530
commitd57f9d560d7b675bda3cf3ba0ac69201b73467b3 (patch)
tree4103a67ede48f3ab0a7be55a91de492e8f096631
parent7f4b043f322f87110251ce7d7e52f444ad1f0940 (diff)
fix(ext/flash): use utf8 length as Content-Length (#15793)
-rw-r--r--cli/tests/unit/flash_test.ts37
-rw-r--r--core/01_core.js1
-rw-r--r--core/ops_builtin.rs13
-rw-r--r--ext/flash/01_http.js20
4 files changed, 68 insertions, 3 deletions
diff --git a/cli/tests/unit/flash_test.ts b/cli/tests/unit/flash_test.ts
index 78340a390..5e1e55d86 100644
--- a/cli/tests/unit/flash_test.ts
+++ b/cli/tests/unit/flash_test.ts
@@ -495,6 +495,43 @@ Deno.test(
},
);
+Deno.test(
+ { permissions: { net: true } },
+ async function httpServerCorrectLengthForUnicodeString() {
+ const ac = new AbortController();
+ const listeningPromise = deferred();
+
+ const server = Deno.serve({
+ handler: () => new Response("韓國".repeat(10)),
+ port: 4503,
+ signal: ac.signal,
+ onListen: onListen(listeningPromise),
+ onError: createOnErrorCb(ac),
+ });
+
+ await listeningPromise;
+ const conn = await Deno.connect({ port: 4503 });
+ const encoder = new TextEncoder();
+ const decoder = new TextDecoder();
+
+ const body =
+ `GET / HTTP/1.1\r\nHost: example.domain\r\nConnection: close\r\n\r\n`;
+ const writeResult = await conn.write(encoder.encode(body));
+ assertEquals(body.length, writeResult);
+
+ const buf = new Uint8Array(1024);
+ const readResult = await conn.read(buf);
+ assert(readResult);
+ const msg = decoder.decode(buf.subarray(0, readResult));
+
+ conn.close();
+
+ ac.abort();
+ await server;
+ assert(msg.includes("Content-Length: 60"));
+ },
+);
+
Deno.test({ permissions: { net: true } }, async function httpServerWebSocket() {
const ac = new AbortController();
const listeningPromise = deferred();
diff --git a/core/01_core.js b/core/01_core.js
index e5e2c9fd5..ddd3ac82d 100644
--- a/core/01_core.js
+++ b/core/01_core.js
@@ -323,6 +323,7 @@
opNames: () => ops.op_op_names(),
eventLoopHasMoreWork: () => ops.op_event_loop_has_more_work(),
setPromiseRejectCallback: (fn) => ops.op_set_promise_reject_callback(fn),
+ byteLength: (str) => ops.op_str_byte_length(str),
});
ObjectAssign(globalThis.__bootstrap, { core });
diff --git a/core/ops_builtin.rs b/core/ops_builtin.rs
index bd19b74f6..26ab4bed5 100644
--- a/core/ops_builtin.rs
+++ b/core/ops_builtin.rs
@@ -39,6 +39,7 @@ pub(crate) fn init_builtins() -> Extension {
op_metrics::decl(),
op_format_file_name::decl(),
op_is_proxy::decl(),
+ op_str_byte_length::decl(),
])
.ops(crate::ops_builtin_v8::init_builtins_v8())
.build()
@@ -195,3 +196,15 @@ fn op_format_file_name(file_name: String) -> String {
fn op_is_proxy(value: serde_v8::Value) -> bool {
value.v8_value.is_proxy()
}
+
+#[op(v8)]
+fn op_str_byte_length(
+ scope: &mut v8::HandleScope,
+ value: serde_v8::Value,
+) -> u32 {
+ if let Ok(string) = v8::Local::<v8::String>::try_from(value.v8_value) {
+ string.utf8_length(scope) as u32
+ } else {
+ 0
+ }
+}
diff --git a/ext/flash/01_http.js b/ext/flash/01_http.js
index b00c9f8e4..949eb0ac3 100644
--- a/ext/flash/01_http.js
+++ b/ext/flash/01_http.js
@@ -122,7 +122,14 @@
// CRLF
// [ message-body ]
//
- function http1Response(method, status, headerList, body, earlyEnd = false) {
+ function http1Response(
+ method,
+ status,
+ headerList,
+ body,
+ bodyLen,
+ earlyEnd = false,
+ ) {
// HTTP uses a "<major>.<minor>" numbering scheme
// HTTP-version = HTTP-name "/" DIGIT "." DIGIT
// HTTP-name = %x48.54.54.50 ; "HTTP", case-sensitive
@@ -155,7 +162,7 @@
// null body status is validated by inititalizeAResponse in ext/fetch
if (body !== null && body !== undefined) {
- str += `Content-Length: ${body.length}\r\n\r\n`;
+ str += `Content-Length: ${bodyLen}\r\n\r\n`;
} else {
str += "Transfer-Encoding: chunked\r\n\r\n";
return str;
@@ -192,6 +199,7 @@
server,
requestId,
response,
+ responseLen,
end,
respondFast,
) {
@@ -215,7 +223,7 @@
}
}
- if (nwritten < response.length) {
+ if (nwritten < responseLen) {
core.opAsync(
"op_flash_respond_async",
server,
@@ -421,16 +429,19 @@
const ws = resp[_ws];
if (isStreamingResponseBody === false) {
+ const length = respBody.byteLength || core.byteLength(respBody);
const responseStr = http1Response(
method,
innerResp.status ?? 200,
innerResp.headerList,
respBody,
+ length,
);
writeFixedResponse(
serverId,
i,
responseStr,
+ length,
!ws, // Don't close socket if there is a deferred websocket upgrade.
respondFast,
);
@@ -460,6 +471,7 @@
method,
innerResp.status ?? 200,
innerResp.headerList,
+ 0, // Content-Length will be set by the op.
null,
true,
),
@@ -483,8 +495,10 @@
method,
innerResp.status ?? 200,
innerResp.headerList,
+ respBody.byteLength,
null,
),
+ respBody.byteLength,
false,
respondFast,
);