Skip to content

Commit 58ddb20

Browse files
Jake ChampionJakeChampion
authored andcommitted
fix: improve fetch error messages
we are using the new pending-req-wait-v2 function which supercedes pending-req-wait by providing a struct with rich error details (if the send has errored). Instead of always saying "NetworkError when attempting to fetch resource." - we now will say things such as "NetworkError: DNS timeout", "NetworkError: TLS configuration error", or "NetworkError: HTTP response status invalid"
1 parent 3d5e704 commit 58ddb20

File tree

11 files changed

+737
-21
lines changed

11 files changed

+737
-21
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/// <reference path="../../../../../types/index.d.ts" />
2+
import { pass, assertRejects } from "./assertions.js";
3+
import { routes } from "./routes.js";
4+
import { Backend } from "fastly:backend";
5+
import { allowDynamicBackends } from "fastly:experimental";
6+
7+
routes.set('/fetch-errors', async () => {
8+
allowDynamicBackends(true)
9+
let error;
10+
error = await assertRejects(async () => {
11+
await fetch('http://127.0.0.1')
12+
}, DOMException, "Connection refused");
13+
if (error) { return error; }
14+
15+
error = await assertRejects(async () => {
16+
await fetch('https://fastly.com/', {
17+
backend: new Backend({
18+
name: 'b1',
19+
target: "fastly.com:8080",
20+
})
21+
})
22+
}, DOMException, "Connection refused");
23+
24+
if (error) { return error; }
25+
error = await assertRejects(async () => {
26+
await fetch('https://fastly.com', {
27+
backend: new Backend({
28+
name: 'b3',
29+
target: "fastly.com",
30+
useSSL: true,
31+
certificateHostname: 'google.com',
32+
})
33+
})
34+
}, DOMException, "TLS certificate error");
35+
if (error) { return error; }
36+
37+
return pass('ok')
38+
});

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/// <reference path="../../../../../types/index.d.ts" />
12
/* eslint-env serviceworker */
23

34
import { routes } from "./routes.js";
@@ -19,6 +20,7 @@ import "./dynamic-backend.js"
1920
import "./env.js"
2021
import "./fanout.js"
2122
import "./fastly-now.js"
23+
import "./fetch-errors.js"
2224
import "./geoip.js"
2325
import "./include-bytes.js"
2426
import "./kv-store.js"

runtime/js-compute-runtime/core/event_loop.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#include "core/event_loop.h"
22
#include "builtins/native-stream-source.h"
33
#include "builtins/request-response.h"
4+
#include "builtins/shared/dom-exception.h"
45
#include "host_interface/host_api.h"
5-
66
#include <chrono>
77
#include <list>
88
#include <memory>
@@ -146,7 +146,8 @@ bool process_pending_request(JSContext *cx, JS::HandleObject request,
146146

147147
auto res = pending.wait();
148148
if (auto *err = res.to_err()) {
149-
JS_ReportErrorUTF8(cx, "NetworkError when attempting to fetch resource.");
149+
std::string message = std::move(err->message()).value_or("when attempting to fetch resource.");
150+
builtins::DOMException::raise(cx, message, "NetworkError");
150151
return RejectPromiseWithPendingError(cx, response_promise);
151152
}
152153

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

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ typedef struct {
159159
} val;
160160
} fastly_world_result_tuple2_u32_fastly_compute_at_edge_http_req_response_fastly_compute_at_edge_http_req_error_t;
161161

162+
typedef struct {
163+
bool is_err;
164+
union {
165+
fastly_world_tuple2_u32_fastly_compute_at_edge_http_req_response_t ok;
166+
fastly_compute_at_edge_http_req_send_error_detail_t err;
167+
} val;
168+
} fastly_world_result_tuple2_u32_fastly_compute_at_edge_http_req_response_fastly_compute_at_edge_http_req_send_error_detail_t;
169+
162170
typedef struct {
163171
bool is_err;
164172
union {
@@ -634,6 +642,9 @@ void __wasm_import_fastly_compute_at_edge_http_req_version_set(int32_t, int32_t,
634642
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("send")))
635643
void __wasm_import_fastly_compute_at_edge_http_req_send(int32_t, int32_t, int32_t, int32_t, int32_t);
636644

645+
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("send-v2")))
646+
void __wasm_import_fastly_compute_at_edge_http_req_send_v2(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
647+
637648
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("send-async")))
638649
void __wasm_import_fastly_compute_at_edge_http_req_send_async(int32_t, int32_t, int32_t, int32_t, int32_t);
639650

