]> git.kaiwu.me - njs.git/commitdiff
Fetch: fixed exception classes
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 3 Jun 2026 02:05:19 +0000 (19:05 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Wed, 3 Jun 2026 21:34:17 +0000 (14:34 -0700)
Report API misuse in Fetch, Request, Response, and Headers as TypeError, and
report status bounds violations as RangeError.  Keep internal host failures as
InternalError and preserve conversion helper exceptions where they already
provide the real cause.

nginx/ngx_js_fetch.c
nginx/ngx_qjs_fetch.c
nginx/t/js_fetch_objects.t

index b1bf55eca854c15924c3fce1fa0a5646bf39ac25..145617f0ccec6b9a6a05022c169f8a4d86a22bdc 100644 (file)
@@ -540,7 +540,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     if (u.host.len >= NGX_JS_HOST_MAX_LEN) {
-        njs_vm_error(vm, "Host name too long");
+        njs_vm_type_error(vm, "Host name too long");
         goto fail;
     }
 
@@ -608,7 +608,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
                                            &http->proxy.url, &http->proxy.auth)
                 != NGX_OK)
             {
-                njs_vm_error(vm, "failed to evaluate proxy URL");
+                njs_vm_internal_error(vm, "failed to evaluate proxy URL");
                 goto fail;
             }
 
@@ -628,7 +628,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     if (ngx_js_check_request_line_component(u.uri.data, u.uri.len) != NGX_OK) {
-        njs_vm_error(vm, "invalid url");
+        njs_vm_type_error(vm, "invalid url");
         goto fail;
     }
 
@@ -649,7 +649,7 @@ ngx_js_ext_fetch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
 
         if (ctx == NGX_NO_RESOLVER) {
-            njs_vm_error(vm, "no resolver defined");
+            njs_vm_internal_error(vm, "no resolver defined");
             goto fail;
         }
 
