Title: [272908] trunk
Revision
272908
Author
achristen...@apple.com
Date
2021-02-16 08:59:26 -0800 (Tue, 16 Feb 2021)

Log Message

Synthesize range responses if needed in WebCoreNSURLSession
https://bugs.webkit.org/show_bug.cgi?id=221072

Reviewed by Geoff Garen.

Source/WebCore:

When we make a media request with a Range HTTP header field and the server doesn't respond with a 206 with Content-Range header field,
until now we would just fail to play the video.  In order to successfully play these videos, I introduce the RangeResponseGenerator class,
which will receive the data for a request for such a video and feed the data into WebCoreNSURLSession as the requested ranges are received.
Seeking is problematic, but at least we will try our best to play the video.

I added API tests that try to play a video that didn't play before using a server that doesn't support range requests.  Manual verification is also necessary.

* Sources.txt:
* SourcesCocoa.txt:
* WebCore.xcodeproj/project.pbxproj:
* platform/graphics/PlatformMediaResourceLoader.h:
(WebCore::PlatformMediaResource::setClient):
* platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm:
(WebCore::PlatformResourceMediaLoader::create):
* platform/network/ParsedRequestRange.cpp: Added.
(WebCore::ParsedRequestRange::parse):
* platform/network/ParsedRequestRange.h: Added.
(WebCore::ParsedRequestRange::begin const):
(WebCore::ParsedRequestRange::end const):
(WebCore::ParsedRequestRange::ParsedRequestRange):
* platform/network/cocoa/RangeResponseGenerator.h: Added.
* platform/network/cocoa/RangeResponseGenerator.mm: Added.
(WebCore::RangeResponseGenerator::Data::Data):
(WebCore::RangeResponseGenerator::Data::TaskData::TaskData):
(WebCore::synthesizedResponseForRange):
(WebCore::RangeResponseGenerator::removeTask):
(WebCore::RangeResponseGenerator::giveResponseToTaskIfBytesInRangeReceived):
(WebCore::RangeResponseGenerator::expectedContentLengthFromData):
(WebCore::RangeResponseGenerator::giveResponseToTasksWithFinishedRanges):
(WebCore::RangeResponseGenerator::willHandleRequest):
(WebCore::RangeResponseGenerator::willSynthesizeRangeResponses):
* platform/network/cocoa/WebCoreNSURLSession.h:
* platform/network/cocoa/WebCoreNSURLSession.mm:
(-[WebCoreNSURLSession rangeResponseGenerator]):
(-[WebCoreNSURLSession dataTaskWithURL:]):
(WebCore::WebCoreNSURLSessionDataTaskClient::dataSent):
(WebCore::WebCoreNSURLSessionDataTaskClient::responseReceived):
(WebCore::WebCoreNSURLSessionDataTaskClient::shouldCacheResponse):
(WebCore::WebCoreNSURLSessionDataTaskClient::dataReceived):
(WebCore::WebCoreNSURLSessionDataTaskClient::redirectReceived):
(WebCore::WebCoreNSURLSessionDataTaskClient::accessControlCheckFailed):
(WebCore::WebCoreNSURLSessionDataTaskClient::loadFailed):
(WebCore::WebCoreNSURLSessionDataTaskClient::loadFinished):
(-[WebCoreNSURLSessionDataTask _restart]):
(-[WebCoreNSURLSessionDataTask _finish]):
(-[WebCoreNSURLSessionDataTask resource:sentBytes:totalBytesToBeSent:]):
(-[WebCoreNSURLSessionDataTask resource:receivedResponse:completionHandler:]):
(-[WebCoreNSURLSessionDataTask resource:shouldCacheResponse:]):
(-[WebCoreNSURLSessionDataTask resource:receivedData:length:]):
(-[WebCoreNSURLSessionDataTask resource:receivedRedirect:request:completionHandler:]):
(-[WebCoreNSURLSessionDataTask _resource:loadFinishedWithError:metrics:]):
(-[WebCoreNSURLSessionDataTask resource:accessControlCheckFailedWithError:]):
(-[WebCoreNSURLSessionDataTask resource:loadFailedWithError:]):
(-[WebCoreNSURLSessionDataTask resourceFinished:metrics:]):
(-[WebCoreNSURLSessionDataTask initWithSession:identifier:URL:]): Deleted.

Source/WebKit:

* WebProcess/GPU/media/MediaPlayerPrivateRemote.cpp:
(WebKit::MediaPlayerPrivateRemote::requestResource):

Tools:

* TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp:
(TestWebKitAPI::TEST):
* TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm:
(TestWebKitAPI::clientCertServer):
* TestWebKitAPI/Tests/WebKitCocoa/MediaLoading.mm:
(TestWebKitAPI::testVideoBytes):
(TestWebKitAPI::runVideoTest):
(TestWebKitAPI::TEST):
* TestWebKitAPI/cocoa/HTTPServer.h:
(TestWebKitAPI::HTTPResponse::HTTPResponse):
(TestWebKitAPI::HTTPServer::HTTPResponse::HTTPResponse): Deleted.
* TestWebKitAPI/cocoa/HTTPServer.mm:
(TestWebKitAPI::HTTPServer::RequestData::RequestData):
(TestWebKitAPI::appendToVector):
(TestWebKitAPI::HTTPServer::parsePath):
(TestWebKitAPI::HTTPServer::respondToRequests):
(TestWebKitAPI::HTTPResponse::bodyFromString):
(TestWebKitAPI::HTTPResponse::serialize):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (272907 => 272908)


--- trunk/Source/WebCore/ChangeLog	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/ChangeLog	2021-02-16 16:59:26 UTC (rev 272908)
@@ -1,3 +1,66 @@
+2021-02-16  Alex Christensen  <achristen...@webkit.org>
+
+        Synthesize range responses if needed in WebCoreNSURLSession
+        https://bugs.webkit.org/show_bug.cgi?id=221072
+
+        Reviewed by Geoff Garen.
+
+        When we make a media request with a Range HTTP header field and the server doesn't respond with a 206 with Content-Range header field,
+        until now we would just fail to play the video.  In order to successfully play these videos, I introduce the RangeResponseGenerator class,
+        which will receive the data for a request for such a video and feed the data into WebCoreNSURLSession as the requested ranges are received.
+        Seeking is problematic, but at least we will try our best to play the video.
+
+        I added API tests that try to play a video that didn't play before using a server that doesn't support range requests.  Manual verification is also necessary.
+
+        * Sources.txt:
+        * SourcesCocoa.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * platform/graphics/PlatformMediaResourceLoader.h:
+        (WebCore::PlatformMediaResource::setClient):
+        * platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm:
+        (WebCore::PlatformResourceMediaLoader::create):
+        * platform/network/ParsedRequestRange.cpp: Added.
+        (WebCore::ParsedRequestRange::parse):
+        * platform/network/ParsedRequestRange.h: Added.
+        (WebCore::ParsedRequestRange::begin const):
+        (WebCore::ParsedRequestRange::end const):
+        (WebCore::ParsedRequestRange::ParsedRequestRange):
+        * platform/network/cocoa/RangeResponseGenerator.h: Added.
+        * platform/network/cocoa/RangeResponseGenerator.mm: Added.
+        (WebCore::RangeResponseGenerator::Data::Data):
+        (WebCore::RangeResponseGenerator::Data::TaskData::TaskData):
+        (WebCore::synthesizedResponseForRange):
+        (WebCore::RangeResponseGenerator::removeTask):
+        (WebCore::RangeResponseGenerator::giveResponseToTaskIfBytesInRangeReceived):
+        (WebCore::RangeResponseGenerator::expectedContentLengthFromData):
+        (WebCore::RangeResponseGenerator::giveResponseToTasksWithFinishedRanges):
+        (WebCore::RangeResponseGenerator::willHandleRequest):
+        (WebCore::RangeResponseGenerator::willSynthesizeRangeResponses):
+        * platform/network/cocoa/WebCoreNSURLSession.h:
+        * platform/network/cocoa/WebCoreNSURLSession.mm:
+        (-[WebCoreNSURLSession rangeResponseGenerator]):
+        (-[WebCoreNSURLSession dataTaskWithURL:]):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::dataSent):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::responseReceived):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::shouldCacheResponse):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::dataReceived):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::redirectReceived):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::accessControlCheckFailed):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::loadFailed):
+        (WebCore::WebCoreNSURLSessionDataTaskClient::loadFinished):
+        (-[WebCoreNSURLSessionDataTask _restart]):
+        (-[WebCoreNSURLSessionDataTask _finish]):
+        (-[WebCoreNSURLSessionDataTask resource:sentBytes:totalBytesToBeSent:]):
+        (-[WebCoreNSURLSessionDataTask resource:receivedResponse:completionHandler:]):
+        (-[WebCoreNSURLSessionDataTask resource:shouldCacheResponse:]):
+        (-[WebCoreNSURLSessionDataTask resource:receivedData:length:]):
+        (-[WebCoreNSURLSessionDataTask resource:receivedRedirect:request:completionHandler:]):
+        (-[WebCoreNSURLSessionDataTask _resource:loadFinishedWithError:metrics:]):
+        (-[WebCoreNSURLSessionDataTask resource:accessControlCheckFailedWithError:]):
+        (-[WebCoreNSURLSessionDataTask resource:loadFailedWithError:]):
+        (-[WebCoreNSURLSessionDataTask resourceFinished:metrics:]):
+        (-[WebCoreNSURLSessionDataTask initWithSession:identifier:URL:]): Deleted.
+
 2021-02-16  Antti Koivisto  <an...@apple.com>
 
         [LFC][Integration] Pass child inline block scroll overflow to parent