@@ -643,12 +654,21 @@ void __wasm_import_fastly_compute_at_edge_http_req_send_async_streaming(int32_t,
643654
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("pending-req-poll")))
644655
void __wasm_import_fastly_compute_at_edge_http_req_pending_req_poll(int32_t, int32_t);
645656

657+
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("pending-req-poll-v2")))
658+
void __wasm_import_fastly_compute_at_edge_http_req_pending_req_poll_v2(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
659+
646660
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("pending-req-wait")))
647661
void __wasm_import_fastly_compute_at_edge_http_req_pending_req_wait(int32_t, int32_t);
648662

663+
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("pending-req-wait-v2")))
664+
void __wasm_import_fastly_compute_at_edge_http_req_pending_req_wait_v2(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t);
665+
649666
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("pending-req-select")))
650667
void __wasm_import_fastly_compute_at_edge_http_req_pending_req_select(int32_t, int32_t, int32_t);
651668

669+
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("pending-req-select-v2")))
670+
void __wasm_import_fastly_compute_at_edge_http_req_pending_req_select_v2(int32_t, int32_t, int32_t);
671+
652672
__attribute__((__import_module__("fastly:compute-at-edge/http-req"), __import_name__("close")))
653673
void __wasm_import_fastly_compute_at_edge_http_req_close(int32_t, int32_t);
654674

@@ -759,7 +779,6 @@ void __wasm_import_fastly_compute_at_edge_uap_parse(int32_t, int32_t, int32_t);
759779

760780
__attribute__((__weak__, __export_name__("cabi_realloc")))
761781
void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
762-
(void) old_size;
763782
if (new_size == 0) return (void*) align;
764783
void *ret = realloc(ptr, new_size);
765784
if (!ret) abort();
@@ -2606,6 +2625,36 @@ bool fastly_compute_at_edge_http_req_send(fastly_compute_at_edge_http_req_reques
26062625
}
26072626
}
26082627

2628+
bool fastly_compute_at_edge_http_req_send_v2(fastly_compute_at_edge_http_req_request_handle_t h, fastly_compute_at_edge_http_req_send_error_detail_t *s, fastly_compute_at_edge_http_req_body_handle_t b, fastly_world_string_t *backend, fastly_compute_at_edge_http_req_response_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
2629+
__attribute__((__aligned__(4)))
2630+
uint8_t ret_area[12];
2631+
int32_t ptr = (int32_t) &ret_area;
2632+
__wasm_import_fastly_compute_at_edge_http_req_send_v2((int32_t) (h), (int32_t) (*s).tag, (*s).mask, (int32_t) ((*s).dns_error_rcode), (int32_t) ((*s).dns_error_info_code), (int32_t) ((*s).tls_alert_id), (int32_t) (b), (int32_t) (*backend).ptr, (int32_t) (*backend).len, ptr);
2633+
fastly_world_result_fastly_compute_at_edge_http_req_response_fastly_compute_at_edge_http_req_error_t result;
2634+
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
2635+
case 0: {
2636+
result.is_err = false;
2637+
result.val.ok = (fastly_compute_at_edge_http_types_response_t) {
2638+
(uint32_t) (*((int32_t*) (ptr + 4))),
2639+
(uint32_t) (*((int32_t*) (ptr + 8))),
2640+
};
2641+
break;
2642+
}
2643+
case 1: {
2644+
result.is_err = true;
2645+
result.val.err = (int32_t) (*((uint8_t*) (ptr + 4)));
2646+
break;
2647+
}
2648+
}
2649+
if (!result.is_err) {
2650+
*ret = result.val.ok;
2651+
return 1;
2652+
} else {
2653+
*err = result.val.err;
2654+
return 0;
2655+
}
2656+
}
2657+
26092658
bool fastly_compute_at_edge_http_req_send_async(fastly_compute_at_edge_http_req_request_handle_t h, fastly_compute_at_edge_http_req_body_handle_t b, fastly_world_string_t *backend, fastly_compute_at_edge_http_req_pending_request_handle_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
26102659
__attribute__((__aligned__(4)))
26112660
uint8_t ret_area[8];
@@ -2703,6 +2752,49 @@ bool fastly_compute_at_edge_http_req_pending_req_poll(fastly_compute_at_edge_htt
27032752
}
27042753
}
27052754

