Skip to content

Commit 391b3d8

Browse files
author
Guy Bedford
authored
feat: support for Response.prototype.ip and port via get_addr_dest_ip & get_addr_dest_port (#817)
1 parent 5341f67 commit 391b3d8

26 files changed

+1173
-255
lines changed

integration-tests/js-compute/fixtures/app/src/dynamic-backend.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,6 +1659,24 @@ routes.set("/backend/timeout", async () => {
16591659
return pass('ok')
16601660
});
16611661
}
1662+
1663+
// ip & port
1664+
routes.set("/backend/port-ip-defined", async () => {
1665+
allowDynamicBackends(true);
1666+
const res = await fetch('https://http-me.glitch.me/headers', {
1667+
cacheOverride: new CacheOverride("pass")
1668+
})
1669+
assert(res.port > 0)
1670+
assert(res.ip.split('.').length > 1 || res.ip.split(':').length > 1)
1671+
return pass('ok')
1672+
});
1673+
routes.set("/backend/port-ip-cached", async () => {
1674+
allowDynamicBackends(true);
1675+
const res = await fetch('https://http-me.glitch.me/headers')
1676+
assert(res.port, undefined)
1677+
assert(res.ip, undefined)
1678+
return pass('ok')
1679+
});
16621680
}
16631681