Modified: trunk/Source/WebCore/Headers.cmake (272907 => 272908)


--- trunk/Source/WebCore/Headers.cmake	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/Headers.cmake	2021-02-16 16:59:26 UTC (rev 272908)
@@ -1412,6 +1412,7 @@
     platform/network/NetworkingContext.h
     platform/network/ParsedContentRange.h
     platform/network/ParsedContentType.h
+    platform/network/ParsedRequestRange.h
     platform/network/ProtectionSpace.h
     platform/network/ProtectionSpaceBase.h
     platform/network/ProtectionSpaceHash.h

Modified: trunk/Source/WebCore/Sources.txt (272907 => 272908)


--- trunk/Source/WebCore/Sources.txt	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/Sources.txt	2021-02-16 16:59:26 UTC (rev 272908)
@@ -2125,6 +2125,7 @@
 platform/network/NetworkStorageSession.cpp
 platform/network/ParsedContentRange.cpp
 platform/network/ParsedContentType.cpp
+platform/network/ParsedRequestRange.cpp
 platform/network/ProtectionSpaceBase.cpp
 platform/network/ProxyServer.cpp
 platform/network/ResourceErrorBase.cpp

Modified: trunk/Source/WebCore/SourcesCocoa.txt (272907 => 272908)


--- trunk/Source/WebCore/SourcesCocoa.txt	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/SourcesCocoa.txt	2021-02-16 16:59:26 UTC (rev 272908)
@@ -569,6 +569,7 @@
 platform/network/cocoa/NetworkLoadMetrics.mm
 platform/network/cocoa/NetworkStorageSessionCocoa.mm
 platform/network/cocoa/ProtectionSpaceCocoa.mm
+platform/network/cocoa/RangeResponseGenerator.mm
 platform/network/cocoa/ResourceRequestCocoa.mm
 platform/network/cocoa/ResourceResponseCocoa.mm @no-unify // Unsafe to unify until rdar://problem/48853137 is resolved
 platform/network/cocoa/WebCoreNSURLSession.mm @no-unify

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (272907 => 272908)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2021-02-16 16:59:26 UTC (rev 272908)
@@ -1945,6 +1945,8 @@
 		5C2B1AEC22397EBC00B91CF7 /* ResourceResponseCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = A1F78D0B1C25422C00245446 /* ResourceResponseCocoa.mm */; };
 		5C4304B1191AC908000E2BC0 /* EXTShaderTextureLOD.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4304AE191AC908000E2BC0 /* EXTShaderTextureLOD.h */; };
 		5C4304B6191AEF46000E2BC0 /* JSEXTShaderTextureLOD.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4304B4191AEF46000E2BC0 /* JSEXTShaderTextureLOD.h */; };
+		5C4A0FD725C3435000D9EE97 /* RangeResponseGenerator.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4A0FD525C342C800D9EE97 /* RangeResponseGenerator.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		5C4A0FDA25C3449A00D9EE97 /* ParsedRequestRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C4A0FD825C3446C00D9EE97 /* ParsedRequestRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C53DCE124465DFC00A93124 /* ApplePaySetupFeatureWebCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C53DCCF2446449900A93124 /* ApplePaySetupFeatureWebCore.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C53DCE724468AD200A93124 /* PaymentInstallmentConfigurationWebCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C53DCCE2446449900A93124 /* PaymentInstallmentConfigurationWebCore.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		5C53DCEA24468FB400A93124 /* ApplePaySetupWebCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C53DCCB2446449800A93124 /* ApplePaySetupWebCore.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -9501,6 +9503,11 @@
 		5C4304AF191AC908000E2BC0 /* EXTShaderTextureLOD.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EXTShaderTextureLOD.idl; sourceTree = "<group>"; };
 		5C4304B3191AEF46000E2BC0 /* JSEXTShaderTextureLOD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSEXTShaderTextureLOD.cpp; sourceTree = "<group>"; };
 		5C4304B4191AEF46000E2BC0 /* JSEXTShaderTextureLOD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSEXTShaderTextureLOD.h; sourceTree = "<group>"; };
+		5C4A0FD325C342C700D9EE97 /* RangeResponseGenerator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RangeResponseGenerator.mm; sourceTree = "<group>"; };
+		5C4A0FD525C342C800D9EE97 /* RangeResponseGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RangeResponseGenerator.h; sourceTree = "<group>"; };
+		5C4A0FD625C342DB00D9EE97 /* CertificateInfoCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CertificateInfoCocoa.mm; sourceTree = "<group>"; };
+		5C4A0FD825C3446C00D9EE97 /* ParsedRequestRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ParsedRequestRange.h; sourceTree = "<group>"; };
+		5C4A0FD925C3446D00D9EE97 /* ParsedRequestRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParsedRequestRange.cpp; sourceTree = "<group>"; };
 		5C5381AF1D8793E000E2EBE6 /* URLSearchParams.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = URLSearchParams.idl; sourceTree = "<group>"; };
 		5C5381B01D87D45700E2EBE6 /* URLSearchParams.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = URLSearchParams.cpp; sourceTree = "<group>"; };
 		5C5381B11D87D45700E2EBE6 /* URLSearchParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = URLSearchParams.h; sourceTree = "<group>"; };
@@ -21848,6 +21855,8 @@
 				CDCD41E61C3DDB0900965D99 /* ParsedContentRange.h */,
 				447958021643B47B001E0A7F /* ParsedContentType.cpp */,
 				447958031643B47B001E0A7F /* ParsedContentType.h */,
+				5C4A0FD925C3446D00D9EE97 /* ParsedRequestRange.cpp */,
+				5C4A0FD825C3446C00D9EE97 /* ParsedRequestRange.h */,
 				37BAAE571980D1DD005DFE71 /* ProtectionSpace.h */,
 				514C765F0CE923A1007EF3CD /* ProtectionSpaceBase.cpp */,
 				514C76600CE923A1007EF3CD /* ProtectionSpaceBase.h */,
