Responses.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. /*
  2. * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Debug.h>
  7. #include <AK/TypeCasts.h>
  8. #include <LibJS/Heap/Heap.h>
  9. #include <LibJS/Runtime/Completion.h>
  10. #include <LibJS/Runtime/VM.h>
  11. #include <LibWeb/Bindings/MainThreadVM.h>
  12. #include <LibWeb/DOMURL/DOMURL.h>
  13. #include <LibWeb/Fetch/Infrastructure/FetchParams.h>
  14. #include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
  15. #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
  16. namespace Web::Fetch::Infrastructure {
  17. JS_DEFINE_ALLOCATOR(Response);
  18. JS_DEFINE_ALLOCATOR(BasicFilteredResponse);
  19. JS_DEFINE_ALLOCATOR(CORSFilteredResponse);
  20. JS_DEFINE_ALLOCATOR(OpaqueFilteredResponse);
  21. JS_DEFINE_ALLOCATOR(OpaqueRedirectFilteredResponse);
  22. Response::Response(JS::NonnullGCPtr<HeaderList> header_list)
  23. : m_header_list(header_list)
  24. , m_response_time(UnixDateTime::now())
  25. {
  26. }
  27. void Response::visit_edges(JS::Cell::Visitor& visitor)
  28. {
  29. Base::visit_edges(visitor);
  30. visitor.visit(m_header_list);
  31. visitor.visit(m_body);
  32. }
  33. JS::NonnullGCPtr<Response> Response::create(JS::VM& vm)
  34. {
  35. return vm.heap().allocate_without_realm<Response>(HeaderList::create(vm));
  36. }
  37. // https://fetch.spec.whatwg.org/#ref-for-concept-network-error%E2%91%A3
  38. // A network error is a response whose status is always 0, status message is always
  39. // the empty byte sequence, header list is always empty, and body is always null.
  40. JS::NonnullGCPtr<Response> Response::aborted_network_error(JS::VM& vm)
  41. {
  42. auto response = network_error(vm, "Fetch has been aborted"sv);
  43. response->set_aborted(true);
  44. return response;
  45. }
  46. JS::NonnullGCPtr<Response> Response::network_error(JS::VM& vm, Variant<String, StringView> message)
  47. {
  48. dbgln_if(WEB_FETCH_DEBUG, "Fetch: Creating network error response with message: {}", message.visit([](auto const& s) -> StringView { return s; }));
  49. auto response = Response::create(vm);
  50. response->set_status(0);
  51. response->set_type(Type::Error);
  52. VERIFY(!response->body());
  53. response->m_network_error_message = move(message);
  54. return response;
  55. }
  56. // https://fetch.spec.whatwg.org/#appropriate-network-error
  57. JS::NonnullGCPtr<Response> Response::appropriate_network_error(JS::VM& vm, FetchParams const& fetch_params)
  58. {
  59. // 1. Assert: fetchParams is canceled.
  60. VERIFY(fetch_params.is_canceled());
  61. // 2. Return an aborted network error if fetchParams is aborted; otherwise return a network error.
  62. return fetch_params.is_aborted()
  63. ? aborted_network_error(vm)
  64. : network_error(vm, "Fetch has been terminated"sv);
  65. }
  66. // https://fetch.spec.whatwg.org/#concept-aborted-network-error
  67. bool Response::is_aborted_network_error() const
  68. {
  69. // A response whose type is "error" and aborted flag is set is known as an aborted network error.
  70. // NOTE: We have to use the virtual getter here to not bypass filtered responses.
  71. return type() == Type::Error && aborted();
  72. }
  73. // https://fetch.spec.whatwg.org/#concept-network-error
  74. bool Response::is_network_error() const
  75. {
  76. // A network error is a response whose type is "error", status is 0, status message is the empty byte sequence,
  77. // header list is « », body is null, and body info is a new response body info.
  78. // NOTE: We have to use the virtual getter here to not bypass filtered responses.
  79. if (type() != Type::Error)
  80. return false;
  81. if (status() != 0)
  82. return false;
  83. if (!status_message().is_empty())
  84. return false;
  85. if (!header_list()->is_empty())
  86. return false;
  87. if (body())
  88. return false;
  89. if (body_info() != BodyInfo {})
  90. return false;
  91. return true;
  92. }
  93. // https://fetch.spec.whatwg.org/#concept-response-url
  94. Optional<URL::URL const&> Response::url() const
  95. {
  96. // A response has an associated URL. It is a pointer to the last URL in response’s URL list and null if response’s URL list is empty.
  97. // NOTE: We have to use the virtual getter here to not bypass filtered responses.
  98. if (url_list().is_empty())
  99. return {};
  100. return url_list().last();
  101. }
  102. // https://fetch.spec.whatwg.org/#concept-response-location-url
  103. ErrorOr<Optional<URL::URL>> Response::location_url(Optional<String> const& request_fragment) const
  104. {
  105. // The location URL of a response response, given null or an ASCII string requestFragment, is the value returned by the following steps. They return null, failure, or a URL.
  106. // 1. If response’s status is not a redirect status, then return null.
  107. // NOTE: We have to use the virtual getter here to not bypass filtered responses.
  108. if (!is_redirect_status(status()))
  109. return Optional<URL::URL> {};
  110. // 2. Let location be the result of extracting header list values given `Location` and response’s header list.
  111. auto location_values_or_failure = extract_header_list_values("Location"sv.bytes(), m_header_list);
  112. if (location_values_or_failure.has<Infrastructure::ExtractHeaderParseFailure>() || location_values_or_failure.has<Empty>())
  113. return Optional<URL::URL> {};
  114. auto const& location_values = location_values_or_failure.get<Vector<ByteBuffer>>();
  115. if (location_values.size() != 1)
  116. return Optional<URL::URL> {};
  117. // 3. If location is a header value, then set location to the result of parsing location with response’s URL.
  118. auto location = DOMURL::parse(location_values.first(), url());
  119. if (!location.is_valid())
  120. return Error::from_string_literal("Invalid 'Location' header URL");
  121. // 4. If location is a URL whose fragment is null, then set location’s fragment to requestFragment.
  122. if (!location.fragment().has_value())
  123. location.set_fragment(request_fragment);
  124. // 5. Return location.
  125. return location;
  126. }
  127. // https://fetch.spec.whatwg.org/#concept-response-clone
  128. JS::NonnullGCPtr<Response> Response::clone(JS::Realm& realm) const
  129. {
  130. // To clone a response response, run these steps:
  131. auto& vm = realm.vm();
  132. // 1. If response is a filtered response, then return a new identical filtered response whose internal response is a clone of response’s internal response.
  133. if (is<FilteredResponse>(*this)) {
  134. auto internal_response = static_cast<FilteredResponse const&>(*this).internal_response()->clone(realm);
  135. if (is<BasicFilteredResponse>(*this))
  136. return BasicFilteredResponse::create(vm, internal_response);
  137. if (is<CORSFilteredResponse>(*this))
  138. return CORSFilteredResponse::create(vm, internal_response);
  139. if (is<OpaqueFilteredResponse>(*this))
  140. return OpaqueFilteredResponse::create(vm, internal_response);
  141. if (is<OpaqueRedirectFilteredResponse>(*this))
  142. return OpaqueRedirectFilteredResponse::create(vm, internal_response);
  143. VERIFY_NOT_REACHED();
  144. }
  145. // 2. Let newResponse be a copy of response, except for its body.
  146. auto new_response = Infrastructure::Response::create(vm);
  147. new_response->set_type(m_type);
  148. new_response->set_aborted(m_aborted);
  149. new_response->set_url_list(m_url_list);
  150. new_response->set_status(m_status);
  151. new_response->set_status_message(m_status_message);
  152. for (auto const& header : *m_header_list)
  153. new_response->header_list()->append(header);
  154. new_response->set_cache_state(m_cache_state);
  155. new_response->set_cors_exposed_header_name_list(m_cors_exposed_header_name_list);
  156. new_response->set_range_requested(m_range_requested);
  157. new_response->set_request_includes_credentials(m_request_includes_credentials);
  158. new_response->set_timing_allow_passed(m_timing_allow_passed);
  159. new_response->set_body_info(m_body_info);
  160. // FIXME: service worker timing info
  161. // 3. If response’s body is non-null, then set newResponse’s body to the result of cloning response’s body.
  162. if (m_body)
  163. new_response->set_body(m_body->clone(realm));
  164. // 4. Return newResponse.
  165. return new_response;
  166. }
  167. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#unsafe-response
  168. JS::NonnullGCPtr<Response> Response::unsafe_response()
  169. {
  170. // A response's unsafe response is its internal response if it has one, and the response itself otherwise.
  171. if (is<FilteredResponse>(this))
  172. return static_cast<FilteredResponse&>(*this).internal_response();
  173. return *this;
  174. }
  175. // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-cross-origin
  176. bool Response::is_cors_cross_origin() const
  177. {
  178. // A response whose type is "opaque" or "opaqueredirect" is CORS-cross-origin.
  179. return type() == Type::Opaque || type() == Type::OpaqueRedirect;
  180. }
  181. // https://fetch.spec.whatwg.org/#concept-fresh-response
  182. bool Response::is_fresh() const
  183. {
  184. // A fresh response is a response whose current age is within its freshness lifetime.
  185. return current_age() < freshness_lifetime();
  186. }
  187. // https://fetch.spec.whatwg.org/#concept-stale-while-revalidate-response
  188. bool Response::is_stale_while_revalidate() const
  189. {
  190. // A stale-while-revalidate response is a response that is not a fresh response and whose current age is within the stale-while-revalidate lifetime.
  191. return !is_fresh() && current_age() < stale_while_revalidate_lifetime();
  192. }
  193. // https://fetch.spec.whatwg.org/#concept-stale-response
  194. bool Response::is_stale() const
  195. {
  196. // A stale response is a response that is not a fresh response or a stale-while-revalidate response.
  197. return !is_fresh() && !is_stale_while_revalidate();
  198. }
  199. // https://httpwg.org/specs/rfc9111.html#age.calculations
  200. u64 Response::current_age() const
  201. {
  202. // The term "age_value" denotes the value of the Age header field (Section 5.1), in a form appropriate for arithmetic operation; or 0, if not available.
  203. Optional<AK::Duration> age;
  204. if (auto const age_header = header_list()->get("Age"sv.bytes()); age_header.has_value()) {
  205. if (auto converted_age = StringView { *age_header }.to_number<u64>(); converted_age.has_value())
  206. age = AK::Duration::from_seconds(converted_age.value());
  207. }
  208. auto const age_value = age.value_or(AK::Duration::from_seconds(0));
  209. // The term "date_value" denotes the value of the Date header field, in a form appropriate for arithmetic operations. See Section 6.6.1 of [HTTP] for the definition of the Date header field and for requirements regarding responses without it.
  210. // FIXME: Do we have a parser for HTTP-date?
  211. auto const date_value = UnixDateTime::now() - AK::Duration::from_seconds(5);
  212. // The term "now" means the current value of this implementation's clock (Section 5.6.7 of [HTTP]).
  213. auto const now = UnixDateTime::now();
  214. // The value of the clock at the time of the request that resulted in the stored response.
  215. // FIXME: Let's get the correct time.
  216. auto const request_time = UnixDateTime::now() - AK::Duration::from_seconds(5);
  217. // The value of the clock at the time the response was received.
  218. auto const response_time = m_response_time;
  219. auto const apparent_age = max(0, (response_time - date_value).to_seconds());
  220. auto const response_delay = response_time - request_time;
  221. auto const corrected_age_value = age_value + response_delay;
  222. auto const corrected_initial_age = max(apparent_age, corrected_age_value.to_seconds());
  223. auto const resident_time = (now - response_time).to_seconds();
  224. return corrected_initial_age + resident_time;
  225. }
  226. // https://httpwg.org/specs/rfc9111.html#calculating.freshness.lifetime
  227. u64 Response::freshness_lifetime() const
  228. {
  229. auto const elem = header_list()->get_decode_and_split("Cache-Control"sv.bytes());
  230. if (!elem.has_value())
  231. return 0;
  232. // FIXME: If the cache is shared and the s-maxage response directive (Section 5.2.2.10) is present, use its value
  233. // If the max-age response directive (Section 5.2.2.1) is present, use its value, or
  234. for (auto const& directive : *elem) {
  235. if (directive.starts_with_bytes("max-age"sv)) {
  236. auto equal_offset = directive.find_byte_offset('=');
  237. if (!equal_offset.has_value()) {
  238. dbgln("Bogus directive: '{}'", directive);
  239. continue;
  240. }
  241. auto const value_string = directive.bytes_as_string_view().substring_view(equal_offset.value() + 1);
  242. auto maybe_value = value_string.to_number<u64>();
  243. if (!maybe_value.has_value()) {
  244. dbgln("Bogus directive: '{}'", directive);
  245. continue;
  246. }
  247. return maybe_value.value();
  248. }
  249. }
  250. // FIXME: If the Expires response header field (Section 5.3) is present, use its value minus the value of the Date response header field (using the time the message was received if it is not present, as per Section 6.6.1 of [HTTP]), or
  251. // FIXME: Otherwise, no explicit expiration time is present in the response. A heuristic freshness lifetime might be applicable; see Section 4.2.2.
  252. return 0;
  253. }
  254. // https://httpwg.org/specs/rfc5861.html#n-the-stale-while-revalidate-cache-control-extension
  255. u64 Response::stale_while_revalidate_lifetime() const
  256. {
  257. auto const elem = header_list()->get_decode_and_split("Cache-Control"sv.bytes());
  258. if (!elem.has_value())
  259. return 0;
  260. for (auto const& directive : *elem) {
  261. if (directive.starts_with_bytes("stale-while-revalidate"sv)) {
  262. auto equal_offset = directive.find_byte_offset('=');
  263. if (!equal_offset.has_value()) {
  264. dbgln("Bogus directive: '{}'", directive);
  265. continue;
  266. }
  267. auto const value_string = directive.bytes_as_string_view().substring_view(equal_offset.value() + 1);
  268. auto maybe_value = value_string.to_number<u64>();
  269. if (!maybe_value.has_value()) {
  270. dbgln("Bogus directive: '{}'", directive);
  271. continue;
  272. }
  273. return maybe_value.value();
  274. }
  275. }
  276. return 0;
  277. }
  278. // Non-standard
  279. Optional<StringView> Response::network_error_message() const
  280. {
  281. if (!m_network_error_message.has_value())
  282. return {};
  283. return m_network_error_message->visit([](auto const& s) -> StringView { return s; });
  284. }
  285. FilteredResponse::FilteredResponse(JS::NonnullGCPtr<Response> internal_response, JS::NonnullGCPtr<HeaderList> header_list)
  286. : Response(header_list)
  287. , m_internal_response(internal_response)
  288. {
  289. }
  290. FilteredResponse::~FilteredResponse()
  291. {
  292. }
  293. void FilteredResponse::visit_edges(JS::Cell::Visitor& visitor)
  294. {
  295. Base::visit_edges(visitor);
  296. visitor.visit(m_internal_response);
  297. }
  298. JS::NonnullGCPtr<BasicFilteredResponse> BasicFilteredResponse::create(JS::VM& vm, JS::NonnullGCPtr<Response> internal_response)
  299. {
  300. // A basic filtered response is a filtered response whose type is "basic" and header list excludes
  301. // any headers in internal response’s header list whose name is a forbidden response-header name.
  302. auto header_list = HeaderList::create(vm);
  303. for (auto const& header : *internal_response->header_list()) {
  304. if (!is_forbidden_response_header_name(header.name))
  305. header_list->append(header);
  306. }
  307. return vm.heap().allocate_without_realm<BasicFilteredResponse>(internal_response, header_list);
  308. }
  309. BasicFilteredResponse::BasicFilteredResponse(JS::NonnullGCPtr<Response> internal_response, JS::NonnullGCPtr<HeaderList> header_list)
  310. : FilteredResponse(internal_response, header_list)
  311. , m_header_list(header_list)
  312. {
  313. }
  314. void BasicFilteredResponse::visit_edges(JS::Cell::Visitor& visitor)
  315. {
  316. Base::visit_edges(visitor);
  317. visitor.visit(m_header_list);
  318. }
  319. JS::NonnullGCPtr<CORSFilteredResponse> CORSFilteredResponse::create(JS::VM& vm, JS::NonnullGCPtr<Response> internal_response)
  320. {
  321. // A CORS filtered response is a filtered response whose type is "cors" and header list excludes
  322. // any headers in internal response’s header list whose name is not a CORS-safelisted response-header
  323. // name, given internal response’s CORS-exposed header-name list.
  324. Vector<ReadonlyBytes> cors_exposed_header_name_list;
  325. for (auto const& header_name : internal_response->cors_exposed_header_name_list())
  326. cors_exposed_header_name_list.append(header_name.span());
  327. auto header_list = HeaderList::create(vm);
  328. for (auto const& header : *internal_response->header_list()) {
  329. if (is_cors_safelisted_response_header_name(header.name, cors_exposed_header_name_list))
  330. header_list->append(header);
  331. }
  332. return vm.heap().allocate_without_realm<CORSFilteredResponse>(internal_response, header_list);
  333. }
  334. CORSFilteredResponse::CORSFilteredResponse(JS::NonnullGCPtr<Response> internal_response, JS::NonnullGCPtr<HeaderList> header_list)
  335. : FilteredResponse(internal_response, header_list)
  336. , m_header_list(header_list)
  337. {
  338. }
  339. void CORSFilteredResponse::visit_edges(JS::Cell::Visitor& visitor)
  340. {
  341. Base::visit_edges(visitor);
  342. visitor.visit(m_header_list);
  343. }
  344. JS::NonnullGCPtr<OpaqueFilteredResponse> OpaqueFilteredResponse::create(JS::VM& vm, JS::NonnullGCPtr<Response> internal_response)
  345. {
  346. // An opaque filtered response is a filtered response whose type is "opaque", URL list is the empty list,
  347. // status is 0, status message is the empty byte sequence, header list is empty, and body is null.
  348. return vm.heap().allocate_without_realm<OpaqueFilteredResponse>(internal_response, HeaderList::create(vm));
  349. }
  350. OpaqueFilteredResponse::OpaqueFilteredResponse(JS::NonnullGCPtr<Response> internal_response, JS::NonnullGCPtr<HeaderList> header_list)
  351. : FilteredResponse(internal_response, header_list)
  352. , m_header_list(header_list)
  353. {
  354. }
  355. void OpaqueFilteredResponse::visit_edges(JS::Cell::Visitor& visitor)
  356. {
  357. Base::visit_edges(visitor);
  358. visitor.visit(m_header_list);
  359. visitor.visit(m_body);
  360. }
  361. JS::NonnullGCPtr<OpaqueRedirectFilteredResponse> OpaqueRedirectFilteredResponse::create(JS::VM& vm, JS::NonnullGCPtr<Response> internal_response)
  362. {
  363. // An opaque-redirect filtered response is a filtered response whose type is "opaqueredirect",
  364. // status is 0, status message is the empty byte sequence, header list is empty, and body is null.
  365. return vm.heap().allocate_without_realm<OpaqueRedirectFilteredResponse>(internal_response, HeaderList::create(vm));
  366. }
  367. OpaqueRedirectFilteredResponse::OpaqueRedirectFilteredResponse(JS::NonnullGCPtr<Response> internal_response, JS::NonnullGCPtr<HeaderList> header_list)
  368. : FilteredResponse(internal_response, header_list)
  369. , m_header_list(header_list)
  370. {
  371. }
  372. void OpaqueRedirectFilteredResponse::visit_edges(JS::Cell::Visitor& visitor)
  373. {
  374. Base::visit_edges(visitor);
  375. visitor.visit(m_header_list);
  376. visitor.visit(m_body);
  377. }
  378. }