Skip to content

Commit 9ebb524

Browse files
authored
fix: parse latin-1 encoded field values correctly (#715)
1 parent 4829bda commit 9ebb524

File tree

13 files changed

+236
-179
lines changed

13 files changed

+236
-179
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* eslint-env serviceworker */
2+
3+
import { routes } from "./routes.js";
4+
import { pass, assert } from "./assertions.js";
5+
6+
routes.set("/headers/non-ascii-latin1-field-value", async () => {
7+
let response = await fetch("https://http-me.glitch.me/meow?header=cat:é", {
8+
backend: "httpme"
9+
})
10+
11+
let text = response.headers.get('cat')
12+
console.log("response.headers.get('cat')", response.headers.get('cat'))
13+
14+
let error = assert(text, "é", `response.headers.get('cat') === "é"`)
15+
if (error) { return error }
16+
return pass("ok")
17+
})

integration-tests/js-compute/fixtures/app/src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import "./fanout.js"
2222
import "./fastly-now.js"
2323
import "./fetch-errors.js"
2424
import "./geoip.js"
25+
import "./headers.js"
2526
import "./include-bytes.js"
2627
import "./kv-store.js"
2728
import "./logger.js"

integration-tests/js-compute/fixtures/app/tests.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4725,5 +4725,17 @@
47254725
"status": 200,
47264726
"headers": { "content-length": "11" }
47274727
}
4728+
},
4729+
4730+
"GET /headers/non-ascii-latin1-field-value": {
4731+
"environments": ["compute", "viceroy"],
4732+
"downstream_request": {
4733+
"method": "GET",
4734+
"pathname": "/headers/non-ascii-latin1-field-value"
4735+
},
4736+
"downstream_response": {
4737+
"status": 200,
4738+
"body": "ok"
4739+
}
47284740
}
47294741
}

runtime/js-compute-runtime/builtins/headers.cpp

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
#include "core/sequence.hpp"
55
#include "host_interface/host_api.h"
66
#include "js-compute-builtins.h"
7-
87
#include "js/Conversions.h"
8+
#include <iostream>
99