16641682
function createValidHttpMeBackend() {

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-env serviceworker */
22

33
import { routes } from "./routes.js";
4-
import { pass, assert } from "./assertions.js";
4+
import { pass, assert, assertThrows } from "./assertions.js";
55

66
routes.set("/response/text/guest-backed-stream", async () => {
77
let contents = new Array(10).fill(new Uint8Array(500).fill(65))
@@ -36,6 +36,14 @@ routes.set("/response/arrayBuffer/guest-backed-stream", async () => {
3636
if (error) { return error }
3737
return pass()
3838
})
39+
routes.set("/response/ip-port-undefined", async () => {
40+
let res = new Response()
41+
let error = assert(res.ip, undefined)
42+
if (error) { return error }
43+
error = assert(res.port, undefined)
44+
if (error) { return error }
45+
return pass()
46+
})
3947

4048
function iteratableToStream(iterable) {
4149
return new ReadableStream({

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3165,6 +3165,28 @@
31653165
"body": "ok"
31663166
}
31673167
},
3168+
"GET /backend/port-ip-defined": {
3169+
"environments": ["viceroy", "compute"],
3170+
"downstream_request": {
3171+
"method": "GET",
3172+
"pathname": "/backend/port-ip-defined"
3173+
},
3174+
"downstream_response": {
3175+
"status": 200,
3176+
"body": "ok"
3177+
}
3178+
},
3179+
"GET /backend/port-ip-cached": {
3180+
"environments": ["viceroy", "compute"],
3181+
"downstream_request": {
3182+
"method": "GET",
3183+
"pathname": "/backend/port-ip-cached"
3184+
},
3185+
"downstream_response": {
3186+
"status": 200,
3187+
"body": "ok"
3188+
}
3189+
},
31683190
"GET /dictionary/exposed-as-global": {
31693191
"environments": ["viceroy", "compute"],
31703192
"downstream_request": {
@@ -4594,6 +4616,16 @@
45944616
"status": 200
45954617
}
45964618
},
4619+
"GET /response/ip-port-undefined": {
4620+
"environments": ["viceroy", "compute"],
4621+
"downstream_request": {
4622+
"method": "GET",
4623+
"pathname": "/response/ip-port-undefined"
4624+
},
4625+
"downstream_response": {
4626+
"status": 200
4627+
}
4628+
},
45974629
"GET /setInterval/exposed-as-global": {
45984630
"environments": ["viceroy", "compute"],
45994631
"downstream_request": {

runtime/fastly/builtins/cache-simple.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class CacheTransaction final {
183183

184184
auto res = this->handle.transaction_cancel();
185185
if (auto *err = res.to_err()) {
186-
host_api::handle_fastly_error(this->cx, *err, this->line, this->func);
186+
host_api::handle_api_error(this->cx, *err, this->line, this->func);
187187
}
188188

189189
// We always reject the promise if the transaction hasn't committed.

runtime/fastly/builtins/fetch/request-response.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "js/Stream.h"
2323
#include "picosha2.h"
2424
#include <algorithm>
25+
#include <arpa/inet.h>
2526
#include <vector>
2627

2728
#pragma clang diagnostic push
@@ -2513,6 +2514,73 @@ bool Response::bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp) {
25132514
return true;
25142515
}
25152516

2517+
bool Response::ip_get(JSContext *cx, unsigned argc, JS::Value *vp) {
2518+
METHOD_HEADER(0)
2519+
2520+
// non-upstream responses always have undefined IP
2521+
if (!Response::is_upstream(self)) {
2522+
args.rval().setUndefined();
2523+
return true;
2524+
}
2525+
2526+
auto handle = response_handle(self);
2527+
auto res = handle.get_ip();
2528+
if (auto *err = res.to_err()) {
2529+
HANDLE_ERROR(cx, *err);
2530+
return false;
2531+
}
2532+
2533+
auto ret = std::move(res.unwrap());
2534+
if (!ret.has_value()) {
2535+
args.rval().setUndefined();
2536+
return true;
2537+
}
2538+
if (ret->len == 4) {
2539+
char *out = (char *)malloc(INET_ADDRSTRLEN);
2540+
inet_ntop(AF_INET, ret->ptr.get(), out, 16);
2541+
JS::RootedString text(
2542+
cx, JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(out, strnlen(out, INET_ADDRSTRLEN))));
2543+
if (!text) {
2544+
return false;
2545+
}
2546+
args.rval().setString(text);
2547+
} else {
2548+
MOZ_ASSERT(ret->len == 16);
2549+
char *out = (char *)malloc(INET6_ADDRSTRLEN);
2550+
inet_ntop(AF_INET6, ret->ptr.get(), out, 16);
2551+
JS::RootedString text(
2552+
cx, JS_NewStringCopyUTF8N(cx, JS::UTF8Chars(out, strnlen(out, INET6_ADDRSTRLEN))));
2553+
if (!text) {
2554+
return false;
2555+
}
2556+
args.rval().setString(text);
2557+
}
2558+
return true;
2559+
}
2560+
2561+
bool Response::port_get(JSContext *cx, unsigned argc, JS::Value *vp) {
2562+
METHOD_HEADER(0)
2563+
2564+
// non-upstream responses always have undefined port
2565+
if (!Response::is_upstream(self)) {
2566+
args.rval().setUndefined();
2567+
return true;
2568+
}
2569+
2570+
auto handle = response_handle(self);
2571+
auto res = handle.get_port();
2572+
if (auto *err = res.to_err()) {
2573+
HANDLE_ERROR(cx, *err);
2574+
return false;
2575+
}
2576+
if (!res.unwrap().has_value()) {
2577+
args.rval().setUndefined();
2578+
} else {
2579+
args.rval().setInt32(res.unwrap().value());
2580+
}
2581+
return true;
2582+
}
2583+
25162584
// https://fetch.spec.whatwg.org/#dom-response-redirect
25172585
// [NewObject] static Response redirect(USVString url, optional unsigned short status = 302);
25182586
bool Response::redirect(JSContext *cx, unsigned argc, JS::Value *vp) {
@@ -2836,6 +2904,8 @@ const JSPropertySpec Response::properties[] = {
28362904
JS_PSG("headers", headers_get, JSPROP_ENUMERATE),
28372905
JS_PSG("body", body_get, JSPROP_ENUMERATE),
28382906
JS_PSG("bodyUsed", bodyUsed_get, JSPROP_ENUMERATE),
2907+
JS_PSG("ip", ip_get, JSPROP_ENUMERATE),
2908+
JS_PSG("port", port_get, JSPROP_ENUMERATE),
28392909
JS_STRING_SYM_PS(toStringTag, "Response", JSPROP_READONLY),
28402910
JS_PS_END,
28412911
};

runtime/fastly/builtins/fetch/request-response.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,9 @@ class Response final : public builtins::BuiltinImpl<Response> {
188188
static bool body_get(JSContext *cx, unsigned argc, JS::Value *vp);
189189
static bool bodyUsed_get(JSContext *cx, unsigned argc, JS::Value *vp);
190190

191+
static bool ip_get(JSContext *cx, unsigned argc, JS::Value *vp);
192+
static bool port_get(JSContext *cx, unsigned argc, JS::Value *vp);
193+
191194
static bool redirect(JSContext *cx, unsigned argc, JS::Value *vp);
192195
static bool json(JSContext *cx, unsigned argc, JS::Value *vp);
193196
static bool setManualFramingHeaders(JSContext *cx, unsigned argc, JS::Value *vp);

runtime/fastly/host-api/component/fastly_world.c

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@ extern void __wasm_import_fastly_compute_at_edge_http_resp_status_set(int32_t, i
337337
__attribute__((__import_module__("fastly:compute-at-edge/http-resp"), __import_name__("close")))
338338
extern void __wasm_import_fastly_compute_at_edge_http_resp_close(int32_t, uint8_t *);
339339

340+
__attribute__((__import_module__("fastly:compute-at-edge/http-resp"), __import_name__("ip-get")))
341+
extern void __wasm_import_fastly_compute_at_edge_http_resp_ip_get(int32_t, uint8_t *);
342+
343+
__attribute__((__import_module__("fastly:compute-at-edge/http-resp"), __import_name__("port-get")))
344+
extern void __wasm_import_fastly_compute_at_edge_http_resp_port_get(int32_t, uint8_t *);
345+
340346
__attribute__((__import_module__("fastly:compute-at-edge/http-resp"), __import_name__("framing-headers-mode-set")))
341347
extern void __wasm_import_fastly_compute_at_edge_http_resp_framing_headers_mode_set(int32_t, int32_t, uint8_t *);
342348

@@ -3497,6 +3503,86 @@ bool fastly_compute_at_edge_http_resp_close(fastly_compute_at_edge_http_resp_res
34973503
}
34983504
}
34993505

3506+
bool fastly_compute_at_edge_http_resp_ip_get(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_option_list_u8_t *ret, fastly_compute_at_edge_http_resp_error_t *err) {
3507+
__attribute__((__aligned__(4)))
3508+
uint8_t ret_area[16];
3509+
uint8_t *ptr = (uint8_t *) &ret_area;
3510+
__wasm_import_fastly_compute_at_edge_http_resp_ip_get((int32_t) (h), ptr);
3511+
fastly_compute_at_edge_http_resp_result_option_list_u8_error_t result;
3512+
switch ((int32_t) *((uint8_t*) (ptr + 0))) {
3513+
case 0: {
3514+
result.is_err = false;
3515+
fastly_world_option_list_u8_t option;
3516+
switch ((int32_t) *((uint8_t*) (ptr + 4))) {
3517+
case 0: {
3518+
option.is_some = false;
3519+
break;
3520+
}
3521+
case 1: {
3522+
option.is_some = true;
3523+
option.val = (fastly_world_list_u8_t) { (uint8_t*)(*((uint8_t **) (ptr + 8))), (*((size_t*) (ptr + 12))) };
3524+
break;
3525+
}
3526+
}
3527+
3528+
result.val.ok = option;
3529+
break;
3530+
}
3531+
case 1: {
3532+
result.is_err = true;
3533+
result.val.err = (int32_t) *((uint8_t*) (ptr + 4));
3534+
break;
3535+
}
3536+
}
3537+
if (!result.is_err) {
3538+
*ret = result.val.ok;
3539+
return 1;
3540+
} else {
3541+
*err = result.val.err;
3542+
return 0;
3543+
}
3544+
}
3545+
3546+
bool fastly_compute_at_edge_http_resp_port_get(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_world_option_u16_t *ret, fastly_compute_at_edge_http_resp_error_t *err) {
3547+
__attribute__((__aligned__(2)))
3548+
uint8_t ret_area[6];
3549+
uint8_t *ptr = (uint8_t *) &ret_area;
3550+
__wasm_import_fastly_compute_at_edge_http_resp_port_get((int32_t) (h), ptr);
3551+
fastly_compute_at_edge_http_resp_result_option_u16_error_t result;
3552+
switch ((int32_t) *((uint8_t*) (ptr + 0))) {
3553+
case 0: {
3554+
result.is_err = false;
3555+
fastly_world_option_u16_t option;
3556+
switch ((int32_t) *((uint8_t*) (ptr + 2))) {
3557+
case 0: {
3558+
option.is_some = false;
3559+
break;
3560+
}
3561+
case 1: {
3562+
option.is_some = true;
3563+
option.val = (uint16_t) ((int32_t) *((uint16_t*) (ptr + 4)));
3564+
break;
3565+
}
3566+
}
3567+
3568+
result.val.ok = option;
3569+
break;
3570+
}
3571+
case 1: {
3572+
result.is_err = true;
3573+
result.val.err = (int32_t) *((uint8_t*) (ptr + 2));
3574+
break;
3575+
}
3576+
}
3577+
if (!result.is_err) {
3578+
*ret = result.val.ok;
3579+
return 1;
3580+
} else {
3581+
*err = result.val.err;
3582+
return 0;
3583+
}
3584+
}
3585+
35003586
bool fastly_compute_at_edge_http_resp_framing_headers_mode_set(fastly_compute_at_edge_http_resp_response_handle_t h, fastly_compute_at_edge_http_resp_framing_headers_mode_t mode, fastly_compute_at_edge_http_resp_error_t *err) {
35013587
__attribute__((__aligned__(1)))
35023588
uint8_t ret_area[2];

runtime/fastly/host-api/component/fastly_world.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,27 @@ typedef struct {
964964
} val;
965965
} fastly_compute_at_edge_http_resp_result_http_status_error_t;
966966

967+
typedef struct {
968+
bool is_err;
969+
union {
970+
fastly_world_option_list_u8_t ok;
971+
fastly_compute_at_edge_http_resp_error_t err;
972+
} val;
973+
} fastly_compute_at_edge_http_resp_result_option_list_u8_error_t;
974+
975+
typedef struct {
976+
bool is_some;
977+
uint16_t val;
978+
} fastly_world_option_u16_t;
979+
980+
typedef struct {
981+
bool is_err;
982+
union {
983+
fastly_world_option_u16_t ok;
984+
fastly_compute_at_edge_http_resp_error_t err;
985+
} val;
986+
} fastly_compute_at_edge_http_resp_result_option_u16_error_t;
987+
967988
typedef fastly_compute_at_edge_types_error_t fastly_compute_at_edge_log_error_t;
968989

969990
typedef uint32_t fastly_compute_at_edge_log_handle_t;
@@ -1563,6 +1584,14 @@ fastly_compute_at_edge_http_resp_status_set(fastly_compute_at_edge_http_resp_res
15631584
extern bool
15641585
fastly_compute_at_edge_http_resp_close(fastly_compute_at_edge_http_resp_response_handle_t h,
15651586
fastly_compute_at_edge_http_resp_error_t *err);
1587+
extern bool
1588+
fastly_compute_at_edge_http_resp_ip_get(fastly_compute_at_edge_http_resp_response_handle_t h,
1589+
fastly_world_option_list_u8_t *ret,
1590+
fastly_compute_at_edge_http_resp_error_t *err);
1591+
extern bool
1592+
fastly_compute_at_edge_http_resp_port_get(fastly_compute_at_edge_http_resp_response_handle_t h,
1593+
fastly_world_option_u16_t *ret,
1594+
fastly_compute_at_edge_http_resp_error_t *err);
15661595
// Adjust how this response's framing headers are determined.
15671596
extern bool fastly_compute_at_edge_http_resp_framing_headers_mode_set(
15681597
fastly_compute_at_edge_http_resp_response_handle_t h,

runtime/fastly/host-api/component/fastly_world_adapter.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,38 @@ bool fastly_compute_at_edge_http_resp_status_set(
755755
return convert_result(fastly::resp_status_set(h, status), err);
756756
}
757757

758+
bool fastly_compute_at_edge_http_resp_ip_get(fastly_compute_at_edge_http_resp_response_handle_t h,
759+
fastly_world_option_list_u8_t *ret,
760+
fastly_compute_at_edge_http_resp_error_t *err) {
761+
ret->val.ptr = static_cast<uint8_t *>(cabi_malloc(16, 1));
762+
if (!convert_result(fastly::resp_ip_get(h, ret->val.ptr, &ret->val.len), err)) {
763+
if (*err == FASTLY_COMPUTE_AT_EDGE_TYPES_ERROR_OPTIONAL_NONE) {
764+
ret->is_some = false;
765+
return true;
766+
} else {
767+
cabi_free(ret->val.ptr);
768+
return false;
769+
}
770+
}
771+
ret->is_some = true;
772+
return true;
773+
}
774+
775+
bool fastly_compute_at_edge_http_resp_port_get(fastly_compute_at_edge_http_resp_response_handle_t h,
776+
fastly_world_option_u16_t *ret,
777+
fastly_compute_at_edge_http_resp_error_t *err) {
778+
if (!convert_result(fastly::resp_port_get(h, &ret->val), err)) {
779+
if (*err == FASTLY_COMPUTE_AT_EDGE_TYPES_ERROR_OPTIONAL_NONE) {
780+
ret->is_some = false;
781+
return true;
782+
} else {
783+
return false;
784+
}
785+
}
786+
ret->is_some = true;
787+
return true;
788+
}
789+
758790
bool fastly_compute_at_edge_dictionary_open(fastly_world_string_t *name,
759791
fastly_compute_at_edge_dictionary_handle_t *ret,
760792
fastly_compute_at_edge_types_error_t *err) {
Binary file not shown.

0 commit comments

Comments
 (0)