2755+
bool fastly_compute_at_edge_http_req_pending_req_poll_v2(fastly_compute_at_edge_http_req_pending_request_handle_t h, fastly_compute_at_edge_http_req_send_error_detail_t *s, fastly_world_option_fastly_compute_at_edge_http_req_response_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
2756+
__attribute__((__aligned__(4)))
2757+
uint8_t ret_area[16];
2758+
int32_t ptr = (int32_t) &ret_area;
2759+
__wasm_import_fastly_compute_at_edge_http_req_pending_req_poll_v2((int32_t) (h), (int32_t) (*s).tag, (*s).mask, (int32_t) ((*s).dns_error_rcode), (int32_t) ((*s).dns_error_info_code), (int32_t) ((*s).tls_alert_id), ptr);
2760+
fastly_world_result_option_fastly_compute_at_edge_http_req_response_fastly_compute_at_edge_http_req_error_t result;
2761+
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
2762+
case 0: {
2763+
result.is_err = false;
2764+
fastly_world_option_fastly_compute_at_edge_http_req_response_t option;
2765+
switch ((int32_t) (*((uint8_t*) (ptr + 4)))) {
2766+
case 0: {
2767+
option.is_some = false;
2768+
break;
2769+
}
2770+
case 1: {
2771+
option.is_some = true;
2772+
option.val = (fastly_compute_at_edge_http_types_response_t) {
2773+
(uint32_t) (*((int32_t*) (ptr + 8))),
2774+
(uint32_t) (*((int32_t*) (ptr + 12))),
2775+
};
2776+
break;
2777+
}
2778+
}
2779+
2780+
result.val.ok = option;
2781+
break;
2782+
}
2783+
case 1: {
2784+
result.is_err = true;
2785+
result.val.err = (int32_t) (*((uint8_t*) (ptr + 4)));
2786+
break;
2787+
}
2788+
}
2789+
if (!result.is_err) {
2790+
*ret = result.val.ok;
2791+
return 1;
2792+
} else {
2793+
*err = result.val.err;
2794+
return 0;
2795+
}
2796+
}
2797+
27062798
bool fastly_compute_at_edge_http_req_pending_req_wait(fastly_compute_at_edge_http_req_pending_request_handle_t h, fastly_compute_at_edge_http_req_response_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
27072799
__attribute__((__aligned__(4)))
27082800
uint8_t ret_area[12];
@@ -2733,6 +2825,36 @@ bool fastly_compute_at_edge_http_req_pending_req_wait(fastly_compute_at_edge_htt
27332825
}
27342826
}
27352827