@@ -22932,6 +22941,7 @@
 		7E7DE1FE195CEF2D0035363B /* cocoa */ = {
 			isa = PBXGroup;
 			children = (
+				5C4A0FD625C342DB00D9EE97 /* CertificateInfoCocoa.mm */,
 				51D1248C1E736456002B2820 /* CookieCocoa.mm */,
 				5120BBAD1F1CE77000EFEBF1 /* CookieStorageObserver.h */,
 				5120BBAC1F1CE77000EFEBF1 /* CookieStorageObserver.mm */,
@@ -22942,6 +22952,8 @@
 				51D1248A1E73625C002B2820 /* NetworkStorageSessionCocoa.mm */,
 				372ADA37197F47B900FC501E /* ProtectionSpaceCocoa.h */,
 				372ADA39197F687600FC501E /* ProtectionSpaceCocoa.mm */,
+				5C4A0FD525C342C800D9EE97 /* RangeResponseGenerator.h */,
+				5C4A0FD325C342C700D9EE97 /* RangeResponseGenerator.mm */,
 				7E7DE1FC195CEF260035363B /* ResourceRequestCocoa.mm */,
 				A1F78D0B1C25422C00245446 /* ResourceResponseCocoa.mm */,
 				CD225C0A1C46FBF400140761 /* WebCoreNSURLSession.h */,
@@ -34015,6 +34027,7 @@
 				A18890AF1AA13F250026C301 /* ParentalControlsContentFilter.h in Headers */,
 				CDCD41E81C3DDB0A00965D99 /* ParsedContentRange.h in Headers */,
 				447958041643B49A001E0A7F /* ParsedContentType.h in Headers */,
+				5C4A0FDA25C3449A00D9EE97 /* ParsedRequestRange.h in Headers */,
 				536D5A23193E8E0C00CE4CAB /* ParsingUtilities.h in Headers */,
 				F55B3DCA1251F12D003EF269 /* PasswordInputType.h in Headers */,
 				4B2708C70AF19EE40065127F /* Pasteboard.h in Headers */,
@@ -34218,6 +34231,7 @@
 				93F1991808245E59001E9ABC /* Range.h in Headers */,
 				93D9D53C0DA27E180077216C /* RangeBoundaryPoint.h in Headers */,
 				F55B3DCE1251F12D003EF269 /* RangeInputType.h in Headers */,
+				5C4A0FD725C3435000D9EE97 /* RangeResponseGenerator.h in Headers */,
 				6E84E9E117668BF100815B68 /* RasterShape.h in Headers */,
 				A84D827C11D333ED00972990 /* RawDataDocumentParser.h in Headers */,
 				416E6FE81BBD12DF000A6043 /* ReadableByteStreamInternalsBuiltins.h in Headers */,

Modified: trunk/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h (272907 => 272908)


--- trunk/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/platform/graphics/PlatformMediaResourceLoader.h	2021-02-16 16:59:26 UTC (rev 272908)
@@ -41,7 +41,7 @@
 class ResourceRequest;
 class ResourceResponse;
 
-class PlatformMediaResourceClient {
+class PlatformMediaResourceClient : public RefCounted<PlatformMediaResourceClient> {
 public:
     virtual ~PlatformMediaResourceClient() = default;
 
@@ -81,11 +81,11 @@
     virtual void stop() { }
     virtual bool didPassAccessControlCheck() const { return false; }
 
-    void setClient(std::unique_ptr<PlatformMediaResourceClient>&& client) { m_client = WTFMove(client); }
+    void setClient(RefPtr<PlatformMediaResourceClient>&& client) { m_client = WTFMove(client); }
     PlatformMediaResourceClient* client() { return m_client.get(); }
 
 protected:
-    std::unique_ptr<PlatformMediaResourceClient> m_client;
+    RefPtr<PlatformMediaResourceClient> m_client;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm (272907 => 272908)


--- trunk/Source/WebCore/platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/platform/graphics/avfoundation/objc/WebCoreAVFResourceLoader.mm	2021-02-16 16:59:26 UTC (rev 272908)
@@ -170,7 +170,7 @@
     if (!resource)
         return nullptr;
     auto* resourcePointer = resource.get();
-    auto client = std::unique_ptr<PlatformResourceMediaLoader>(new PlatformResourceMediaLoader { parent, resource.releaseNonNull() });
+    auto client = adoptRef(*new PlatformResourceMediaLoader { parent, resource.releaseNonNull() });
     auto result = makeWeakPtr(client.get());
 
     resourcePointer->setClient(WTFMove(client));

Modified: trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp (272907 => 272908)


--- trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp	2021-02-16 16:59:26 UTC (rev 272908)
@@ -699,7 +699,7 @@
         PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
         members->resource = members->loader->requestResource(ResourceRequest(request), loadOptions);
         if (members->resource) {
-            members->resource->setClient(makeUnique<CachedResourceStreamingClient>(protector.get(), ResourceRequest(request), requestNumber));
+            members->resource->setClient(adoptRef(*new CachedResourceStreamingClient(protector.get(), ResourceRequest(request), requestNumber)));
             GST_DEBUG_OBJECT(protector.get(), "Started request R%u", requestNumber);
         } else {
             GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client to handle R%u", requestNumber);

Added: trunk/Source/WebCore/platform/network/ParsedRequestRange.cpp (0 => 272908)


--- trunk/Source/WebCore/platform/network/ParsedRequestRange.cpp	                        (rev 0)
+++ trunk/Source/WebCore/platform/network/ParsedRequestRange.cpp	2021-02-16 16:59:26 UTC (rev 272908)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ParsedRequestRange.h"
+
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+Optional<ParsedRequestRange> ParsedRequestRange::parse(StringView input)
+{
+    // https://tools.ietf.org/html/rfc7233#section-2.1 but assuming there will always be a begin and an end or parsing will fail
+    if (!input.startsWith(StringView("bytes="_s)))
+        return WTF::nullopt;
+
+    size_t begin { 0 };
+    size_t end { 0 };
+    size_t rangeBeginPosition = 6;
+    size_t dashPosition = input.find('-', rangeBeginPosition);
+    if (dashPosition == notFound)
+        return WTF::nullopt;
+
+    auto beginString = input.substring(rangeBeginPosition, dashPosition - rangeBeginPosition);
+    auto optionalBegin = beginString.toUInt64Strict();
+    if (!optionalBegin)
+        return WTF::nullopt;
+    begin = *optionalBegin;
+
+    auto endString = input.substring(dashPosition + 1);
+    auto optionalEnd = endString.toUInt64Strict();
+    if (!optionalEnd)
+        return WTF::nullopt;
+    end = *optionalEnd;
+
+    if (begin > end)
+        return WTF::nullopt;
+
+    return {{ begin, end }};
+}
+
+}

Added: trunk/Source/WebCore/platform/network/ParsedRequestRange.h (0 => 272908)


--- trunk/Source/WebCore/platform/network/ParsedRequestRange.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/network/ParsedRequestRange.h	2021-02-16 16:59:26 UTC (rev 272908)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/Forward.h>
+#include <wtf/HashTraits.h>
+#include <wtf/text/StringView.h>
+
+namespace WebCore {
+
+struct ParsedRequestRange {
+public:
+    WEBCORE_EXPORT static Optional<ParsedRequestRange> parse(StringView);
+    static Optional<ParsedRequestRange> parse(const String& string) { return parse(StringView(string)); }
+
+    const size_t begin { 0 };
+    const size_t end { 0 };
+
+private:
+    ParsedRequestRange(size_t begin, size_t end)
+        : begin(begin)
+        , end(end) { }
+};
+
+}

Added: trunk/Source/WebCore/platform/network/cocoa/RangeResponseGenerator.h (0 => 272908)


--- trunk/Source/WebCore/platform/network/cocoa/RangeResponseGenerator.h	                        (rev 0)
+++ trunk/Source/WebCore/platform/network/cocoa/RangeResponseGenerator.h	2021-02-16 16:59:26 UTC (rev 272908)
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <wtf/URLHash.h>
+#include <wtf/WeakPtr.h>
+
+OBJC_CLASS NSURLRequest;
+OBJC_CLASS WebCoreNSURLSessionDataTask;
+
+namespace WebCore {
+
+struct ParsedRequestRange;
+class PlatformMediaResource;
+class ResourceResponse;
+
+class RangeResponseGenerator : public CanMakeWeakPtr<RangeResponseGenerator> {
+public:
+    RangeResponseGenerator();
+    ~RangeResponseGenerator();
+
+    bool willSynthesizeRangeResponses(WebCoreNSURLSessionDataTask *, PlatformMediaResource&, const ResourceResponse&);
+    bool willHandleRequest(WebCoreNSURLSessionDataTask *, NSURLRequest *);
+
+private:
+    struct Data;
+    class MediaResourceClient;
+    void giveResponseToTasksWithFinishedRanges(Data&);
+    void giveResponseToTaskIfBytesInRangeReceived(WebCoreNSURLSessionDataTask *, const ParsedRequestRange&, Optional<size_t> expectedContentLength, const Data&);
+    static Optional<size_t> expectedContentLengthFromData(const Data&);
+    void removeTask(WebCoreNSURLSessionDataTask *);
+
+    HashMap<String, std::unique_ptr<Data>> m_map;
+};
+
+} // namespace WebCore

Added: trunk/Source/WebCore/platform/network/cocoa/RangeResponseGenerator.mm (0 => 272908)


--- trunk/Source/WebCore/platform/network/cocoa/RangeResponseGenerator.mm	                        (rev 0)
+++ trunk/Source/WebCore/platform/network/cocoa/RangeResponseGenerator.mm	2021-02-16 16:59:26 UTC (rev 272908)
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2021 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "RangeResponseGenerator.h"
+
+#import "NetworkLoadMetrics.h"
+#import "ParsedRequestRange.h"
+#import "PlatformMediaResourceLoader.h"
+#import "ResourceResponse.h"
+#import "SharedBuffer.h"
+#import "WebCoreNSURLSession.h"
+#import <pal/spi/cf/CFNetworkSPI.h>
+#import <wtf/FastMalloc.h>
+#import <wtf/text/StringBuilder.h>
+
+namespace WebCore {
+
+struct RangeResponseGenerator::Data {
+    WTF_MAKE_STRUCT_FAST_ALLOCATED;
+    Data(const ResourceResponse& response, PlatformMediaResource& resource)
+        : buffer(SharedBuffer::create())
+        , originalResponse(response)
+        , resource(&resource) { }
+
+    struct TaskData : public CanMakeWeakPtr<TaskData> {
+        WTF_MAKE_STRUCT_FAST_ALLOCATED;
+        TaskData(ParsedRequestRange&& range)
+            : range(WTFMove(range))
+            , nextByteToGiveBufferIndex(range.begin) { }
+
+        ParsedRequestRange range;
+        size_t nextByteToGiveBufferIndex { 0 };
+        enum class ResponseState : uint8_t { NotSynthesizedYet, WaitingForSession, SessionCalledCompletionHandler } responseState { ResponseState::NotSynthesizedYet };
+    };
+    
+    HashMap<RetainPtr<WebCoreNSURLSessionDataTask>, std::unique_ptr<TaskData>> taskData;
+    Ref<SharedBuffer> buffer;
+    ResourceResponse originalResponse;
+    enum class SuccessfullyFinishedLoading : bool { No, Yes } successfullyFinishedLoading { SuccessfullyFinishedLoading::No };
+    RefPtr<PlatformMediaResource> resource;
+};
+
+RangeResponseGenerator::RangeResponseGenerator() = default;
+RangeResponseGenerator::~RangeResponseGenerator() = default;
+
+static ResourceResponse synthesizedResponseForRange(const ResourceResponse& originalResponse, const ParsedRequestRange& parsedRequestRange, Optional<size_t> totalContentLength)
+{
+    ASSERT(isMainThread());
+    auto begin = parsedRequestRange.begin;
+    auto end = parsedRequestRange.end;
+
+    auto newContentRange = makeString("bytes ", begin, "-", end, "/", (totalContentLength ? makeString(*totalContentLength) : "*"));
+    auto newContentLength = makeString(end - begin + 1);
+
+    ResourceResponse newResponse = originalResponse;
+    newResponse.setHTTPHeaderField(HTTPHeaderName::ContentRange, newContentRange);
+    newResponse.setHTTPHeaderField(HTTPHeaderName::ContentLength, newContentLength);
+    constexpr auto partialContent = 206;
+    newResponse.setHTTPStatusCode(partialContent);
+
+    return newResponse;
+}
+
+void RangeResponseGenerator::removeTask(WebCoreNSURLSessionDataTask *task)
+{
+    callOnMainThread([task = retainPtr(task), weakThis = makeWeakPtr(*this)] {
+        if (!weakThis)
+            return;
+        auto* data = ""
+        if (!data)
+            return;
+        data->taskData.remove(task);
+    });
+}
+
+void RangeResponseGenerator::giveResponseToTaskIfBytesInRangeReceived(WebCoreNSURLSessionDataTask *task, const ParsedRequestRange& range, Optional<size_t> expectedContentLength, const Data& data)
+{
+    ASSERT(isMainThread());
+    auto buffer = data.buffer;
+    auto bufferSize = buffer->size();
+
+    if (bufferSize < range.begin)
+        return;
+    
+    auto* taskData = data.taskData.get(task);
+    if (!taskData)
+        return;
+    
+    auto giveBytesToTask = [task = retainPtr(task), buffer, taskData = makeWeakPtr(*taskData), generator = makeWeakPtr(*this)] {
+        ASSERT(isMainThread());
+        if ([task state] != NSURLSessionTaskStateRunning)
+            return;
+        if (!taskData)
+            return;
+        auto& range = taskData->range;
+        auto& byteIndex = taskData->nextByteToGiveBufferIndex;
+        while (true) {
+            if (byteIndex >= buffer->size())
+                break;
+            auto bufferView = buffer->getSomeData(byteIndex);
+            if (!bufferView.size() || byteIndex > range.end)
+                break;
+
+            size_t bytesFromThisViewToDeliver = std::min(bufferView.size(), range.end - byteIndex + 1);
+            byteIndex += bytesFromThisViewToDeliver;
+            [task resource:nullptr receivedData:bufferView.data() length:bytesFromThisViewToDeliver];
+        }
+        if (byteIndex >= range.end) {
+            [task resourceFinished:nullptr metrics:NetworkLoadMetrics { }];
+            if (generator)
+                generator->removeTask(task.get());
+        }
+    };
+
+    switch (taskData->responseState) {
+    case Data::TaskData::ResponseState::NotSynthesizedYet: {
+        auto response = synthesizedResponseForRange(data.originalResponse, range, expectedContentLength);
+        [task resource:nullptr receivedResponse:response completionHandler:[giveBytesToTask = WTFMove(giveBytesToTask), taskData = makeWeakPtr(taskData), task = retainPtr(task)] (WebCore::ShouldContinuePolicyCheck shouldContinue) {
+            if (taskData)
+                taskData->responseState = Data::TaskData::ResponseState::SessionCalledCompletionHandler;
+            if (shouldContinue == ShouldContinuePolicyCheck::Yes)
+                giveBytesToTask();
+            else
+                [task cancel];
+        }];
+        taskData->responseState = Data::TaskData::ResponseState::WaitingForSession;
+        break;
+    }
+    case Data::TaskData::ResponseState::WaitingForSession:
+        break;
+    case Data::TaskData::ResponseState::SessionCalledCompletionHandler:
+        giveBytesToTask();
+        break;
+    }
+}
+
+Optional<size_t> RangeResponseGenerator::expectedContentLengthFromData(const Data& data)
+{
+    ASSERT(isMainThread());
+    if (data.successfullyFinishedLoading == Data::SuccessfullyFinishedLoading::Yes)
+        return data.buffer->size();
+
+    // FIXME: ResourceResponseBase::expectedContentLength() should return Optional<size_t> instead of us doing this check here.
+    auto expectedContentLength = data.originalResponse.expectedContentLength();
+    if (expectedContentLength == NSURLResponseUnknownLength)
+        return WTF::nullopt;
+    return expectedContentLength;
+}
+
+void RangeResponseGenerator::giveResponseToTasksWithFinishedRanges(Data& data)
+{
+    ASSERT(isMainThread());
+    auto expectedContentLength = expectedContentLengthFromData(data);
+
+    for (auto& pair : data.taskData)
+        giveResponseToTaskIfBytesInRangeReceived(pair.key.get(), pair.value->range, expectedContentLength, data);
+}
+
+bool RangeResponseGenerator::willHandleRequest(WebCoreNSURLSessionDataTask *task, NSURLRequest *request)
+{
+    ASSERT(isMainThread());
+    auto* data = ""
+    if (!data)
+        return false;
+
+    auto range = ParsedRequestRange::parse([request valueForHTTPHeaderField:@"Range"]);
+    if (!range)
+        return false;
+
+    auto expectedContentLength = expectedContentLengthFromData(*data);
+    data->taskData.add(task, makeUnique<Data::TaskData>(WTFMove(*range)));
+    giveResponseToTaskIfBytesInRangeReceived(task, *range, expectedContentLength, *data);
+
+    return true;
+}
+
+class RangeResponseGenerator::MediaResourceClient : public PlatformMediaResourceClient {
+public:
+    MediaResourceClient(RangeResponseGenerator& generator, URL&& url)
+        : m_generator(makeWeakPtr(generator))
+        , m_urlString(WTFMove(url).string()) { }
+private:
+
+    // These methods should have been called before changing the client to this.
+    void responseReceived(PlatformMediaResource&, const ResourceResponse&, CompletionHandler<void(ShouldContinuePolicyCheck)>&& completionHandler) final
+    {
+        RELEASE_ASSERT_NOT_REACHED();
+        completionHandler(ShouldContinuePolicyCheck::No);
+    }
+    void redirectReceived(PlatformMediaResource&, ResourceRequest&&, const ResourceResponse&, CompletionHandler<void(ResourceRequest&&)>&& completionHandler) final
+    {
+        RELEASE_ASSERT_NOT_REACHED();
+        completionHandler({ });
+    }
+    void dataSent(PlatformMediaResource&, unsigned long long, unsigned long long) final
+    {
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+    void accessControlCheckFailed(PlatformMediaResource&, const ResourceError&) final
+    {
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+
+    bool shouldCacheResponse(PlatformMediaResource&, const ResourceResponse&) final
+    {
+        ASSERT(isMainThread());
+        return false;
+    }
+
+    void dataReceived(PlatformMediaResource&, const char* bytes, int length) final
+    {
+        ASSERT(isMainThread());
+        if (!m_generator)
+            return;
+        auto* data = ""
+        if (!data)
+            return;
+        data->buffer->append(bytes, length);
+        m_generator->giveResponseToTasksWithFinishedRanges(*data);
+    }
+
+    void loadFailed(PlatformMediaResource&, const ResourceError& error) final
+    {
+        ASSERT(isMainThread());
+        if (!m_generator)
+            return;
+        auto data = ""
+        if (!data)
+            return;
+        for (auto& task : data->taskData.keys())
+            [task resource:nullptr loadFailedWithError:error];
+    }
+
+    void loadFinished(PlatformMediaResource&, const NetworkLoadMetrics&) final
+    {
+        ASSERT(isMainThread());
+        if (!m_generator)
+            return;
+        auto* data = ""
+        if (!data)
+            return;
+        data->successfullyFinishedLoading = Data::SuccessfullyFinishedLoading::Yes;
+        data->resource = nullptr;
+        m_generator->giveResponseToTasksWithFinishedRanges(*data);
+    }
+    
+    WeakPtr<RangeResponseGenerator> m_generator;
+    const String m_urlString;
+};
+
+bool RangeResponseGenerator::willSynthesizeRangeResponses(WebCoreNSURLSessionDataTask *task, PlatformMediaResource& resource, const ResourceResponse& response)
+{
+    ASSERT(isMainThread());
+    NSURLRequest *originalRequest = task.originalRequest;
+    if (!originalRequest.URL)
+        return false;
+    if (response.httpStatusCode() != 200)
+        return false;
+    if (!response.httpHeaderField(HTTPHeaderName::ContentRange).isEmpty())
+        return false;
+
+    auto parsedRequestRange = ParsedRequestRange::parse([originalRequest valueForHTTPHeaderField:@"Range"]);
+    if (!parsedRequestRange)
+        return false;
+
+    resource.setClient(adoptRef(new MediaResourceClient(*this, originalRequest.URL)));
+
+    m_map.ensure(originalRequest.URL.absoluteString, [&] {
+        return makeUnique<Data>(response, resource);
+    }).iterator->value->taskData.add(task, makeUnique<Data::TaskData>(WTFMove(*parsedRequestRange)));
+
+    return true;
+}
+
+} // namespace WebCore

Modified: trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.h (272907 => 272908)


--- trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.h	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.h	2021-02-16 16:59:26 UTC (rev 272908)
@@ -23,8 +23,10 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
+#import "RangeResponseGenerator.h"
 #import "SecurityOrigin.h"
 #import <Foundation/NSURLSession.h>
+#import <wtf/CompletionHandler.h>
 #import <wtf/HashSet.h>
 #import <wtf/Lock.h>
 #import <wtf/OSObjectPtr.h>
@@ -41,12 +43,17 @@
 
 namespace WebCore {
 class CachedResourceRequest;
+class NetworkLoadMetrics;
 class PlatformMediaResource;
 class PlatformMediaResourceLoader;
+class ResourceError;
+class ResourceRequest;
+class ResourceResponse;
 class WebCoreNSURLSessionDataTaskClient;
+enum class ShouldContinuePolicyCheck : bool;
 }
 
-enum class WebCoreNSURLSessionCORSAccessCheckResults {
+enum class WebCoreNSURLSessionCORSAccessCheckResults : uint8_t {
     Unknown,
     Pass,
     Fail,
@@ -67,6 +74,7 @@
     NSUInteger _nextTaskIdentifier;
     OSObjectPtr<dispatch_queue_t> _internalQueue;
     WebCoreNSURLSessionCORSAccessCheckResults _corsResults;
+    WebCore::RangeResponseGenerator _rangeResponseGenerator;
 }
 - (id)initWithResourceLoader:(WebCore::PlatformMediaResourceLoader&)loader delegate:(id<NSURLSessionTaskDelegate>)delegate delegateQueue:(NSOperationQueue*)queue;
 @property (readonly, retain) NSOperationQueue *delegateQueue;
@@ -110,7 +118,7 @@
 @end
 
 @interface WebCoreNSURLSessionDataTask : NSObject {
-    __unsafe_unretained WebCoreNSURLSession *_session;
+    WeakObjCPtr<WebCoreNSURLSession> _session;
     RefPtr<WebCore::PlatformMediaResource> _resource;
     RetainPtr<NSURLResponse> _response;
     NSUInteger _taskIdentifier;
@@ -143,4 +151,15 @@
 - (void)resume;
 @end
 
+@interface WebCoreNSURLSessionDataTask (WebKitInternal)
+- (void)resource:(nullable WebCore::PlatformMediaResource*)resource sentBytes:(unsigned long long)bytesSent totalBytesToBeSent:(unsigned long long)totalBytesToBeSent;
+- (void)resource:(nullable WebCore::PlatformMediaResource*)resource receivedResponse:(const WebCore::ResourceResponse&)response completionHandler:(CompletionHandler<void(WebCore::ShouldContinuePolicyCheck)>&&)completionHandler;
+- (BOOL)resource:(nullable WebCore::PlatformMediaResource*)resource shouldCacheResponse:(const WebCore::ResourceResponse&)response;
+- (void)resource:(nullable WebCore::PlatformMediaResource*)resource receivedData:(const char*)data length:(int)length;
+- (void)resource:(nullable WebCore::PlatformMediaResource*)resource receivedRedirect:(const WebCore::ResourceResponse&)response request:(WebCore::ResourceRequest&&)request completionHandler:(CompletionHandler<void(WebCore::ResourceRequest&&)>&&)completionHandler;
+- (void)resource:(nullable WebCore::PlatformMediaResource*)resource accessControlCheckFailedWithError:(const WebCore::ResourceError&)error;
+- (void)resource:(nullable WebCore::PlatformMediaResource*)resource loadFailedWithError:(const WebCore::ResourceError&)error;
+- (void)resourceFinished:(nullable WebCore::PlatformMediaResource*)resource metrics:(const WebCore::NetworkLoadMetrics&)metrics;
+@end
+
 NS_ASSUME_NONNULL_END

Modified: trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.mm (272907 => 272908)


--- trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.mm	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSession.mm	2021-02-16 16:59:26 UTC (rev 272908)
@@ -27,10 +27,12 @@
 #import "WebCoreNSURLSession.h"
 
 #import "CachedResourceRequest.h"
+#import "ParsedRequestRange.h"
 #import "PlatformMediaResourceLoader.h"
 #import "SubresourceLoader.h"
 #import <wtf/BlockPtr.h>
 #import <wtf/CompletionHandler.h>
+#import <wtf/WeakObjCPtr.h>
 #import <wtf/cocoa/VectorCocoa.h>
 
 using namespace WebCore;
@@ -203,24 +205,15 @@
 - (void)addDelegateOperation:(Function<void()>&&)operation;
 - (void)task:(WebCoreNSURLSessionDataTask *)task didReceiveCORSAccessCheckResult:(BOOL)result;
 - (void)task:(WebCoreNSURLSessionDataTask *)task didReceiveResponseFromOrigin:(Ref<WebCore::SecurityOrigin>&&)origin;
+- (WebCore::RangeResponseGenerator&)rangeResponseGenerator;
 @end
 
 @interface WebCoreNSURLSessionDataTask ()
 - (id)initWithSession:(WebCoreNSURLSession *)session identifier:(NSUInteger)identifier request:(NSURLRequest *)request;
-- (id)initWithSession:(WebCoreNSURLSession *)session identifier:(NSUInteger)identifier URL:(NSURL *)url;
 - (void)_restart;
 - (void)_cancel;
-- (void)_finish;
 @property (assign) WebCoreNSURLSession * _Nullable session;
 
-- (void)resource:(PlatformMediaResource&)resource sentBytes:(unsigned long long)bytesSent totalBytesToBeSent:(unsigned long long)totalBytesToBeSent;
-- (void)resource:(PlatformMediaResource&)resource receivedResponse:(const ResourceResponse&)response completionHandler:(CompletionHandler<void(ShouldContinuePolicyCheck)>&&)completionHandler;
-- (BOOL)resource:(PlatformMediaResource&)resource shouldCacheResponse:(const ResourceResponse&)response;
-- (void)resource:(PlatformMediaResource&)resource receivedData:(const char*)data length:(int)length;
-- (void)resource:(PlatformMediaResource&)resource receivedRedirect:(const ResourceResponse&)response request:(ResourceRequest&&)request completionHandler:(CompletionHandler<void(ResourceRequest&&)>&&)completionHandler;
-- (void)resource:(PlatformMediaResource&)resource accessControlCheckFailedWithError:(const ResourceError&)error;
-- (void)resource:(PlatformMediaResource&)resource loadFailedWithError:(const ResourceError&)error;
-- (void)resourceFinished:(PlatformMediaResource&)resource metrics:(const NetworkLoadMetrics&)metrics;
 @end
 
 NS_ASSUME_NONNULL_END
@@ -311,6 +304,11 @@
     _origins.add(WTFMove(origin));
 }
 
+- (WebCore::RangeResponseGenerator&)rangeResponseGenerator
+{
+    return _rangeResponseGenerator;
+}
+
 #pragma mark - NSURLSession API
 @dynamic delegate;
 - (__nullable id<NSURLSessionDelegate>)delegate
@@ -455,15 +453,7 @@
 
 - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url
 {
-    if (_invalidated)
-        return nil;
-
-    WebCoreNSURLSessionDataTask *task = [[WebCoreNSURLSessionDataTask alloc] initWithSession:self identifier:_nextTaskIdentifier++ URL:url];
-    {
-        Locker<Lock> locker(_dataTasksLock);
-        _dataTasks.add(task);
-    }
-    return (NSURLSessionDataTask *)[task autorelease];
+    return [self dataTaskWithRequest:[NSURLRequest requestWithURL:url]];
 }
 
 - (void)sendH2Ping:(NSURL *)url pongHandler:(void (^)(NSError *error, NSTimeInterval interval))pongHandler
@@ -571,7 +561,7 @@
 
 private:
     Lock m_taskLock;
-    WebCoreNSURLSessionDataTask *m_task;
+    WeakObjCPtr<WebCoreNSURLSessionDataTask> m_task;
 };
 
 void WebCoreNSURLSessionDataTaskClient::clearTask()
@@ -586,16 +576,17 @@
     if (!m_task)
         return;
 
-    [m_task resource:resource sentBytes:bytesSent totalBytesToBeSent:totalBytesToBeSent];
+    [m_task resource:&resource sentBytes:bytesSent totalBytesToBeSent:totalBytesToBeSent];
 }
 
 void WebCoreNSURLSessionDataTaskClient::responseReceived(PlatformMediaResource& resource, const ResourceResponse& response, CompletionHandler<void(ShouldContinuePolicyCheck)>&& completionHandler)
 {
+    auto protectedThis = makeRef(*this);
     LockHolder locker(m_taskLock);
     if (!m_task)
         return completionHandler(ShouldContinuePolicyCheck::No);
 
-    [m_task resource:resource receivedResponse:response completionHandler:WTFMove(completionHandler)];
+    [m_task resource:&resource receivedResponse:response completionHandler:WTFMove(completionHandler)];
 }
 
 bool WebCoreNSURLSessionDataTaskClient::shouldCacheResponse(PlatformMediaResource& resource, const ResourceResponse& response)
@@ -604,7 +595,7 @@
     if (!m_task)
         return false;
 
-    return [m_task resource:resource shouldCacheResponse:response];
+    return [m_task resource:&resource shouldCacheResponse:response];
 }
 
 void WebCoreNSURLSessionDataTaskClient::dataReceived(PlatformMediaResource& resource, const char* data, int length)
@@ -613,7 +604,7 @@
     if (!m_task)
         return;
 
-    [m_task resource:resource receivedData:data length:length];
+    [m_task resource:&resource receivedData:data length:length];
 }
 
 void WebCoreNSURLSessionDataTaskClient::redirectReceived(PlatformMediaResource& resource, ResourceRequest&& request, const ResourceResponse& response, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
@@ -622,7 +613,7 @@
     if (!m_task)
         return;
 
-    [m_task resource:resource receivedRedirect:response request:WTFMove(request) completionHandler: [completionHandler = WTFMove(completionHandler)] (auto&& request) mutable {
+    [m_task resource:&resource receivedRedirect:response request:WTFMove(request) completionHandler: [completionHandler = WTFMove(completionHandler)] (auto&& request) mutable {
         callOnMainThread([request = request.isolatedCopy(), completionHandler = WTFMove(completionHandler)] () mutable {
             completionHandler(WTFMove(request));
         });
@@ -635,7 +626,7 @@
     if (!m_task)
         return;
 
-    [m_task resource:resource accessControlCheckFailedWithError:error];
+    [m_task resource:&resource accessControlCheckFailedWithError:error];
 }
 
 void WebCoreNSURLSessionDataTaskClient::loadFailed(PlatformMediaResource& resource, const ResourceError& error)
@@ -644,7 +635,7 @@
     if (!m_task)
         return;
 
-    [m_task resource:resource loadFailedWithError:error];
+    [m_task resource:&resource loadFailedWithError:error];
 }
 
 void WebCoreNSURLSessionDataTaskClient::loadFinished(PlatformMediaResource& resource, const NetworkLoadMetrics& metrics)
@@ -653,7 +644,7 @@
     if (!m_task)
         return;
 
-    [m_task resourceFinished:resource metrics:metrics];
+    [m_task resourceFinished:&resource metrics:metrics];
 }
 
 }
@@ -661,17 +652,6 @@
 #pragma mark - WebCoreNSURLSessionDataTask
 
 @implementation WebCoreNSURLSessionDataTask
-- (id)initWithSession:(WebCoreNSURLSession *)session identifier:(NSUInteger)identifier URL:(NSURL *)url
-{
-    self.taskIdentifier = identifier;
-    self.session = session;
-    self.state = NSURLSessionTaskStateSuspended;
-    self.priority = NSURLSessionTaskPriorityDefault;
-    self.originalRequest = self.currentRequest = [NSURLRequest requestWithURL:url];
-
-    return self;
-}
-
 - (id)initWithSession:(WebCoreNSURLSession *)session identifier:(NSUInteger)identifier request:(NSURLRequest *)request
 {
     self.taskIdentifier = identifier;
@@ -708,9 +688,12 @@
 
     [self _cancel];
 
+    if ([self.session rangeResponseGenerator].willHandleRequest(self, self.originalRequest))
+        return;
+
     _resource = self.session.loader.requestResource(self.originalRequest, PlatformMediaResourceLoader::LoadOption::DisallowCaching);
     if (_resource)
-        _resource->setClient(makeUnique<WebCoreNSURLSessionDataTaskClient>(self));
+        _resource->setClient(adoptRef(*new WebCoreNSURLSessionDataTaskClient(self)));
 }
 
 - (void)_cancel
@@ -723,15 +706,7 @@
     }
 }
 
-- (void)_finish
-{
-    ASSERT(isMainThread());
-    if (_resource)
-        [self resourceFinished:*_resource metrics:NetworkLoadMetrics { }];
-}
-
 #pragma mark - NSURLSession API
-@synthesize session = _session;
 @synthesize taskIdentifier = _taskIdentifier;
 @synthesize originalRequest = _originalRequest;
 @synthesize currentRequest = _currentRequest;
@@ -744,6 +719,16 @@
 @synthesize taskDescription = _taskDescription;
 @synthesize priority = _priority;
 
+- (WebCoreNSURLSession *)session
+{
+    return _session.get().get();
+}
+
+- (void)setSession:(WebCoreNSURLSession *)session
+{
+    _session = session;
+}
+
 - (NSURLResponse *)response
 {
     return _response.get();
@@ -751,10 +736,12 @@
 
 - (void)cancel
 {
+    if (self.state == NSURLSessionTaskStateCompleted)
+        return;
     self.state = NSURLSessionTaskStateCanceling;
-    callOnMainThread([protectedSelf = RetainPtr<WebCoreNSURLSessionDataTask>(self)] {
+    callOnMainThread([protectedSelf = retainPtr(self)] {
         [protectedSelf _cancel];
-        [protectedSelf _finish];
+        [protectedSelf _resource:nullptr loadFinishedWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:nil] metrics:NetworkLoadMetrics { }];
     });
 }
 
@@ -806,23 +793,28 @@
 
 #pragma mark - PlatformMediaResourceClient callbacks
 
-- (void)resource:(PlatformMediaResource&)resource sentBytes:(unsigned long long)bytesSent totalBytesToBeSent:(unsigned long long)totalBytesToBeSent
+- (void)resource:(PlatformMediaResource*)resource sentBytes:(unsigned long long)bytesSent totalBytesToBeSent:(unsigned long long)totalBytesToBeSent
 {
-    ASSERT_UNUSED(resource, &resource == _resource);
+    ASSERT_UNUSED(resource, !resource || resource == _resource);
     UNUSED_PARAM(bytesSent);
     UNUSED_PARAM(totalBytesToBeSent);
     // No-op.
 }
 
-- (void)resource:(PlatformMediaResource&)resource receivedResponse:(const ResourceResponse&)response completionHandler:(CompletionHandler<void(ShouldContinuePolicyCheck)>&&)completionHandler
+- (void)resource:(PlatformMediaResource*)resource receivedResponse:(const ResourceResponse&)response completionHandler:(CompletionHandler<void(ShouldContinuePolicyCheck)>&&)completionHandler
 {
     ASSERT(response.source() == ResourceResponse::Source::Network || response.source() == ResourceResponse::Source::DiskCache || response.source() == ResourceResponse::Source::DiskCacheAfterValidation || response.source() == ResourceResponse::Source::ServiceWorker);
-    ASSERT_UNUSED(resource, &resource == _resource);
+    ASSERT_UNUSED(resource, !resource || resource == _resource);
     ASSERT(isMainThread());
     [self.session task:self didReceiveResponseFromOrigin:SecurityOrigin::create(response.url())];
-    [self.session task:self didReceiveCORSAccessCheckResult:resource.didPassAccessControlCheck()];
+    // FIXME: Think about this and make sure it's safe.
+    [self.session task:self didReceiveCORSAccessCheckResult:resource ? resource->didPassAccessControlCheck() : YES];
     self.countOfBytesExpectedToReceive = response.expectedContentLength();
-    RetainPtr<NSURLResponse> strongResponse { response.nsURLResponse() };
+    RetainPtr<NSURLResponse> strongResponse = response.nsURLResponse();
+
+    if (resource && self.session && [self.session rangeResponseGenerator].willSynthesizeRangeResponses(self, *resource, response))
+        return completionHandler(ShouldContinuePolicyCheck::Yes);
+    
     RetainPtr<WebCoreNSURLSessionDataTask> strongSelf { self };
     if (!self.session)
         return completionHandler(ShouldContinuePolicyCheck::No);
@@ -850,9 +842,9 @@
     }];
 }
 
-- (BOOL)resource:(PlatformMediaResource&)resource shouldCacheResponse:(const ResourceResponse&)response
+- (BOOL)resource:(PlatformMediaResource*)resource shouldCacheResponse:(const ResourceResponse&)response
 {
-    ASSERT_UNUSED(resource, &resource == _resource);
+    ASSERT_UNUSED(resource, !resource || resource == _resource);
 
     ASSERT(isMainThread());
 
@@ -860,9 +852,9 @@
     return response.httpHeaderField(HTTPHeaderName::ContentRange).isEmpty();
 }
 
-- (void)resource:(PlatformMediaResource&)resource receivedData:(const char*)data length:(int)length
+- (void)resource:(PlatformMediaResource*)resource receivedData:(const char*)data length:(int)length
 {
-    ASSERT_UNUSED(resource, &resource == _resource);
+    ASSERT_UNUSED(resource, !resource || resource == _resource);
     RetainPtr<NSData> nsData = adoptNS([[NSData alloc] initWithBytes:data length:length]);
     RetainPtr<WebCoreNSURLSessionDataTask> strongSelf { self };
     [self.session addDelegateOperation:[strongSelf, length, nsData] {
@@ -873,9 +865,9 @@
     }];
 }
 
-- (void)resource:(PlatformMediaResource&)resource receivedRedirect:(const ResourceResponse&)response request:(ResourceRequest&&)request completionHandler:(CompletionHandler<void(ResourceRequest&&)>&&)completionHandler
+- (void)resource:(PlatformMediaResource*)resource receivedRedirect:(const ResourceResponse&)response request:(ResourceRequest&&)request completionHandler:(CompletionHandler<void(ResourceRequest&&)>&&)completionHandler
 {
-    ASSERT_UNUSED(resource, &resource == _resource);
+    ASSERT_UNUSED(resource, !resource || resource == _resource);
     [self.session addDelegateOperation:[strongSelf = retainPtr(self), response = retainPtr(response.nsURLResponse()), request = request.isolatedCopy(), completionHandler = WTFMove(completionHandler)] () mutable {
         if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
             ASSERT_NOT_REACHED();
@@ -905,9 +897,9 @@
     }];
 }
 
-- (void)_resource:(PlatformMediaResource&)resource loadFinishedWithError:(NSError *)error metrics:(const NetworkLoadMetrics&)metrics
+- (void)_resource:(PlatformMediaResource*)resource loadFinishedWithError:(NSError *)error metrics:(const NetworkLoadMetrics&)metrics
 {
-    ASSERT_UNUSED(resource, &resource == _resource);
+    ASSERT_UNUSED(resource, !resource || resource == _resource);
     if (self.state == NSURLSessionTaskStateCompleted)
         return;
     self.state = NSURLSessionTaskStateCompleted;
@@ -930,17 +922,17 @@
     }];
 }
 