1010
namespace builtins {
1111

@@ -142,10 +142,27 @@ host_api::HostString normalize_header_value(JSContext *cx, JS::MutableHandleValu
142142
return nullptr;
143143
}
144144

145-
auto value = core::encode(cx, value_str);
146-
if (!value) {
145+
#pragma clang diagnostic push
146+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
147+
if (!JS_DeprecatedStringHasLatin1Chars(value_str)) {
148+
#pragma clang diagnostic pop
149+
JS::AutoCheckCannotGC nogc;
150+
size_t length;
151+
const char16_t *chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, value_str, &length);
152+
for (auto i = 0; i < length; i++) {
153+
if (chars[i] > 255) {
154+
JS_ReportErrorASCII(cx, "header value contains bytes greater than 255");
155+
return nullptr;
156+
}
157+
}
158+
}
159+
160+
host_api::HostString value;
161+
value.ptr = JS_EncodeStringToLatin1(cx, value_str);
162+
if (!value.ptr) {
147163
return nullptr;
148164
}
165+
value.len = JS_GetStringLength(value_str);
149166

150167
auto *value_chars = value.begin();
151168
size_t start = 0;
@@ -205,8 +222,9 @@ bool append_header_value_to_map(JSContext *cx, JS::HandleObject self,
205222
JS::MutableHandleValue normalized_value) {
206223
JS::RootedValue existing(cx);
207224
JS::RootedObject map(cx, get_backing_map(self));
208-
if (!JS::MapGet(cx, map, normalized_name, &existing))
225+
if (!JS::MapGet(cx, map, normalized_name, &existing)) {
209226
return false;
227+
}
210228

211229
// Existing value must only be null if we're in the process if applying
212230
// header values from a handle.
@@ -271,7 +289,6 @@ bool retrieve_value_for_header_from_handle(JSContext *cx, JS::HandleObject self,
271289

272290
JS::RootedString name_str(cx, name.toString());
273291
auto name_chars = core::encode(cx, name_str);
274-
275292
auto ret = mode == Headers::Mode::ProxyToRequest
276293
? host_api::HttpReq{handle}.get_header_values(name_chars)
277294
: host_api::HttpResp{handle}.get_header_values(name_chars);
@@ -286,9 +303,9 @@ bool retrieve_value_for_header_from_handle(JSContext *cx, JS::HandleObject self,
286303
return true;
287304
}
288305

289-
JS::RootedString val_str(cx);
290306
for (auto &str : values.value()) {
291-
val_str = JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(str.ptr.get(), str.len));
307+
JS::RootedString val_str(
308+
cx, JS_NewStringCopyN(cx, reinterpret_cast<char *>(str.ptr.get()), str.len));
292309
if (!val_str) {
293310
return false;
294311
}
@@ -403,29 +420,35 @@ std::vector<std::string_view> splitCookiesString(std::string_view cookiesString)
403420

404421
bool ensure_all_header_values_from_handle(JSContext *cx, JS::HandleObject self,
405422
JS::HandleObject backing_map) {
406-
if (!lazy_values(self))
423+
if (!lazy_values(self)) {
407424
return true;
425+
}
408426

409427
JS::RootedValue iterable(cx);
410-
if (!JS::MapKeys(cx, backing_map, &iterable))
428+
if (!JS::MapKeys(cx, backing_map, &iterable)) {
411429
return false;
430+
}
412431

413432
JS::ForOfIterator it(cx);
414-
if (!it.init(iterable))
433+
if (!it.init(iterable)) {
415434
return false;
435+
}
416436

417437
JS::RootedValue name(cx);
418438
JS::RootedValue v(cx);
419439
while (true) {
420440
bool done;
421-
if (!it.next(&name, &done))
441+
if (!it.next(&name, &done)) {
422442
return false;
443+
}
423444

424-
if (done)
445+
if (done) {
425446
break;
447+
}
426448

427-
if (!ensure_value_for_header(cx, self, name, &v))
449+
if (!ensure_value_for_header(cx, self, name, &v)) {
428450
return false;
451+
}
429452
}
430453

431454
JS_SetReservedSlot(self, static_cast<uint32_t>(Headers::Slots::HasLazyValues),
@@ -458,18 +481,22 @@ bool Headers::append_header_value(JSContext *cx, JS::HandleObject self, JS::Hand
458481
std::string_view value = value_chars;
459482
if (name == "set-cookie") {
460483
for (auto value : splitCookiesString(value)) {
484+
std::span<uint8_t> v = {reinterpret_cast<uint8_t *>(const_cast<char *>(value.data())),
485+
value.size()};
461486
auto res = mode == Headers::Mode::ProxyToRequest
462-
? host_api::HttpReq{handle}.append_header(name, value)
463-
: host_api::HttpResp{handle}.append_header(name, value);
487+
? host_api::HttpReq{handle}.append_header(name, v)
488+
: host_api::HttpResp{handle}.append_header(name, v);
464489
if (auto *err = res.to_err()) {
465490
HANDLE_ERROR(cx, *err);
466491
return false;
467492
}
468493
}
469494
} else {
495+
std::span<uint8_t> v = {reinterpret_cast<uint8_t *>(const_cast<char *>(value.data())),
496+
value.size()};
470497
auto res = mode == Headers::Mode::ProxyToRequest
471-
? host_api::HttpReq{handle}.append_header(name, value)
472-
: host_api::HttpResp{handle}.append_header(name, value);
498+
? host_api::HttpReq{handle}.append_header(name, v)
499+
: host_api::HttpResp{handle}.append_header(name, v);
473500
if (auto *err = res.to_err()) {
474501
HANDLE_ERROR(cx, *err);
475502
return false;
@@ -583,8 +610,10 @@ bool Headers::set(JSContext *cx, unsigned argc, JS::Value *vp) {
583610
auto handle = get_handle(self);
584611
std::string_view name = name_chars;
585612
std::string_view val = value_chars;
586-
auto res = mode == Mode::ProxyToRequest ? host_api::HttpReq{handle}.insert_header(name, val)
587-
: host_api::HttpResp{handle}.insert_header(name, val);
613+
std::span<uint8_t> v = {reinterpret_cast<uint8_t *>(const_cast<char *>(val.data())),
614+
val.size()};
615+
auto res = mode == Mode::ProxyToRequest ? host_api::HttpReq{handle}.insert_header(name, v)
616+
: host_api::HttpResp{handle}.insert_header(name, v);
588617
if (auto *err = res.to_err()) {
589618
HANDLE_ERROR(cx, *err);
590619
return false;

runtime/js-compute-runtime/host_interface/component/fastly_world.c

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -106,18 +106,18 @@ typedef struct {
106106
typedef struct {
107107
bool is_err;
108108
union {
109-
fastly_world_option_string_t ok;
109+
fastly_world_option_list_u8_t ok;
110110
fastly_compute_at_edge_http_req_error_t err;
111111
} val;
112-
} fastly_world_result_option_string_fastly_compute_at_edge_http_req_error_t;
112+
} fastly_world_result_option_list_u8_fastly_compute_at_edge_http_req_error_t;
113113

114114
typedef struct {
115115
bool is_err;
116116
union {
117-
fastly_world_option_list_string_t ok;
117+
fastly_world_option_list_list_u8_t ok;
118118
fastly_compute_at_edge_http_req_error_t err;
119119
} val;
120-
} fastly_world_result_option_list_string_fastly_compute_at_edge_http_req_error_t;
120+
} fastly_world_result_option_list_list_u8_fastly_compute_at_edge_http_req_error_t;
121121

122122
typedef struct {
123123
bool is_err;
@@ -194,10 +194,10 @@ typedef struct {
194194
typedef struct {
195195
bool is_err;
196196
union {
197-
fastly_world_option_list_string_t ok;
197+
fastly_world_option_list_list_u8_t ok;
198198
fastly_compute_at_edge_http_resp_error_t err;
199199
} val;
200-
} fastly_world_result_option_list_string_fastly_compute_at_edge_http_resp_error_t;
200+
} fastly_world_result_option_list_list_u8_fastly_compute_at_edge_http_resp_error_t;
201201

202202
typedef struct {
203203
bool is_err;
@@ -2259,24 +2259,24 @@ bool fastly_compute_at_edge_http_req_original_header_count(uint32_t *ret, fastly
22592259
}
22602260
}
22612261

2262-
bool fastly_compute_at_edge_http_req_header_value_get(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_option_string_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
2262+
bool fastly_compute_at_edge_http_req_header_value_get(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_option_list_u8_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
22632263
__attribute__((__aligned__(4)))
22642264
uint8_t ret_area[16];
22652265
int32_t ptr = (int32_t) &ret_area;
22662266
__wasm_import_fastly_compute_at_edge_http_req_header_value_get((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr);
2267-
fastly_world_result_option_string_fastly_compute_at_edge_http_req_error_t result;
2267+
fastly_world_result_option_list_u8_fastly_compute_at_edge_http_req_error_t result;
22682268
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
22692269
case 0: {
22702270
result.is_err = false;
2271-
fastly_world_option_string_t option;
2271+
fastly_world_option_list_u8_t option;
22722272
switch ((int32_t) (*((uint8_t*) (ptr + 4)))) {
22732273
case 0: {
22742274
option.is_some = false;
22752275
break;
22762276
}
22772277
case 1: {
22782278
option.is_some = true;
2279-
option.val = (fastly_world_string_t) { (char*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
2279+
option.val = (fastly_world_list_u8_t) { (uint8_t*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
22802280
break;
22812281
}
22822282
}
@@ -2299,24 +2299,24 @@ bool fastly_compute_at_edge_http_req_header_value_get(fastly_compute_at_edge_htt
22992299
}
23002300
}
23012301

2302-
bool fastly_compute_at_edge_http_req_header_values_get(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_option_list_string_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
2302+
bool fastly_compute_at_edge_http_req_header_values_get(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_option_list_list_u8_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
23032303
__attribute__((__aligned__(4)))
23042304
uint8_t ret_area[16];
23052305
int32_t ptr = (int32_t) &ret_area;
23062306
__wasm_import_fastly_compute_at_edge_http_req_header_values_get((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr);
2307-
fastly_world_result_option_list_string_fastly_compute_at_edge_http_req_error_t result;
2307+
fastly_world_result_option_list_list_u8_fastly_compute_at_edge_http_req_error_t result;
23082308
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
23092309
case 0: {
23102310
result.is_err = false;
2311-
fastly_world_option_list_string_t option;
2311+
fastly_world_option_list_list_u8_t option;
23122312
switch ((int32_t) (*((uint8_t*) (ptr + 4)))) {
23132313
case 0: {
23142314
option.is_some = false;
23152315
break;
23162316
}
23172317
case 1: {
23182318
option.is_some = true;
2319-
option.val = (fastly_world_list_string_t) { (fastly_world_string_t*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
2319+
option.val = (fastly_world_list_list_u8_t) { (fastly_world_list_u8_t*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
23202320
break;
23212321
}
23222322
}
@@ -2339,7 +2339,7 @@ bool fastly_compute_at_edge_http_req_header_values_get(fastly_compute_at_edge_ht
23392339
}
23402340
}
23412341

2342-
bool fastly_compute_at_edge_http_req_header_values_set(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_list_string_t *values, fastly_compute_at_edge_http_req_error_t *err) {
2342+
bool fastly_compute_at_edge_http_req_header_values_set(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_list_list_u8_t *values, fastly_compute_at_edge_http_req_error_t *err) {
23432343
__attribute__((__aligned__(1)))
23442344
uint8_t ret_area[2];
23452345
int32_t ptr = (int32_t) &ret_area;
@@ -2364,7 +2364,7 @@ bool fastly_compute_at_edge_http_req_header_values_set(fastly_compute_at_edge_ht
23642364
}
23652365
}
23662366

2367-
bool fastly_compute_at_edge_http_req_header_insert(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_compute_at_edge_http_req_error_t *err) {
2367+
bool fastly_compute_at_edge_http_req_header_insert(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_list_u8_t *value, fastly_compute_at_edge_http_req_error_t *err) {
23682368
__attribute__((__aligned__(1)))
23692369
uint8_t ret_area[2];
23702370
int32_t ptr = (int32_t) &ret_area;
@@ -2389,7 +2389,7 @@ bool fastly_compute_at_edge_http_req_header_insert(fastly_compute_at_edge_http_r
23892389
}
23902390
}
23912391

2392-
bool fastly_compute_at_edge_http_req_header_append(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_compute_at_edge_http_req_error_t *err) {
2392+
bool fastly_compute_at_edge_http_req_header_append(fastly_compute_at_edge_http_req_request_handle_t h, fastly_world_string_t *name, fastly_world_list_u8_t *value, fastly_compute_at_edge_http_req_error_t *err) {
23932393
__attribute__((__aligned__(1)))
23942394
uint8_t ret_area[2];
23952395
int32_t ptr = (int32_t) &ret_area;
@@ -3299,24 +3299,24 @@ bool fastly_compute_at_edge_http_resp_header_value_get(fastly_compute_at_edge_ht
32993299
}
33003300
}
33013301

3302-
bool fastly_compute_at_edge_http_resp_header_values_get(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_option_list_string_t *ret, fastly_compute_at_edge_http_resp_error_t *err) {
3302+
bool fastly_compute_at_edge_http_resp_header_values_get(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_option_list_list_u8_t *ret, fastly_compute_at_edge_http_resp_error_t *err) {
33033303
__attribute__((__aligned__(4)))
33043304
uint8_t ret_area[16];
33053305
int32_t ptr = (int32_t) &ret_area;
33063306
__wasm_import_fastly_compute_at_edge_http_resp_header_values_get((int32_t) (h), (int32_t) (*name).ptr, (int32_t) (*name).len, ptr);
3307-
fastly_world_result_option_list_string_fastly_compute_at_edge_http_resp_error_t result;
3307+
fastly_world_result_option_list_list_u8_fastly_compute_at_edge_http_resp_error_t result;
33083308
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
33093309
case 0: {
33103310
result.is_err = false;
3311-
fastly_world_option_list_string_t option;
3311+
fastly_world_option_list_list_u8_t option;
33123312
switch ((int32_t) (*((uint8_t*) (ptr + 4)))) {
33133313
case 0: {
33143314
option.is_some = false;
33153315
break;
33163316
}
33173317
case 1: {
33183318
option.is_some = true;
3319-
option.val = (fastly_world_list_string_t) { (fastly_world_string_t*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
3319+
option.val = (fastly_world_list_list_u8_t) { (fastly_world_list_u8_t*)(*((int32_t*) (ptr + 8))), (size_t)(*((int32_t*) (ptr + 12))) };
33203320
break;
33213321
}
33223322
}
@@ -3339,7 +3339,7 @@ bool fastly_compute_at_edge_http_resp_header_values_get(fastly_compute_at_edge_h
33393339
}
33403340
}
33413341

3342-
bool fastly_compute_at_edge_http_resp_header_values_set(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_list_string_t *values, fastly_compute_at_edge_http_resp_error_t *err) {
3342+
bool fastly_compute_at_edge_http_resp_header_values_set(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_list_list_u8_t *values, fastly_compute_at_edge_http_resp_error_t *err) {
33433343
__attribute__((__aligned__(1)))
33443344
uint8_t ret_area[2];
33453345
int32_t ptr = (int32_t) &ret_area;
@@ -3364,7 +3364,7 @@ bool fastly_compute_at_edge_http_resp_header_values_set(fastly_compute_at_edge_h
33643364
}
33653365
}
33663366

3367-
bool fastly_compute_at_edge_http_resp_header_insert(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_compute_at_edge_http_resp_error_t *err) {
3367+
bool fastly_compute_at_edge_http_resp_header_insert(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_list_u8_t *value, fastly_compute_at_edge_http_resp_error_t *err) {
33683368
__attribute__((__aligned__(1)))
33693369
uint8_t ret_area[2];
33703370
int32_t ptr = (int32_t) &ret_area;
@@ -3389,7 +3389,7 @@ bool fastly_compute_at_edge_http_resp_header_insert(fastly_compute_at_edge_http_
33893389
}
33903390
}
33913391

3392-
bool fastly_compute_at_edge_http_resp_header_append(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_string_t *value, fastly_compute_at_edge_http_resp_error_t *err) {
3392+
bool fastly_compute_at_edge_http_resp_header_append(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_string_t *name, fastly_world_list_u8_t *value, fastly_compute_at_edge_http_resp_error_t *err) {
33933393
__attribute__((__aligned__(1)))
33943394
uint8_t ret_area[2];
33953395
int32_t ptr = (int32_t) &ret_area;

0 commit comments

Comments
 (0)