@@ -801,13 +801,12 @@ ngx_js_ext_response_constructor(njs_vm_t *vm, njs_value_t *args,
         value = njs_vm_object_prop(vm, init, &status, &lvalue);
         if (value != NULL) {
             if (ngx_js_integer(vm, value, &response->code) != NGX_OK) {
-                njs_vm_error(vm, "invalid Response status");
                 return NJS_ERROR;
             }
 
             if (response->code < 200 || response->code > 599) {
-                njs_vm_error(vm, "status provided (%i) is outside of "
-                             "[200, 599] range", response->code);
+                njs_vm_range_error(vm, "status provided (%i) is outside of "
+                                   "[200, 599] range", response->code);
                 return NJS_ERROR;
             }
         }
@@ -815,7 +814,6 @@ ngx_js_ext_response_constructor(njs_vm_t *vm, njs_value_t *args,
         value = njs_vm_object_prop(vm, init, &status_text, &lvalue);
         if (value != NULL) {
             if (ngx_js_ngx_string(vm, value, &response->status_text) != NGX_OK) {
-                njs_vm_error(vm, "invalid Response statusText");
                 return NJS_ERROR;
             }
 
@@ -824,7 +822,7 @@ ngx_js_ext_response_constructor(njs_vm_t *vm, njs_value_t *args,
 
             while (p < end) {
                 if (*p != '\t' && *p < ' ') {
-                    njs_vm_error(vm, "invalid Response statusText");
+                    njs_vm_type_error(vm, "invalid Response statusText");
                     return NJS_ERROR;
                 }
 
@@ -835,7 +833,7 @@ ngx_js_ext_response_constructor(njs_vm_t *vm, njs_value_t *args,
         value = njs_vm_object_prop(vm, init, &headers, &lvalue);
         if (value != NULL) {
             if (!njs_value_is_object(value)) {
-                njs_vm_error(vm, "Headers is not an object");
+                njs_vm_type_error(vm, "Headers is not an object");
                 return NJS_ERROR;
             }
 
@@ -852,7 +850,6 @@ ngx_js_ext_response_constructor(njs_vm_t *vm, njs_value_t *args,
 
     if (!njs_value_is_null_or_undefined(body)) {
         if (ngx_js_string(vm, body, &bd) != NGX_OK) {
-            njs_vm_error(vm, "invalid Response body");
             return NJS_ERROR;
         }
 
@@ -908,13 +905,13 @@ ngx_js_method_process(njs_vm_t *vm, ngx_js_request_t *request)
                                                request->method.len)
            != NGX_OK)
     {
-        njs_vm_error(vm, "invalid Request method");
+        njs_vm_type_error(vm, "invalid Request method");
         return NJS_ERROR;
     }
 
     for (m = &forbidden[0]; m->length != 0; m++) {
         if (njs_strstr_case_eq(&str, m)) {
-            njs_vm_error(vm, "forbidden method: %V", m);
+            njs_vm_type_error(vm, "forbidden method: %V", m);
             return NJS_ERROR;
         }
     }
@@ -1012,7 +1009,8 @@ ngx_js_headers_fill(njs_vm_t *vm, ngx_js_headers_t *headers, njs_value_t *init)
             start++;
 
             if (len != 2) {
-                njs_vm_error(vm, "header does not contain exactly two items");
+                njs_vm_type_error(vm,
+                                  "header does not contain exactly two items");
                 return NJS_ERROR;
             }
 
@@ -1143,7 +1141,7 @@ ngx_js_fetch_alloc(njs_vm_t *vm, ngx_pool_t *pool, ngx_log_t *log,
 
 failed:
 
-    njs_vm_error(vm, "internal error");
+    njs_vm_internal_error(vm, "internal error");
 
     return NULL;
 }
@@ -1220,7 +1218,7 @@ ngx_js_fetch_promissified_result(njs_vm_t *vm, njs_value_t *result,
 
 error:
 
-    njs_vm_error(vm, "internal error");
+    njs_vm_internal_error(vm, "internal error");
 
     return NJS_ERROR;
 }
@@ -1299,7 +1297,7 @@ ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
 
     input = njs_arg(args, nargs, 1);
     if (njs_value_is_undefined(input)) {
-        njs_vm_error(vm, "1st argument is required");
+        njs_vm_type_error(vm, "1st argument is required");
         return NJS_ERROR;
     }
 
@@ -1334,14 +1332,13 @@ ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
     if (njs_value_is_string(input)) {
         ret = ngx_js_ngx_string(vm, input, &request->url);
         if (ret != NJS_OK) {
-            njs_vm_error(vm, "failed to convert url arg");
             return NJS_ERROR;
         }
 
     } else {
         orig = njs_vm_external(vm, ngx_http_js_fetch_request_proto_id, input);
         if (orig == NULL) {
-            njs_vm_error(vm, "input is not string or a Request object");
+            njs_vm_type_error(vm, "input is not string or a Request object");
             return NJS_ERROR;
         }
 
@@ -1385,13 +1382,13 @@ ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
 #endif
 
     } else {
-        njs_vm_error(vm, "unsupported URL schema (only http or https are"
-                     " supported)");
+        njs_vm_type_error(vm, "unsupported URL schema (only http or https are"
+                          " supported)");
         return NJS_ERROR;
     }
 
     if (ngx_parse_url(pool, u) != NGX_OK) {
-        njs_vm_error(vm, "invalid url");
+        njs_vm_type_error(vm, "invalid url");
         return NJS_ERROR;
     }
 
@@ -1402,7 +1399,6 @@ ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
         if (value != NULL && ngx_js_ngx_string(vm, value, &request->method)
             != NGX_OK)
         {
-            njs_vm_error(vm, "invalid Request method");
             return NJS_ERROR;
         }
 
@@ -1446,7 +1442,7 @@ ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
         headers = njs_vm_object_prop(vm, init, &headers_key, &lvalue);
         if (headers != NULL) {
             if (!njs_value_is_object(headers)) {
-                njs_vm_error(vm, "Headers is not an object");
+                njs_vm_type_error(vm, "Headers is not an object");
                 return NJS_ERROR;
             }
 
@@ -1474,7 +1470,6 @@ ngx_js_request_constructor(njs_vm_t *vm, ngx_js_request_t *request,
         value = njs_vm_object_prop(vm, init, &body_key, &lvalue);
         if (value != NULL) {
             if (ngx_js_ngx_string(vm, value, &request->body) != NGX_OK) {
-                njs_vm_error(vm, "invalid Request body");
                 return NJS_ERROR;
             }
 
@@ -1543,18 +1538,18 @@ ngx_js_headers_append(njs_vm_t *vm, ngx_js_headers_t *headers,
 
     ret = ngx_js_check_header_name(name, len);
     if (ret != NGX_OK) {
-        njs_vm_error(vm, "invalid header name");
+        njs_vm_type_error(vm, "invalid header name");
         return NJS_ERROR;
     }
 
     ret = ngx_js_check_header_value(value, vlen);
     if (ret != NGX_OK) {
-        njs_vm_error(vm, "invalid header value");
+        njs_vm_type_error(vm, "invalid header value");
         return NJS_ERROR;
     }
 
     if (headers->guard == GUARD_IMMUTABLE) {
-        njs_vm_error(vm, "cannot append to immutable object");
+        njs_vm_type_error(vm, "cannot append to immutable object");
         return NJS_ERROR;
     }
 
@@ -1724,7 +1719,7 @@ ngx_headers_js_ext_append(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id,
                               njs_argument(args, 0));
     if (headers == NULL) {
-        njs_vm_error(vm, "\"this\" is not fetch headers object");
+        njs_vm_type_error(vm, "\"this\" is not fetch headers object");
         return NJS_ERROR;
     }
 
@@ -1764,7 +1759,7 @@ ngx_headers_js_ext_delete(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id,
                               njs_argument(args, 0));
     if (headers == NULL) {
-        njs_vm_error(vm, "\"this\" is not fetch headers object");
+        njs_vm_type_error(vm, "\"this\" is not fetch headers object");
         return NJS_ERROR;
     }
 
@@ -1827,14 +1822,14 @@ ngx_headers_js_ext_for_each(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id, this);
     if (headers == NULL) {
-        njs_vm_error(vm, "\"this\" is not fetch headers object");
+        njs_vm_type_error(vm, "\"this\" is not fetch headers object");
         return NJS_ERROR;
     }
 
     callback = njs_arg(args, nargs, 1);
 
     if (!njs_value_is_function(callback)) {
-        njs_vm_error(vm, "\"callback\" is not a function");
+        njs_vm_type_error(vm, "\"callback\" is not a function");
         return NJS_ERROR;
     }
 
@@ -2064,7 +2059,7 @@ ngx_headers_js_ext_set(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     headers = njs_vm_external(vm, ngx_http_js_fetch_headers_proto_id,
                               njs_argument(args, 0));
     if (headers == NULL) {
-        njs_vm_error(vm, "\"this\" is not fetch headers object");
+        njs_vm_type_error(vm, "\"this\" is not fetch headers object");
         return NJS_ERROR;
     }
 
@@ -2145,7 +2140,7 @@ ngx_request_js_ext_body(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     if (request->body_used) {
-        njs_vm_error(vm, "body stream already read");
+        njs_vm_type_error(vm, "body stream already read");
         return NJS_ERROR;
     }
 
@@ -2258,7 +2253,7 @@ ngx_request_js_ext_headers(njs_vm_t *vm, njs_object_prop_t *prop,
                                      ngx_http_js_fetch_headers_proto_id,
                                      &request->headers, 0);
         if (ret != NJS_OK) {
-            njs_vm_error(vm, "fetch header creation failed");
+            njs_vm_memory_error(vm);
             return NJS_ERROR;
         }
     }
@@ -2303,7 +2298,7 @@ ngx_response_js_ext_body(njs_vm_t *vm, njs_value_t *args,
     }
 
     if (response->body_used) {
-        njs_vm_error(vm, "body stream already read");
+        njs_vm_type_error(vm, "body stream already read");
         return NJS_ERROR;
     }
 
@@ -2385,7 +2380,7 @@ ngx_response_js_ext_headers(njs_vm_t *vm, njs_object_prop_t *prop,
                                      ngx_http_js_fetch_headers_proto_id,
                                      &response->headers, 0);
         if (ret != NJS_OK) {
-            njs_vm_error(vm, "fetch header creation failed");
+            njs_vm_memory_error(vm);
             return NJS_ERROR;
         }
     }
@@ -2509,7 +2504,7 @@ ngx_fetch_flag_set(njs_vm_t *vm, const ngx_js_entry_t *entries,
         }
     }
 
-    njs_vm_error(vm, "unknown %s type: %V", type, &flag);
+    njs_vm_type_error(vm, "unknown %s type: %V", type, &flag);
 
     return NJS_ERROR;
 }
index 162531e5a610cf3f853c103f21dd12dcfe10fa28..4aa8f54f119bca6487a54ec839345ee6b59c3111 100644 (file)
@@ -260,7 +260,7 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
     }
 
     if (u.host.len >= NGX_JS_HOST_MAX_LEN) {
-        JS_ThrowInternalError(cx, "Host name too long");
+        JS_ThrowTypeError(cx, "Host name too long");
         goto fail;
     }
 
@@ -365,7 +365,7 @@ ngx_qjs_ext_fetch(JSContext *cx, JSValueConst this_val, int argc,
     }
 
     if (ngx_js_check_request_line_component(u.uri.data, u.uri.len) != NGX_OK) {
-        JS_ThrowInternalError(cx, "invalid url");
+        JS_ThrowTypeError(cx, "invalid url");
         goto fail;
     }
 
@@ -522,7 +522,7 @@ ngx_qjs_request_ctor(JSContext *cx, ngx_js_request_t *request,
 
     input = argv[0];
     if (JS_IsUndefined(input)) {
-        JS_ThrowInternalError(cx, "1st argument is required");
+        JS_ThrowTypeError(cx, "1st argument is required");
         return NGX_ERROR;
     }
 
@@ -568,8 +568,8 @@ ngx_qjs_request_ctor(JSContext *cx, ngx_js_request_t *request,
     } else {
         orig = JS_GetOpaque(input, NGX_QJS_CLASS_ID_FETCH_REQUEST);
         if (orig == NULL) {
-            JS_ThrowInternalError(cx,
-                                  "input is not string or a Request object");
+            JS_ThrowTypeError(cx,
+                              "input is not string or a Request object");
             return NGX_ERROR;
         }
 
@@ -612,13 +612,13 @@ ngx_qjs_request_ctor(JSContext *cx, ngx_js_request_t *request,
 #endif
 
     } else {
-        JS_ThrowInternalError(cx, "unsupported URL schema (only http or https"
-                                  " are supported)");
+        JS_ThrowTypeError(cx, "unsupported URL schema (only http or https"
+                              " are supported)");
         return NGX_ERROR;
     }
 
     if (ngx_parse_url(pool, u) != NGX_OK) {
-        JS_ThrowInternalError(cx, "invalid url");
+        JS_ThrowTypeError(cx, "invalid url");
         return NGX_ERROR;
     }
 
@@ -626,7 +626,6 @@ ngx_qjs_request_ctor(JSContext *cx, ngx_js_request_t *request,
         init = argv[1];
         value = JS_GetPropertyStr(cx, init, "method");
         if (JS_IsException(value)) {
-            JS_ThrowInternalError(cx, "invalid Request method");
             return NGX_ERROR;
         }
 
@@ -673,14 +672,13 @@ ngx_qjs_request_ctor(JSContext *cx, ngx_js_request_t *request,
 
         value = JS_GetPropertyStr(cx, init, "headers");
         if (JS_IsException(value)) {
-            JS_ThrowInternalError(cx, "invalid Request headers");
             return NGX_ERROR;
         }
 
         if (!JS_IsUndefined(value)) {
             if (!JS_IsObject(value)) {
                 JS_FreeValue(cx, value);
-                JS_ThrowInternalError(cx, "Headers is not an object");
+                JS_ThrowTypeError(cx, "Headers is not an object");
                 return NGX_ERROR;
             }
 
@@ -710,7 +708,6 @@ ngx_qjs_request_ctor(JSContext *cx, ngx_js_request_t *request,
 
         value = JS_GetPropertyStr(cx, init, "body");
         if (JS_IsException(value)) {
-            JS_ThrowInternalError(cx, "invalid Request body");
             return NGX_ERROR;
         }
 
@@ -788,7 +785,7 @@ ngx_qjs_fetch_response_ctor(JSContext *cx, JSValueConst new_target, int argc,
     if (JS_IsObject(init)) {
         value = JS_GetPropertyStr(cx, init, "status");
         if (JS_IsException(value)) {
-            return JS_ThrowInternalError(cx, "invalid Response status");
+            return JS_EXCEPTION;
         }
 
         if (!JS_IsUndefined(value)) {
@@ -800,15 +797,15 @@ ngx_qjs_fetch_response_ctor(JSContext *cx, JSValueConst new_target, int argc,
             }
 
             if (response->code < 200 || response->code > 599) {
-                return JS_ThrowInternalError(cx, "status provided (%d) is "
-                                                 "outside of [200, 599] range",
-                                             (int) response->code);
+                return JS_ThrowRangeError(cx, "status provided (%d) is "
+                                              "outside of [200, 599] range",
+                                          (int) response->code);
             }
         }
 
         value = JS_GetPropertyStr(cx, init, "statusText");
         if (JS_IsException(value)) {
-            return JS_ThrowInternalError(cx, "invalid Response statusText");
+            return JS_EXCEPTION;
         }
 
         if (!JS_IsUndefined(value)) {
@@ -828,8 +825,8 @@ ngx_qjs_fetch_response_ctor(JSContext *cx, JSValueConst new_target, int argc,
 
             while (p < end) {
                 if (*p != '\t' && *p < ' ') {
-                    return JS_ThrowInternalError(cx,
-                                                 "invalid Response statusText");
+                    return JS_ThrowTypeError(cx,
+                                             "invalid Response statusText");
                 }
 
                 p++;
@@ -838,13 +835,13 @@ ngx_qjs_fetch_response_ctor(JSContext *cx, JSValueConst new_target, int argc,
 
         value = JS_GetPropertyStr(cx, init, "headers");
         if (JS_IsException(value)) {
-            return JS_ThrowInternalError(cx, "invalid Response headers");
+            return JS_EXCEPTION;
         }
 
         if (!JS_IsUndefined(value)) {
             if (!JS_IsObject(value)) {
                 JS_FreeValue(cx, value);
-                return JS_ThrowInternalError(cx, "Headers is not an object");
+                return JS_ThrowTypeError(cx, "Headers is not an object");
             }
 
             rc = ngx_qjs_headers_fill(cx, &response->headers, value);
@@ -962,7 +959,7 @@ ngx_qjs_method_process(JSContext *cx, ngx_js_request_t *request)
                                                request->method.len)
            != NGX_OK)
     {
-        JS_ThrowInternalError(cx, "invalid Request method");
+        JS_ThrowTypeError(cx, "invalid Request method");
         return NGX_ERROR;
     }
 
@@ -970,8 +967,8 @@ ngx_qjs_method_process(JSContext *cx, ngx_js_request_t *request)
         if (request->method.len == m->len
             && ngx_strncasecmp(request->method.data, m->data, m->len) == 0)
         {
-            JS_ThrowInternalError(cx, "forbidden method: %.*s",
-                                  (int) m->len, m->data);
+            JS_ThrowTypeError(cx, "forbidden method: %.*s",
+                              (int) m->len, m->data);
             return NGX_ERROR;
         }
     }
@@ -1109,8 +1106,8 @@ ngx_qjs_headers_fill(JSContext *cx, ngx_js_headers_t *headers, JSValue init)
 
             if (length != 2) {
                 JS_FreeValue(cx, header);
-                JS_ThrowInternalError(cx,
-                                   "header does not contain exactly two items");
+                JS_ThrowTypeError(cx,
+                                  "header does not contain exactly two items");
                 goto fail;
             }
 
@@ -1368,18 +1365,18 @@ ngx_qjs_headers_append(JSContext *cx, ngx_js_headers_t *headers,
 
     ret = ngx_js_check_header_name(name, len);
     if (ret != NGX_OK) {
-        JS_ThrowInternalError(cx, "invalid header name");
+        JS_ThrowTypeError(cx, "invalid header name");
         return NGX_ERROR;
     }
 
     ret = ngx_js_check_header_value(value, vlen);
     if (ret != NGX_OK) {
-        JS_ThrowInternalError(cx, "invalid header value");
+        JS_ThrowTypeError(cx, "invalid header value");
         return NGX_ERROR;
     }
 
     if (headers->guard == GUARD_IMMUTABLE) {
-        JS_ThrowInternalError(cx, "cannot append to immutable object");
+        JS_ThrowTypeError(cx, "cannot append to immutable object");
         return NGX_ERROR;
     }
 
@@ -1699,9 +1696,9 @@ ngx_qjs_fetch_headers_own_property_names(JSContext *cx, JSPropertyEnum **ptab,
     ngx_js_tb_elt_t   *h;
     ngx_js_headers_t  *headers;
 
-    headers = JS_GetOpaque2(cx, obj, NGX_QJS_CLASS_ID_FETCH_HEADERS);
+    headers = JS_GetOpaque(obj, NGX_QJS_CLASS_ID_FETCH_HEADERS);
     if (headers == NULL) {
-        (void) JS_ThrowInternalError(cx, "\"this\" is not a Headers object");
+        (void) JS_ThrowTypeError(cx, "\"this\" is not a Headers object");
         return -1;
     }
 
@@ -1766,10 +1763,10 @@ ngx_qjs_ext_fetch_headers_append(JSContext *cx, JSValueConst this_val,
     ngx_int_t          rc;
     ngx_js_headers_t  *headers;
 
-    headers = JS_GetOpaque2(cx, this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
+    headers = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
     if (headers == NULL) {
-        return JS_ThrowInternalError(cx,
-                                     "\"this\" is not fetch headers object");
+        return JS_ThrowTypeError(cx,
+                                 "\"this\" is not fetch headers object");
     }
 
     rc = ngx_qjs_headers_fill_header_free(cx, headers,
@@ -1793,10 +1790,10 @@ ngx_qjs_ext_fetch_headers_delete(JSContext *cx, JSValueConst this_val,
     ngx_js_tb_elt_t   *h;
     ngx_js_headers_t  *headers;
 
-    headers = JS_GetOpaque2(cx, this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
+    headers = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
     if (headers == NULL) {
-        return JS_ThrowInternalError(cx,
-                                     "\"this\" is not fetch headers object");
+        return JS_ThrowTypeError(cx,
+                                 "\"this\" is not fetch headers object");
     }
 
     name.data = (u_char *) JS_ToCStringLen(cx, &name.len, argv[0]);
@@ -1854,16 +1851,16 @@ ngx_qjs_ext_fetch_headers_foreach(JSContext *cx, JSValueConst this_val,
     ngx_uint_t         i;
     ngx_js_headers_t  *headers;
 
-    headers = JS_GetOpaque2(cx, this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
+    headers = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
     if (headers == NULL) {
-        return JS_ThrowInternalError(cx,
-                                     "\"this\" is not fetch headers object");
+        return JS_ThrowTypeError(cx,
+                                 "\"this\" is not fetch headers object");
     }
 
     callback = argv[0];
 
     if (!JS_IsFunction(cx, callback)) {
-        return JS_ThrowInternalError(cx, "\"callback\" is not a function");
+        return JS_ThrowTypeError(cx, "\"callback\" is not a function");
     }
 
     keys = ngx_qjs_headers_ext_keys(cx, this_val);
@@ -1974,10 +1971,10 @@ ngx_qjs_ext_fetch_headers_set(JSContext *cx, JSValueConst this_val,
     ngx_js_tb_elt_t   *h, **ph, **pp;
     ngx_js_headers_t  *headers;
 
-    headers = JS_GetOpaque2(cx, this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
+    headers = JS_GetOpaque(this_val, NGX_QJS_CLASS_ID_FETCH_HEADERS);
     if (headers == NULL) {
-        return JS_ThrowInternalError(cx,
-                                     "\"this\" is not fetch headers object");
+        return JS_ThrowTypeError(cx,
+                                 "\"this\" is not fetch headers object");
     }
 
     name.data = (u_char *) JS_ToCStringLen(cx, &name.len, argv[0]);
@@ -2057,7 +2054,7 @@ ngx_qjs_ext_fetch_request_body(JSContext *cx, JSValueConst this_val,
     }
 
     if (request->body_used) {
-        return JS_ThrowInternalError(cx, "body stream already read");
+        return JS_ThrowTypeError(cx, "body stream already read");
     }
 
     request->body_used = 1;
@@ -2171,7 +2168,7 @@ ngx_qjs_ext_fetch_request_headers(JSContext *cx, JSValueConst this_val)
     if (JS_IsUndefined(header)) {
         header = JS_NewObjectClass(cx, NGX_QJS_CLASS_ID_FETCH_HEADERS);
         if (JS_IsException(header)) {
-            return JS_ThrowInternalError(cx, "fetch header creation failed");
+            return JS_ThrowOutOfMemory(cx);
         }
 
         JS_SetOpaque(header, &request->headers);
@@ -2298,7 +2295,7 @@ ngx_qjs_ext_fetch_response_headers(JSContext *cx, JSValueConst this_val)
     if (JS_IsUndefined(header)) {
         header = JS_NewObjectClass(cx, NGX_QJS_CLASS_ID_FETCH_HEADERS);
         if (JS_IsException(header)) {
-            return JS_ThrowInternalError(cx, "fetch header creation failed");
+            return JS_ThrowOutOfMemory(cx);
         }
 
         JS_SetOpaque(header, &response->headers);
@@ -2339,7 +2336,7 @@ ngx_qjs_ext_fetch_response_body(JSContext *cx, JSValueConst this_val,
     }
 
     if (response->body_used) {
-        return JS_ThrowInternalError(cx, "body stream already read");
+        return JS_ThrowTypeError(cx, "body stream already read");
     }
 
     response->body_used = 1;
@@ -2463,7 +2460,6 @@ ngx_qjs_fetch_flag_set(JSContext *cx, const ngx_qjs_entry_t *entries,
 
     value = JS_GetPropertyStr(cx, object, prop);
     if (JS_IsException(value)) {
-        JS_ThrowInternalError(cx, "failed to get %s property", prop);
         return NGX_ERROR;
     }
 
@@ -2486,8 +2482,8 @@ ngx_qjs_fetch_flag_set(JSContext *cx, const ngx_qjs_entry_t *entries,
         }
     }
 
-    JS_ThrowInternalError(cx, "unknown %s type: %.*s", prop,
-                          (int) flag.len, flag.data);
+    JS_ThrowTypeError(cx, "unknown %s type: %.*s", prop,
+                      (int) flag.len, flag.data);
 
     JS_FreeCString(cx, (const char *) flag.data);
 
index 8bf9cf1de6d9f097abefb219c3a2513361c453af..8a6806ac8d3c4df75eb9cb0566774172e00bde84 100644 (file)
@@ -239,6 +239,21 @@ $t->write_file('test.js', <<EOF);
                 h.forEach((v, k) => { r.push(`\${v}:\${k}`)})
                 return r.join('|');
              }, 'a:0, 4|c:2|q:5|z:3'],
+            ['receiver', () => {
+                try {
+                    (new Headers()).append.call({}, 'a', 'b');
+                    throw new Error('no error');
+
+                } catch (e) {
+                    if (!(e instanceof TypeError)
+                        || e.message != '"this" is not fetch headers object')
+                    {
+                        throw e;
+                    }
+                }
+
+                return 'OK';
+             }, 'OK'],
             ['set', () => {
                 var h = new Headers([['A', 'x'], ['a', 'y'], ['a', 'z']]);
                 h.set('a', '#');
@@ -376,7 +391,9 @@ $t->write_file('test.js', <<EOF);
                         throw new Error('no error');
 
                     } catch (e) {
-                        if (!e.message.startsWith(`forbidden method: \${m}`)) {
+                        if (!(e instanceof TypeError)
+                            || !e.message.startsWith(`forbidden method: \${m}`))
+                        {
                             throw e;
                         }
                     }
@@ -388,7 +405,9 @@ $t->write_file('test.js', <<EOF);
                         throw new Error('no error');
 
                     } catch (e) {
-                        if (e.message != 'invalid Request method') {
+                        if (!(e instanceof TypeError)
+                            || e.message != 'invalid Request method')
+                        {
                             throw e;
                         }
                     }
@@ -548,6 +567,22 @@ $t->write_file('test.js', <<EOF);
 
                 return 'OK';
 
+             }, 'OK'],
+            ['status range', () => {
+                try {
+                    new Response(null, {status: 199});
+                    throw new Error('no error');
+
+                } catch (e) {
+                    if (!(e instanceof RangeError)
+                        || !e.message.startsWith('status provided (199) is'))
+                    {
+                        throw e;
+                    }
+                }
+
+                return 'OK';
+
              }, 'OK'],
         ];