-- (void)resource:(PlatformMediaResource&)resource accessControlCheckFailedWithError:(const ResourceError&)error
+- (void)resource:(PlatformMediaResource*)resource accessControlCheckFailedWithError:(const ResourceError&)error
 {
     [self _resource:resource loadFinishedWithError:error.nsError() metrics:NetworkLoadMetrics { }];
 }
 
-- (void)resource:(PlatformMediaResource&)resource loadFailedWithError:(const ResourceError&)error
+- (void)resource:(PlatformMediaResource*)resource loadFailedWithError:(const ResourceError&)error
 {
     [self _resource:resource loadFinishedWithError:error.nsError() metrics:NetworkLoadMetrics { }];
 }
 
-- (void)resourceFinished:(PlatformMediaResource&)resource metrics:(const NetworkLoadMetrics&)metrics
+- (void)resourceFinished:(PlatformMediaResource*)resource metrics:(const NetworkLoadMetrics&)metrics
 {
     [self _resource:resource loadFinishedWithError:nil metrics:metrics];
 }

Added: trunk/Source/WebCore/platform/network/cocoa/WebCoreNSURLSessionInternal.h ( => )


Modified: trunk/Source/WebKit/ChangeLog
===================================================================
--- trunk/Source/WebKit/ChangeLog	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebKit/ChangeLog	2021-02-16 16:59:26 UTC (rev 272908)
@@ -1,3 +1,13 @@
+2021-02-16  Alex Christensen  <achristen...@webkit.org>
+
+        Synthesize range responses if needed in WebCoreNSURLSession
+        https://bugs.webkit.org/show_bug.cgi?id=221072
+
+        Reviewed by Geoff Garen.
+
+        * WebProcess/GPU/media/MediaPlayerPrivateRemote.cpp:
+        (WebKit::MediaPlayerPrivateRemote::requestResource):
+
 2021-02-16  Kimmo Kinnunen  <kkinnu...@apple.com>
 
         ThreadMessageReceiverRefCounted subclasses are accessed in non-thread-safe way

