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();