2828+
bool fastly_compute_at_edge_http_req_pending_req_wait_v2(fastly_compute_at_edge_http_req_pending_request_handle_t h, fastly_compute_at_edge_http_req_send_error_detail_t *s, fastly_compute_at_edge_http_req_response_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
2829+
__attribute__((__aligned__(4)))
2830+
uint8_t ret_area[12];
2831+
int32_t ptr = (int32_t) &ret_area;
2832+
__wasm_import_fastly_compute_at_edge_http_req_pending_req_wait_v2((int32_t) (h), (int32_t) (*s).tag, (*s).mask, (int32_t) ((*s).dns_error_rcode), (int32_t) ((*s).dns_error_info_code), (int32_t) ((*s).tls_alert_id), ptr);
2833+
fastly_world_result_fastly_compute_at_edge_http_req_response_fastly_compute_at_edge_http_req_error_t result;
2834+
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
2835+
case 0: {
2836+
result.is_err = false;
2837+
result.val.ok = (fastly_compute_at_edge_http_types_response_t) {
2838+
(uint32_t) (*((int32_t*) (ptr + 4))),
2839+
(uint32_t) (*((int32_t*) (ptr + 8))),
2840+
};
2841+
break;
2842+
}
2843+
case 1: {
2844+
result.is_err = true;
2845+
result.val.err = (int32_t) (*((uint8_t*) (ptr + 4)));
2846+
break;
2847+
}
2848+
}
2849+
if (!result.is_err) {
2850+
*ret = result.val.ok;
2851+
return 1;
2852+
} else {
2853+
*err = result.val.err;
2854+
return 0;
2855+
}
2856+
}
2857+
27362858
bool fastly_compute_at_edge_http_req_pending_req_select(fastly_world_list_fastly_compute_at_edge_http_req_pending_request_handle_t *h, fastly_world_tuple2_u32_fastly_compute_at_edge_http_req_response_t *ret, fastly_compute_at_edge_http_req_error_t *err) {
27372859
__attribute__((__aligned__(4)))
27382860
uint8_t ret_area[16];
@@ -2766,6 +2888,45 @@ bool fastly_compute_at_edge_http_req_pending_req_select(fastly_world_list_fastly
27662888
}
27672889
}
27682890

2891+
bool fastly_compute_at_edge_http_req_pending_req_select_v2(fastly_world_list_fastly_compute_at_edge_http_req_pending_request_handle_t *h, fastly_world_tuple2_u32_fastly_compute_at_edge_http_req_response_t *ret, fastly_compute_at_edge_http_req_send_error_detail_t *err) {
2892+
__attribute__((__aligned__(4)))
2893+
uint8_t ret_area[16];
2894+
int32_t ptr = (int32_t) &ret_area;
2895+
__wasm_import_fastly_compute_at_edge_http_req_pending_req_select_v2((int32_t) (*h).ptr, (int32_t) (*h).len, ptr);
2896+
fastly_world_result_tuple2_u32_fastly_compute_at_edge_http_req_response_fastly_compute_at_edge_http_req_send_error_detail_t result;
2897+
switch ((int32_t) (*((uint8_t*) (ptr + 0)))) {
2898+
case 0: {
2899+
result.is_err = false;
2900+
result.val.ok = (fastly_world_tuple2_u32_fastly_compute_at_edge_http_req_response_t) {
2901+
(uint32_t) (*((int32_t*) (ptr + 4))),
2902+
(fastly_compute_at_edge_http_types_response_t) {
2903+
(uint32_t) (*((int32_t*) (ptr + 8))),
2904+
(uint32_t) (*((int32_t*) (ptr + 12))),
2905+
},
2906+
};
2907+
break;
2908+
}
2909+
case 1: {
2910+
result.is_err = true;
2911+
result.val.err = (fastly_compute_at_edge_http_types_send_error_detail_t) {
2912+
(int32_t) (*((uint8_t*) (ptr + 4))),
2913+
(int32_t) (*((uint8_t*) (ptr + 5))),
2914+
(uint16_t) ((int32_t) (*((uint16_t*) (ptr + 6)))),
2915+
(uint16_t) ((int32_t) (*((uint16_t*) (ptr + 8)))),
2916+
(uint8_t) ((int32_t) (*((uint8_t*) (ptr + 10)))),
2917+
};
2918+
break;
2919+
}
2920+
}
2921+
if (!result.is_err) {
2922+
*ret = result.val.ok;
2923+
return 1;
2924+
} else {
2925+
*err = result.val.err;
2926+
return 0;
2927+
}
2928+
}
2929+
27692930
bool fastly_compute_at_edge_http_req_close(fastly_compute_at_edge_http_req_request_handle_t h, fastly_compute_at_edge_http_req_error_t *err) {
27702931
__attribute__((__aligned__(1)))
27712932
uint8_t ret_area[2];

0 commit comments

Comments
 (0)