Modified: trunk/Source/WebKit/WebProcess/GPU/media/MediaPlayerPrivateRemote.cpp (272907 => 272908)


--- trunk/Source/WebKit/WebProcess/GPU/media/MediaPlayerPrivateRemote.cpp	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Source/WebKit/WebProcess/GPU/media/MediaPlayerPrivateRemote.cpp	2021-02-16 16:59:26 UTC (rev 272908)
@@ -1180,7 +1180,7 @@
         return;
     }
     // PlatformMediaResource owns the PlatformMediaResourceClient
-    resource->setClient(makeUnique<RemoteMediaResourceProxy>(connection(), *resource, remoteMediaResourceIdentifier));
+    resource->setClient(adoptRef(*new RemoteMediaResourceProxy(connection(), *resource, remoteMediaResourceIdentifier)));
     m_mediaResources.add(remoteMediaResourceIdentifier, WTFMove(resource));
 
     completionHandler();

Modified: trunk/Tools/ChangeLog (272907 => 272908)


--- trunk/Tools/ChangeLog	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Tools/ChangeLog	2021-02-16 16:59:26 UTC (rev 272908)
@@ -1,3 +1,29 @@
+2021-02-16  Alex Christensen  <achristen...@webkit.org>
+
+        Synthesize range responses if needed in WebCoreNSURLSession
+        https://bugs.webkit.org/show_bug.cgi?id=221072
+
+        Reviewed by Geoff Garen.
+
+        * TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp:
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm:
+        (TestWebKitAPI::clientCertServer):
+        * TestWebKitAPI/Tests/WebKitCocoa/MediaLoading.mm:
+        (TestWebKitAPI::testVideoBytes):
+        (TestWebKitAPI::runVideoTest):
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/cocoa/HTTPServer.h:
+        (TestWebKitAPI::HTTPResponse::HTTPResponse):
+        (TestWebKitAPI::HTTPServer::HTTPResponse::HTTPResponse): Deleted.
+        * TestWebKitAPI/cocoa/HTTPServer.mm:
+        (TestWebKitAPI::HTTPServer::RequestData::RequestData):
+        (TestWebKitAPI::appendToVector):
+        (TestWebKitAPI::HTTPServer::parsePath):
+        (TestWebKitAPI::HTTPServer::respondToRequests):
+        (TestWebKitAPI::HTTPResponse::bodyFromString):
+        (TestWebKitAPI::HTTPResponse::serialize):
+
 2021-02-15  John Wilander  <wilan...@apple.com>
 
         PCM: Add high entropy attributionSourceNonce attribute to anchor tags

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp (272907 => 272908)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp	2021-02-16 16:59:26 UTC (rev 272908)
@@ -26,6 +26,7 @@
 #include "config.h"
 
 #include <WebCore/ParsedContentRange.h>
