@@ -2373,7 +2373,7 @@ bool Response::redirect(JSContext *cx, unsigned argc, JS::Value *vp) {
2373
2373
if (!headers) {
2374
2374
return false ;
2375
2375
}
2376
- if (!builtins::Headers::maybe_add (cx, headers, " Location " , value)) {
2376
+ if (!builtins::Headers::maybe_add (cx, headers, " location " , value)) {
2377
2377
return false ;
2378
2378
}
2379
2379
JS::SetReservedSlot (response, static_cast <uint32_t >(Slots::Headers), JS::ObjectValue (*headers));
@@ -2384,8 +2384,160 @@ bool Response::redirect(JSContext *cx, unsigned argc, JS::Value *vp) {
2384
2384
return true ;
2385
2385
}
2386
2386
2387
+ namespace {
2388
+ bool callbackCalled;
2389
+ bool write_json_to_buf (const char16_t *str, uint32_t strlen, void *out) {
2390
+ callbackCalled = true ;
2391
+ auto outstr = static_cast <std::u16string *>(out);
2392
+ outstr->append (str, strlen );
2393
+
2394
+ return true ;
2395
+ }
2396
+ } // namespace
2397
+
2398
+ bool Response::json (JSContext *cx, unsigned argc, JS::Value *vp) {
2399
+ JS::CallArgs args = JS::CallArgsFromVp (argc, vp);
2400
+ if (!args.requireAtLeast (cx, " json" , 1 )) {
2401
+ return false ;
2402
+ }
2403
+ JS::RootedValue data (cx, args.get (0 ));
2404
+ JS::RootedValue init_val (cx, args.get (1 ));
2405
+ JS::RootedObject replacer (cx);
2406
+ JS::RootedValue space (cx);
2407
+
2408
+ std::u16string out;
2409
+ // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.
2410
+ callbackCalled = false ;
2411
+ if (!JS::ToJSON (cx, data, replacer, space, &write_json_to_buf, &out)) {
2412
+ return false ;
2413
+ }
2414
+ if (!callbackCalled) {
2415
+ JS_ReportErrorNumberASCII (cx, GetErrorMessage, nullptr , JSMSG_RESPONSE_JSON_INVALID_VALUE);
2416
+ return false ;
2417
+ }
2418
+ // 2. Let body be the result of extracting bytes.
2419
+
2420
+ // 3. Let responseObject be the result of creating a Response object, given a new response,
2421
+ // "response", and this’s relevant Realm.
2422
+ JS::RootedValue status_val (cx);
2423
+ uint16_t status = 200 ;
2424
+
2425
+ JS::RootedValue statusText_val (cx);
2426
+ JS::RootedString statusText (cx, JS_GetEmptyString (cx));
2427
+ JS::RootedValue headers_val (cx);
2428
+
2429
+ if (init_val.isObject ()) {
2430
+ JS::RootedObject init (cx, init_val.toObjectOrNull ());
2431
+ if (!JS_GetProperty (cx, init, " status" , &status_val) ||
2432
+ !JS_GetProperty (cx, init, " statusText" , &statusText_val) ||
2433
+ !JS_GetProperty (cx, init, " headers" , &headers_val)) {
2434
+ return false ;
2435
+ }
2436
+
2437
+ if (!status_val.isUndefined () && !JS::ToUint16 (cx, status_val, &status)) {
2438
+ return false ;
2439
+ }
2440
+
2441
+ if (status == 204 || status == 205 || status == 304 ) {
2442
+ JS_ReportErrorNumberASCII (cx, GetErrorMessage, nullptr ,
2443
+ JSMSG_RESPONSE_NULL_BODY_STATUS_WITH_BODY);
2444
+ return false ;
2445
+ }
2446
+
2447
+ if (!statusText_val.isUndefined () && !(statusText = JS::ToString (cx, statusText_val))) {
2448
+ return false ;
2449
+ }
2450
+
2451
+ } else if (!init_val.isNullOrUndefined ()) {
2452
+ JS_ReportErrorLatin1 (cx, " Response constructor: |init| parameter can't be converted to "
2453
+ " a dictionary" );
2454
+ return false ;
2455
+ }
2456
+
2457
+ fastly_response_handle_t response_handle = INVALID_HANDLE;
2458
+ fastly_error_t err;
2459
+ if (!fastly_http_resp_new (&response_handle, &err)) {
2460
+ HANDLE_ERROR (cx, err);
2461
+ return false ;
2462
+ }
2463
+ if (response_handle == INVALID_HANDLE) {
2464
+ return false ;
2465
+ }
2466
+
2467
+ auto make_res = HttpBody::make ();
2468
+ if (auto *err = make_res.to_err ()) {
2469
+ HANDLE_ERROR (cx, *err);
2470
+ return false ;
2471
+ }
2472
+
2473
+ auto body = make_res.unwrap ();
2474
+ JS::RootedString string (cx, JS_NewUCStringCopyN (cx, out.c_str (), out.length ()));
2475
+ size_t encoded_len;
2476
+ auto stringChars = encode (cx, string, &encoded_len);
2477
+
2478
+ auto write_res = body.write_all (reinterpret_cast <uint8_t *>(stringChars.get ()), encoded_len);
2479
+ if (auto *err = write_res.to_err ()) {
2480
+ HANDLE_ERROR (cx, *err);
2481
+ return false ;
2482
+ }
2483
+ JS::RootedObject response_instance (cx, JS_NewObjectWithGivenProto (cx, &builtins::Response::class_,
2484
+ builtins::Response::proto_obj));
2485
+ if (!response_instance) {
2486
+ return false ;
2487
+ }
2488
+ JS::RootedObject response (cx, create (cx, response_instance, response_handle, body.handle , false ));
2489
+ if (!response) {
2490
+ return false ;
2491
+ }
2492
+
2493
+ // Set `this`’s `response`’s `status` to `init`["status"].
2494
+ if (!fastly_http_resp_status_set (response_handle, status, &err)) {
2495
+ HANDLE_ERROR (cx, err);
2496
+ return false ;
2497
+ }
2498
+ // To ensure that we really have the same status value as the host,
2499
+ // we always read it back here.
2500
+ if (!fastly_http_resp_status_get (response_handle, &status, &err)) {
2501
+ HANDLE_ERROR (cx, err);
2502
+ return false ;
2503
+ }
2504
+
2505
+ JS::SetReservedSlot (response, static_cast <uint32_t >(Slots::Status), JS::Int32Value (status));
2506
+
2507
+ // Set `this`’s `response`’s `status message` to `init`["statusText"].
2508
+ JS::SetReservedSlot (response, static_cast <uint32_t >(Slots::StatusMessage),
2509
+ JS::StringValue (statusText));
2510
+
2511
+ // If `init`["headers"] `exists`, then `fill` `this`’s `headers` with
2512
+ // `init`["headers"].
2513
+ JS::RootedObject headers (cx);
2514
+ JS::RootedObject headersInstance (
2515
+ cx, JS_NewObjectWithGivenProto (cx, &builtins::Headers::class_, builtins::Headers::proto_obj));
2516
+ if (!headersInstance)
2517
+ return false ;
2518
+
2519
+ headers = builtins::Headers::create (cx, headersInstance, builtins::Headers::Mode::ProxyToResponse,
2520
+ response, headers_val);
2521
+ if (!headers) {
2522
+ return false ;
2523
+ }
2524
+ // 4. Perform initialize a response given responseObject, init, and (body, "application/json").
2525
+ if (!builtins::Headers::maybe_add (cx, headers, " content-type" , " application/json" )) {
2526
+ return false ;
2527
+ }
2528
+ JS::SetReservedSlot (response, static_cast <uint32_t >(Slots::Headers), JS::ObjectValue (*headers));
2529
+ JS::SetReservedSlot (response, static_cast <uint32_t >(Slots::Redirected), JS::FalseValue ());
2530
+ JS::SetReservedSlot (response, static_cast <uint32_t >(Slots::HasBody), JS::TrueValue ());
2531
+ RequestOrResponse::set_url (response, JS_GetEmptyStringValue (cx));
2532
+
2533
+ // 5. Return responseObject.
2534
+ args.rval ().setObjectOrNull (response);
2535
+ return true ;
2536
+ }
2537
+
2387
2538
const JSFunctionSpec Response::static_methods[] = {
2388
2539
JS_FN (" redirect" , redirect, 1 , JSPROP_ENUMERATE),
2540
+ JS_FN (" json" , json, 1 , JSPROP_ENUMERATE),
2389
2541
JS_FS_END,
2390
2542
};
2391
2543
0 commit comments