From a14f6e42a82809eec71ba5fda7c30758707bda54 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 1 Aug 2023 18:53:39 -0400 Subject: [PATCH] LibWeb: Begin support for requesting blob URLs with Fetch infrastructure This does not yet implement requests with a Range header. --- .../LibWeb/Fetch/Fetching/Fetching.cpp | 81 ++++++++++++++++++- .../Fetch/Infrastructure/HTTP/Bodies.cpp | 3 + 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp b/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp index 8a4a9aec4d..5e93026bdd 100644 --- a/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include #include @@ -718,8 +720,83 @@ WebIDL::ExceptionOr> scheme_fetch(JS::Realm& r } // -> "blob" else if (request->current_url().scheme() == "blob"sv) { - // FIXME: Support 'blob://' URLs - return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Request has 'blob:' URL which is currently unsupported"sv)); + auto const& store = FileAPI::blob_url_store(); + + // 1. Let blobURLEntry be request’s current URL’s blob URL entry. + auto blob_url_entry = store.get(TRY_OR_THROW_OOM(vm, request->current_url().to_string())); + + // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s object is not a Blob object, + // then return a network error. [FILEAPI] + if (request->method() != "GET"sv.bytes() || !blob_url_entry.has_value()) { + // FIXME: Handle "blobURLEntry’s object is not a Blob object". It could be a MediaSource object, but we + // have not yet implemented the Media Source Extensions spec. + return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Request has an invalid 'blob:' URL"sv)); + } + + // 3. Let blob be blobURLEntry’s object. + auto const& blob = blob_url_entry->object; + + // 4. Let response be a new response. + auto response = Infrastructure::Response::create(vm); + + // 5. Let fullLength be blob’s size. + auto full_length = blob->size(); + + // 6. Let serializedFullLength be fullLength, serialized and isomorphic encoded. + auto serialized_full_length = TRY_OR_THROW_OOM(vm, String::number(full_length)); + + // 7. Let type be blob’s type. + auto const& type = blob->type(); + + // 8. If request’s header list does not contain `Range`: + if (!request->header_list()->contains("Range"sv.bytes())) { + // 1. Let bodyWithType be the result of safely extracting blob. + auto body_with_type = TRY(safely_extract_body(realm, blob)); + + // 2. Set response’s status message to `OK`. + response->set_status_message(MUST(ByteBuffer::copy("OK"sv.bytes()))); + + // 3. Set response’s body to bodyWithType’s body. + response->set_body(move(body_with_type.body)); + + // 4. Set response’s header list to « (`Content-Length`, serializedFullLength), (`Content-Type`, type) ». + auto content_length_header = TRY_OR_THROW_OOM(vm, Infrastructure::Header::from_string_pair("Content-Length"sv, serialized_full_length)); + TRY_OR_THROW_OOM(vm, response->header_list()->append(move(content_length_header))); + + auto content_type_header = TRY_OR_THROW_OOM(vm, Infrastructure::Header::from_string_pair("Content-Type"sv, type)); + TRY_OR_THROW_OOM(vm, response->header_list()->append(move(content_type_header))); + } + // FIXME: 9. Otherwise: + else { + // 1. Set response’s range-requested flag. + // 2. Let rangeHeader be the result of getting `Range` from request’s header list. + // 3. Let rangeValue be the result of parsing a single range header value given rangeHeader and true. + // 4. If rangeValue is failure, then return a network error. + // 5. Let (rangeStart, rangeEnd) be rangeValue. + // 6. If rangeStart is null: + // 1. Set rangeStart to fullLength − rangeEnd. + // 2. Set rangeEnd to rangeStart + rangeEnd − 1. + // 7. Otherwise: + // 1. If rangeStart is greater than or equal to fullLength, then return a network error. + // 2. If rangeEnd is null or rangeEnd is greater than or equal to fullLength, then set rangeEnd to fullLength − 1. + // 8. Let slicedBlob be the result of invoking slice blob given blob, rangeStart, rangeEnd + 1, and type. + // 9. Let slicedBodyWithType be the result of safely extracting slicedBlob. + // 10. Set response’s body to slicedBodyWithType’s body. + // 11. Let serializedSlicedLength be slicedBlob’s size, serialized and isomorphic encoded. + // 12. Let contentRange be `bytes `. + // 13. Append rangeStart, serialized and isomorphic encoded, to contentRange. + // 14. Append 0x2D (-) to contentRange. + // 15. Append rangeEnd, serialized and isomorphic encoded to contentRange. + // 16. Append 0x2F (/) to contentRange. + // 17. Append serializedFullLength to contentRange. + // 18. Set response’s status to 206. + // 19. Set response’s status message to `Partial Content`. + // 20. Set response’s header list to « (`Content-Length`, serializedSlicedLength), (`Content-Type`, type), (`Content-Range`, contentRange) ». + return PendingResponse::create(vm, request, Infrastructure::Response::network_error(vm, "Request has a 'blob:' URL with a Content-Range header, which is currently unsupported"sv)); + } + + // 10. Return response. + return PendingResponse::create(vm, request, response); } // -> "data" else if (request->current_url().scheme() == "data"sv) { diff --git a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp index d4b0bf3517..5c7ee0c8c0 100644 --- a/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Infrastructure/HTTP/Bodies.cpp @@ -69,6 +69,9 @@ WebIDL::ExceptionOr Body::fully_read(JS::Realm& realm, Web::Fetch::Infrast // FIXME: Implement the streams spec - this is completely made up for now :^) if (auto const* byte_buffer = m_source.get_pointer()) { TRY_OR_THROW_OOM(vm, success_steps(*byte_buffer)); + } else if (auto const* blob_handle = m_source.get_pointer>()) { + auto byte_buffer = TRY_OR_THROW_OOM(vm, ByteBuffer::copy((*blob_handle)->bytes())); + TRY_OR_THROW_OOM(vm, success_steps(move(byte_buffer))); } else { // Empty, Blob, FormData error_steps(WebIDL::DOMException::create(realm, "DOMException", "Reading from Blob, FormData or null source is not yet implemented"sv));