+#include <WebCore/ParsedRequestRange.h>
 #include <wtf/text/WTFString.h>
 
 using namespace WebCore;
@@ -95,4 +96,31 @@
     ASSERT_STREQ("", ParsedContentRange().headerValue().utf8().data());
 }
 
+TEST(WebCore, ParsedRequestRange)
+{
+    Vector<String> failureCases {
+        { },
+        "",
+        "abc",
+        "bytes=",
+        "bytes=-",
+        "bytes=abc-",
+        "bytes=1-abc",
+        "bytes=2-1",
+        "bytes=1-",
+        "bytes=-1",
+        "bytes=1-999999999999999999999999"
+    };
+    for (const auto& input : failureCases)
+        EXPECT_EQ(WTF::nullopt, ParsedRequestRange::parse(input));
+
+    auto compare = [] (const String& input, Optional<size_t> begin, Optional<size_t> end) {
+        auto range = ParsedRequestRange::parse(input);
+        EXPECT_NE(WTF::nullopt, range);
+        
+    };
+    compare("bytes=1-1", 1, 1);
+    compare("bytes=1-2", 1, 2);
 }
+
+}

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm (272907 => 272908)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Challenge.mm	2021-02-16 16:59:26 UTC (rev 272908)
@@ -589,7 +589,7 @@
         { "/5.png", { longString } },
         { "/6.png", { longString } },
         { "/redirectToError", { 301, {{ "Location", "/error" }} } },
-        { "/error", { HTTPServer::HTTPResponse::TerminateConnection::Yes } },
+        { "/error", { HTTPResponse::TerminateConnection::Yes } },
     }, HTTPServer::Protocol::Https, [] (auto, auto, auto certificateAllowed) {
         certificateAllowed(true);
     });

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaLoading.mm (272907 => 272908)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaLoading.mm	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/MediaLoading.mm	2021-02-16 16:59:26 UTC (rev 272908)
@@ -27,6 +27,7 @@
 
 #import "HTTPServer.h"
 #import "PlatformUtilities.h"
+#import "TestUIDelegate.h"
 #import "TestWKWebView.h"
 #import <wtf/text/StringConcatenateNumbers.h>
 
@@ -116,6 +117,74 @@
     Util::run(&receivedMediaRequest);
 }
 
+const char* videoPlayTestHTML ="<script>"
+    "function createVideoElement() {"
+        "let video = document.createElement('video');"
+        "video.addEventListener('error', ()=>{alert('error')});"
+        "video.addEventListener('playing', ()=>{alert('playing')});"
+        "video.src='';"
+        "video.autoplay=1;"
+        "document.body.appendChild(video);"
+    "}"
+"</script>"
+"<body _onload_='createVideoElement()'></body>";
+
+static Vector<uint8_t> testVideoBytes()
+{
+    NSData *data = "" dataWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"test" withExtension:@"mp4" subdirectory:@"TestWebKitAPI.resources"]];
+    Vector<uint8_t> vector;
+    vector.append(static_cast<const uint8_t*>(data.bytes), data.length);
+    return vector;
 }
 
+static void runVideoTest(NSURLRequest *request, const char* expectedMessage = "playing")
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    configuration.get().mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration.get() addToWindow:YES]);
+    [webView loadRequest:request];
+    EXPECT_WK_STREQ([webView _test_waitForAlert], expectedMessage);
+}
+
+TEST(MediaLoading, RangeRequestSynthesisWithContentLength)
+{
+    HTTPServer server({
+        {"/", { videoPlayTestHTML }},
+        {"/video.mp4", { testVideoBytes() }}
+    });
+    runVideoTest(server.request());
+    EXPECT_EQ(server.totalRequests(), 2u);
+}
+
+TEST(MediaLoading, RangeRequestSynthesisWithoutContentLength)
+{
+    size_t totalRequests { 0 };
+    Function<void(Connection)> respondToRequests;
+    respondToRequests = [&] (Connection connection) {
+        connection.receiveHTTPRequest([&, connection] (Vector<char>&& request) {
+            auto sendResponse = [&, connection] (HTTPResponse response, HTTPResponse::IncludeContentLength includeContentLength) {
+                connection.send(response.serialize(includeContentLength), [&, connection] {
+                    respondToRequests(connection);
+                });
+            };
+            totalRequests++;
+            auto path = HTTPServer::parsePath(request);
+            if (path == "/")
+                sendResponse({ videoPlayTestHTML }, HTTPResponse::IncludeContentLength::Yes);
+            else if (path == "/video.mp4")
+                sendResponse(testVideoBytes(), HTTPResponse::IncludeContentLength::No);
+            else
+                ASSERT(path.isNull());
+        });
+    };
+
+    HTTPServer server([&](Connection connection) {
+        respondToRequests(connection);
+    });
+    runVideoTest(server.request(), "error");
+    EXPECT_EQ(totalRequests, 2u);
+}
+
+} // namespace TestWebKitAPI
+
 #endif // ENABLE(VIDEO) && USE(AVFOUNDATION)

Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h (272907 => 272908)


--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h	2021-02-16 16:59:26 UTC (rev 272908)
@@ -35,10 +35,10 @@
 namespace TestWebKitAPI {
 
 class Connection;
+struct HTTPResponse;
 
 class HTTPServer {
 public:
-    struct HTTPResponse;
     struct RequestData;
     enum class Protocol : uint8_t { Http, Https, HttpsWithLegacyTLS, Http2 };
     using CertificateVerifier = Function<void(sec_protocol_metadata_t, sec_trust_t, sec_protocol_verify_complete_t)>;
@@ -53,6 +53,7 @@
 
     static void respondWithOK(Connection);
     static void respondWithChallengeThenOK(Connection);
+    static String parsePath(const Vector<char>& request);
 
 private:
     static RetainPtr<nw_parameters_t> listenerParameters(Protocol, CertificateVerifier&&, RetainPtr<SecIdentityRef>&&, Optional<uint16_t> port);
@@ -81,18 +82,20 @@
     RetainPtr<nw_connection_t> m_connection;
 };
 
-struct HTTPServer::HTTPResponse {
+struct HTTPResponse {
     enum class TerminateConnection { No, Yes };
-    
+
+    HTTPResponse(Vector<uint8_t>&& body)
+        : body(WTFMove(body)) { }
     HTTPResponse(const String& body)
-        : body(body) { }
-    HTTPResponse(HashMap<String, String>&& headerFields, String&& body)
+        : body(bodyFromString(body)) { }
+    HTTPResponse(HashMap<String, String>&& headerFields, const String& body)
         : headerFields(WTFMove(headerFields))
-        , body(WTFMove(body)) { }
-    HTTPResponse(unsigned statusCode, HashMap<String, String>&& headerFields = { }, String&& body = { })
+        , body(bodyFromString(body)) { }
+    HTTPResponse(unsigned statusCode, HashMap<String, String>&& headerFields = { }, const String& body = { })
         : statusCode(statusCode)
         , headerFields(WTFMove(headerFields))
-        , body(WTFMove(body)) { }
+        , body(bodyFromString(body)) { }
     HTTPResponse(TerminateConnection terminateConnection)
         : terminateConnection(terminateConnection) { }
 
@@ -101,10 +104,14 @@
     HTTPResponse() = default;
     HTTPResponse& operator=(const HTTPResponse&) = default;
     HTTPResponse& operator=(HTTPResponse&&) = default;
-    
+
+    enum class IncludeContentLength : bool { No, Yes };
+    Vector<uint8_t> serialize(IncludeContentLength = IncludeContentLength::Yes);
+    static Vector<uint8_t> bodyFromString(const String&);
+
     unsigned statusCode { 200 };
     HashMap<String, String> headerFields;
-    String body;
+    Vector<uint8_t> body;
     TerminateConnection terminateConnection { TerminateConnection::No };
 };
 

Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm (272907 => 272908)


--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm	2021-02-16 16:48:00 UTC (rev 272907)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm	2021-02-16 16:59:26 UTC (rev 272908)
@@ -38,8 +38,8 @@
 
 struct HTTPServer::RequestData : public ThreadSafeRefCounted<RequestData, WTF::DestructionThread::MainRunLoop> {
     RequestData(std::initializer_list<std::pair<String, HTTPResponse>> responses)
-    : requestMap([](std::initializer_list<std::pair<String, HTTPServer::HTTPResponse>> list) {
-        HashMap<String, HTTPServer::HTTPResponse> map;
+    : requestMap([](std::initializer_list<std::pair<String, HTTPResponse>> list) {
+        HashMap<String, HTTPResponse> map;
         for (auto& pair : list)
             map.add(pair.first, pair.second);
         return map;
@@ -212,6 +212,31 @@
     return request;
 }
 
+static void appendUTF8ToVector(Vector<uint8_t>& vector, const String& string)
+{
+    auto utf8 = string.utf8();
+    vector.append(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
+}
+
+String HTTPServer::parsePath(const Vector<char>& request)
+{
+    if (!request.size())
+        return { };
+    const char* getPathPrefix = "GET ";
+    const char* postPathPrefix = "POST ";
+    const char* pathSuffix = " HTTP/1.1\r\n";
+    const char* pathEnd = strnstr(request.data(), pathSuffix, request.size());
+    ASSERT_WITH_MESSAGE(pathEnd, "HTTPServer assumes request is HTTP 1.1");
+    size_t pathPrefixLength = 0;
+    if (!memcmp(request.data(), getPathPrefix, strlen(getPathPrefix)))
+        pathPrefixLength = strlen(getPathPrefix);
+    else if (!memcmp(request.data(), postPathPrefix, strlen(postPathPrefix)))
+        pathPrefixLength = strlen(postPathPrefix);
+    ASSERT_WITH_MESSAGE(pathPrefixLength, "HTTPServer assumes request is GET or POST");
+    size_t pathLength = pathEnd - request.data() - pathPrefixLength;
+    return String(request.data() + pathPrefixLength, pathLength);
+}
+
 void HTTPServer::respondToRequests(Connection connection, Ref<RequestData> requestData)
 {
     connection.receiveHTTPRequest([connection, requestData] (Vector<char>&& request) mutable {
@@ -219,32 +244,14 @@
             return;
         requestData->requestCount++;
 
-        const char* getPathPrefix = "GET ";
-        const char* postPathPrefix = "POST ";
-        const char* pathSuffix = " HTTP/1.1\r\n";
-        const char* pathEnd = strnstr(request.data(), pathSuffix, request.size());
-        ASSERT_WITH_MESSAGE(pathEnd, "HTTPServer assumes request is HTTP 1.1");
-        size_t pathPrefixLength = 0;
-        if (!memcmp(request.data(), getPathPrefix, strlen(getPathPrefix)))
-            pathPrefixLength = strlen(getPathPrefix);
-        else if (!memcmp(request.data(), postPathPrefix, strlen(postPathPrefix)))
-            pathPrefixLength = strlen(postPathPrefix);
-        ASSERT_WITH_MESSAGE(pathPrefixLength, "HTTPServer assumes request is GET or POST");
-        size_t pathLength = pathEnd - request.data() - pathPrefixLength;
-        String path(request.data() + pathPrefixLength, pathLength);
+        auto path = parsePath(request);
         ASSERT_WITH_MESSAGE(requestData->requestMap.contains(path), "This HTTPServer does not know how to respond to a request for %s", path.utf8().data());
 
         auto response = requestData->requestMap.get(path);
         if (response.terminateConnection == HTTPResponse::TerminateConnection::Yes)
             return connection.terminate();
-        StringBuilder responseBuilder;
-        responseBuilder.append("HTTP/1.1 ", response.statusCode, ' ', statusText(response.statusCode), "\r\n");
-        responseBuilder.append("Content-Length: ", response.body.length(), "\r\n");
-        for (auto& pair : response.headerFields)
-            responseBuilder.append(pair.key, ": ", pair.value, "\r\n");
-        responseBuilder.append("\r\n");
-        responseBuilder.append(response.body);
-        connection.send(responseBuilder.toString(), [connection, requestData] {
+
+        connection.send(response.serialize(), [connection, requestData] {
             respondToRequests(connection, requestData);
         });
     });
@@ -334,6 +341,29 @@
     m_connection = nullptr;
 }
 
+Vector<uint8_t> HTTPResponse::bodyFromString(const String& string)
+{
+    Vector<uint8_t> vector;
+    appendUTF8ToVector(vector, string);
+    return vector;
+}
+
+Vector<uint8_t> HTTPResponse::serialize(IncludeContentLength includeContentLength)
+{
+    StringBuilder responseBuilder;
+    responseBuilder.append("HTTP/1.1 ", statusCode, ' ', statusText(statusCode), "\r\n");
+    if (includeContentLength == IncludeContentLength::Yes)
+        responseBuilder.append("Content-Length: ", body.size(), "\r\n");
+    for (auto& pair : headerFields)
+        responseBuilder.append(pair.key, ": ", pair.value, "\r\n");
+    responseBuilder.append("\r\n");
+    
+    Vector<uint8_t> bytesToSend;
+    appendUTF8ToVector(bytesToSend, responseBuilder.toString());
+    bytesToSend.appendVector(body);
+    return bytesToSend;
+}
+
 void H2::Connection::send(Frame&& frame, CompletionHandler<void()>&& completionHandler) const
 {
     auto frameType = frame.type();
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to