Diff
Modified: trunk/Source/WTF/ChangeLog (263878 => 263879)
--- trunk/Source/WTF/ChangeLog 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Source/WTF/ChangeLog 2020-07-03 02:01:21 UTC (rev 263879)
@@ -1,3 +1,14 @@
+2020-07-02 Alex Christensen <achristen...@webkit.org>
+
+ Add testing infrastructure and SPI declaration for HTTP/2 ping
+ https://bugs.webkit.org/show_bug.cgi?id=213913
+
+ Reviewed by Jer Noble.
+
+ This is work towards rdar://problem/64495827
+
+ * wtf/PlatformHave.h:
+
2020-07-01 Tim Horton <timothy_hor...@apple.com>
Upstream application accent color support
Modified: trunk/Source/WTF/wtf/PlatformHave.h (263878 => 263879)
--- trunk/Source/WTF/wtf/PlatformHave.h 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Source/WTF/wtf/PlatformHave.h 2020-07-03 02:01:21 UTC (rev 263879)
@@ -380,6 +380,14 @@
#define HAVE_ACCESSIBILITY_BUNDLES_PATH 1
#endif
+#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101600) \
+ || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000) \
+ || (PLATFORM(WATCHOS) && __WATCH_OS_VERSION_MIN_REQUIRED >= 70000) \
+ || (PLATFORM(APPLETV) && __TV_OS_VERSION_MIN_REQUIRED >= 140000) \
+ || (PLATFORM(MACCATALYST) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000)
+#define HAVE_PRECONNECT_PING 1
+#endif
+
#if PLATFORM(COCOA) && !(PLATFORM(MAC) && !(__MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101404))
#define HAVE_CFNETWORK_OVERRIDE_SESSION_COOKIE_ACCEPT_POLICY 1
#endif
Modified: trunk/Source/WebCore/PAL/ChangeLog (263878 => 263879)
--- trunk/Source/WebCore/PAL/ChangeLog 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Source/WebCore/PAL/ChangeLog 2020-07-03 02:01:21 UTC (rev 263879)
@@ -1,3 +1,12 @@
+2020-07-02 Alex Christensen <achristen...@webkit.org>
+
+ Add testing infrastructure and SPI declaration for HTTP/2 ping
+ https://bugs.webkit.org/show_bug.cgi?id=213913
+
+ Reviewed by Jer Noble.
+
+ * pal/spi/cf/CFNetworkSPI.h:
+
2020-07-01 Tim Horton <timothy_hor...@apple.com>
Upstream application accent color support
Modified: trunk/Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h (263878 => 263879)
--- trunk/Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h 2020-07-03 02:01:21 UTC (rev 263879)
@@ -67,6 +67,17 @@
#else // !PLATFORM(WIN) && !USE(APPLE_INTERNAL_SDK)
+#if HAVE(PRECONNECT_PING)
+@interface _NSHTTPConnectionInfo : NSObject
+- (void)sendPingWithReceiveHandler:(void (^)(NSError * _Nullable error, NSTimeInterval interval))pongHandler;
+@property (readonly) BOOL isValid;
+@end
+
+@interface NSURLSessionTask (HTTPConnectionInfo)
+- (void)getUnderlyingHTTPConnectionInfoWithCompletionHandler:(void (^)(_NSHTTPConnectionInfo *connectionInfo))completionHandler;
+@end
+#endif // HAVE(PRECONNECT_PING)
+
#if HAVE(LOGGING_PRIVACY_LEVEL)
typedef enum {
nw_context_privacy_level_public = 1,
Modified: trunk/Tools/ChangeLog (263878 => 263879)
--- trunk/Tools/ChangeLog 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Tools/ChangeLog 2020-07-03 02:01:21 UTC (rev 263879)
@@ -1,3 +1,33 @@
+2020-07-02 Alex Christensen <achristen...@webkit.org>
+
+ Add testing infrastructure and SPI declaration for HTTP/2 ping
+ https://bugs.webkit.org/show_bug.cgi?id=213913
+
+ Reviewed by Jer Noble.
+
+ * TestWebKitAPI/Tests/WebKitCocoa/Preconnect.mm:
+ (-[SessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):
+ (TestWebKitAPI::pingPong):
+ (TestWebKitAPI::TEST):
+ * TestWebKitAPI/cocoa/HTTPServer.h:
+ (TestWebKitAPI::H2::Frame::Frame):
+ (TestWebKitAPI::H2::Frame::type const):
+ (TestWebKitAPI::H2::Frame::flags const):
+ (TestWebKitAPI::H2::Frame::streamID const):
+ (TestWebKitAPI::H2::Frame::payload const):
+ (TestWebKitAPI::H2::Connection::create):
+ (TestWebKitAPI::H2::Connection::Connection):
+ * TestWebKitAPI/cocoa/HTTPServer.mm:
+ (TestWebKitAPI::HTTPServer::listenerParameters):
+ (TestWebKitAPI::dataFromVector):
+ (TestWebKitAPI::vectorFromData):
+ (TestWebKitAPI::HTTPServer::request const):
+ (TestWebKitAPI::Connection::receiveBytes const):
+ (TestWebKitAPI::Connection::receiveHTTPRequest const):
+ (TestWebKitAPI::Connection::send const):
+ (TestWebKitAPI::H2::Connection::send const):
+ (TestWebKitAPI::H2::Connection::receive const):
+
2020-07-02 Kate Cheney <katherine_che...@apple.com>
Custom URL schemes should be treated as app-bound
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Preconnect.mm (263878 => 263879)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Preconnect.mm 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Preconnect.mm 2020-07-03 02:01:21 UTC (rev 263879)
@@ -32,8 +32,21 @@
#import "TestNavigationDelegate.h"
#import "Utilities.h"
#import <WebKit/WKWebViewPrivate.h>
+#import <pal/spi/cf/CFNetworkSPI.h>
#import <wtf/RetainPtr.h>
+#if HAVE(PRECONNECT_PING)
+@interface SessionDelegate : NSObject <NSURLSessionDataDelegate>
+@end
+
+@implementation SessionDelegate
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
+{
+ completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
+}
+@end
+#endif
+
namespace TestWebKitAPI {
TEST(Preconnect, HTTP)
@@ -90,6 +103,76 @@
#endif
+#if HAVE(PRECONNECT_PING)
+static void pingPong(Ref<H2::Connection>&& connection, size_t* headersCount)
+{
+ connection->receive([connection, headersCount] (H2::Frame&& frame) mutable {
+ switch (frame.type()) {
+ case H2::Frame::Type::Headers:
+ ++*headersCount;
+ break;
+ case H2::Frame::Type::Settings:
+ case H2::Frame::Type::WindowUpdate:
+ // These frame types are ok for a preconnect task.
+ break;
+ case H2::Frame::Type::Ping:
+ {
+ // https://http2.github.io/http2-spec/#rfc.section.6.7
+ constexpr uint8_t ack = 0x1;
+ connection->send(H2::Frame(H2::Frame::Type::Ping, ack, frame.streamID(), frame.payload()));
+ }
+ break;
+ default:
+ // If anything else is sent by the client, preconnect is doing too much.
+ ASSERT_NOT_REACHED();
+ break;
+ }
+ pingPong(WTFMove(connection), headersCount);
+ });
}
+// This should remain disabled until rdar://problem/65055930 is integrated and bots are updated
+TEST(Preconnect, DISABLED_H2Ping)
+{
+ size_t headersCount = 0;
+ HTTPServer server([headersCount = &headersCount] (Connection tlsConnection) {
+ pingPong(H2::Connection::create(tlsConnection), headersCount);
+ }, HTTPServer::Protocol::Http2);
+
+ auto delegate = adoptNS([SessionDelegate new]);
+ NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:delegate.get() delegateQueue:[NSOperationQueue mainQueue]];
+ NSURLSessionDataTask *task = [session dataTaskWithRequest:server.request()];
+ task._preconnect = YES;
+ __block bool done = false;
+
+ [task getUnderlyingHTTPConnectionInfoWithCompletionHandler:^(_NSHTTPConnectionInfo *connectionInfo) {
+ EXPECT_TRUE(connectionInfo.isValid);
+ [connectionInfo sendPingWithReceiveHandler:^(NSError *error, NSTimeInterval interval) {
+ EXPECT_FALSE(error);
+ EXPECT_GT(interval, 0.0);
+ done = true;
+ }];
+ }];
+ [task resume];
+ Util::run(&done);
+
+ // Make sure the client doesn't send anything except Settings, WindowUpdate, and Ping.
+ // If Headers or Data were sent, then the preconnect wouldn't be preconnect.
+ usleep(100000);
+ Util::spinRunLoop(100);
+ EXPECT_EQ(headersCount, 0u);
+
+ NSURLSessionDataTask *task2 = [session dataTaskWithRequest:server.request()];
+ [task2 resume];
+ while (!headersCount)
+ Util::spinRunLoop();
+ EXPECT_EQ(headersCount, 1u);
+ usleep(100000);
+ Util::spinRunLoop(100);
+ EXPECT_EQ(headersCount, 1u);
+}
+#endif // HAVE(PRECONNECT_PING)
+
+}
+
#endif
Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h (263878 => 263879)
--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h 2020-07-03 02:01:21 UTC (rev 263879)
@@ -43,7 +43,7 @@
public:
struct HTTPResponse;
struct RequestData;
- enum class Protocol : uint8_t { Http, Https, HttpsWithLegacyTLS };
+ 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)>;
HTTPServer(std::initializer_list<std::pair<String, HTTPResponse>>, Protocol = Protocol::Http, CertificateVerifier&& = nullptr);
@@ -67,6 +67,9 @@
class Connection {
public:
void send(String&&, CompletionHandler<void()>&& = nullptr) const;
+ void send(Vector<uint8_t>&&, CompletionHandler<void()>&& = nullptr) const;
+ void send(RetainPtr<dispatch_data_t>&&, CompletionHandler<void()>&& = nullptr) const;
+ void receiveBytes(CompletionHandler<void(Vector<uint8_t>&&)>&&) const;
void receiveHTTPRequest(CompletionHandler<void(Vector<char>&&)>&&, Vector<char>&& buffer = { }) const;
void terminate() const;
@@ -105,6 +108,61 @@
TerminateConnection terminateConnection { TerminateConnection::No };
};
+namespace H2 {
+
+// https://http2.github.io/http2-spec/#rfc.section.4.1
+class Frame {
+public:
+
+ // https://http2.github.io/http2-spec/#rfc.section.6
+ enum class Type : uint8_t {
+ Data = ""
+ Headers = 0x1,
+ Priority = 0x2,
+ RSTStream = 0x3,
+ Settings = 0x4,
+ PushPromise = 0x5,
+ Ping = 0x6,
+ GoAway = 0x7,
+ WindowUpdate = 0x8,
+ Continuation = 0x9,
+ };
+
+ Frame(Type type, uint8_t flags, uint32_t streamID, Vector<uint8_t> payload)
+ : m_type(type)
+ , m_flags(flags)
+ , m_streamID(streamID)
+ , m_payload(WTFMove(payload)) { }
+
+ Type type() const { return m_type; }
+ uint8_t flags() const { return m_flags; }
+ uint32_t streamID() const { return m_streamID; }
+ const Vector<uint8_t>& payload() const { return m_payload; }
+
+private:
+ Type m_type;
+ uint8_t m_flags;
+ uint32_t m_streamID;
+ Vector<uint8_t> m_payload;
+};
+
+class Connection : public RefCounted<Connection> {
+public:
+ static Ref<Connection> create(TestWebKitAPI::Connection tlsConnection) { return adoptRef(*new Connection(tlsConnection)); }
+ void send(Frame&&, CompletionHandler<void()>&& = nullptr) const;
+ void receive(CompletionHandler<void(Frame&&)>&&) const;
+private:
+ Connection(TestWebKitAPI::Connection tlsConnection)
+ : m_tlsConnection(tlsConnection) { }
+
+ TestWebKitAPI::Connection m_tlsConnection;
+ mutable bool m_expectClientConnectionPreface { true };
+ mutable bool m_sendServerConnectionPreface { true };
+ mutable Vector<uint8_t> m_receiveBuffer;
+};
+
+} // namespace H2
+
} // namespace TestWebKitAPI
#endif // HAVE(NETWORK_FRAMEWORK)
Modified: trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm (263878 => 263879)
--- trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm 2020-07-03 01:05:35 UTC (rev 263878)
+++ trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm 2020-07-03 02:01:21 UTC (rev 263879)
@@ -66,6 +66,8 @@
verifier(metadata, trust, completion);
}).get(), dispatch_get_main_queue());
}
+ if (protocol == Protocol::Http2)
+ sec_protocol_options_add_tls_application_protocol(options.get(), "h2");
#else
UNUSED_PARAM(protocolOptions);
ASSERT_UNUSED(protocol, protocol != Protocol::HttpsWithLegacyTLS);
@@ -165,9 +167,19 @@
}));
}
-static Vector<char> vectorFromData(dispatch_data_t content)
+static RetainPtr<dispatch_data_t> dataFromVector(Vector<uint8_t>&& v)
{
- __block Vector<char> request;
+ auto bufferSize = v.size();
+ auto rawPointer = v.releaseBuffer().leakPtr();
+ return adoptNS(dispatch_data_create(rawPointer, bufferSize, dispatch_get_main_queue(), ^{
+ fastFree(rawPointer);
+ }));
+}
+
+static Vector<uint8_t> vectorFromData(dispatch_data_t content)
+{
+ ASSERT(content);
+ __block Vector<uint8_t> request;
dispatch_data_apply(content, ^bool(dispatch_data_t, size_t, const void* buffer, size_t size) {
request.append(static_cast<const char*>(buffer), size);
return true;
@@ -227,6 +239,7 @@
break;
case Protocol::Https:
case Protocol::HttpsWithLegacyTLS:
+ case Protocol::Http2:
format = @"https://127.0.0.1:%d%s";
break;
}
@@ -233,12 +246,19 @@
return [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:format, port(), path.utf8().data()]]];
}
-void Connection::receiveHTTPRequest(CompletionHandler<void(Vector<char>&&)>&& completionHandler, Vector<char>&& buffer) const
+void Connection::receiveBytes(CompletionHandler<void(Vector<uint8_t>&&)>&& completionHandler) const
{
- nw_connection_receive(m_connection.get(), 1, std::numeric_limits<uint32_t>::max(), makeBlockPtr([connection = *this, completionHandler = WTFMove(completionHandler), buffer = WTFMove(buffer)](dispatch_data_t content, nw_content_context_t, bool, nw_error_t error) mutable {
+ nw_connection_receive(m_connection.get(), 1, std::numeric_limits<uint32_t>::max(), makeBlockPtr([connection = *this, completionHandler = WTFMove(completionHandler)](dispatch_data_t content, nw_content_context_t, bool, nw_error_t error) mutable {
if (error || !content)
return completionHandler({ });
- buffer.appendVector(vectorFromData(content));
+ completionHandler(vectorFromData(content));
+ }).get());
+}
+
+void Connection::receiveHTTPRequest(CompletionHandler<void(Vector<char>&&)>&& completionHandler, Vector<char>&& buffer) const
+{
+ receiveBytes([connection = *this, completionHandler = WTFMove(completionHandler), buffer = WTFMove(buffer)](Vector<uint8_t>&& bytes) mutable {
+ buffer.appendVector(WTFMove(bytes));
if (auto* doubleNewline = strnstr(buffer.data(), "\r\n\r\n", buffer.size())) {
if (auto* contentLengthBegin = strnstr(buffer.data(), "Content-Length", buffer.size())) {
size_t contentLength = atoi(contentLengthBegin + strlen("Content-Length: "));
@@ -249,12 +269,22 @@
completionHandler(WTFMove(buffer));
} else
connection.receiveHTTPRequest(WTFMove(completionHandler), WTFMove(buffer));
- }).get());
+ });
}
void Connection::send(String&& message, CompletionHandler<void()>&& completionHandler) const
{
- nw_connection_send(m_connection.get(), dataFromString(WTFMove(message)).get(), NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, makeBlockPtr([completionHandler = WTFMove(completionHandler)](nw_error_t error) mutable {
+ send(dataFromString(WTFMove(message)), WTFMove(completionHandler));
+}
+
+void Connection::send(Vector<uint8_t>&& message, CompletionHandler<void()>&& completionHandler) const
+{
+ send(dataFromVector(WTFMove(message)), WTFMove(completionHandler));
+}
+
+void Connection::send(RetainPtr<dispatch_data_t>&& message, CompletionHandler<void()>&& completionHandler) const
+{
+ nw_connection_send(m_connection.get(), message.get(), NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, makeBlockPtr([completionHandler = WTFMove(completionHandler)](nw_error_t error) mutable {
ASSERT(!error);
if (completionHandler)
completionHandler();
@@ -266,6 +296,79 @@
nw_connection_cancel(m_connection.get());
}
+void H2::Connection::send(Frame&& frame, CompletionHandler<void()>&& completionHandler) const
+{
+ auto frameType = frame.type();
+ auto sendFrame = [tlsConnection = m_tlsConnection, frame = WTFMove(frame), completionHandler = WTFMove(completionHandler)] () mutable {
+ // https://http2.github.io/http2-spec/#rfc.section.4.1
+ Vector<uint8_t> bytes;
+ constexpr size_t frameHeaderLength = 9;
+ bytes.reserveInitialCapacity(frameHeaderLength + frame.payload().size());
+ bytes.uncheckedAppend(frame.payload().size() >> 16);
+ bytes.uncheckedAppend(frame.payload().size() >> 8);
+ bytes.uncheckedAppend(frame.payload().size() >> 0);
+ bytes.uncheckedAppend(static_cast<uint8_t>(frame.type()));
+ bytes.uncheckedAppend(frame.flags());
+ bytes.uncheckedAppend(frame.streamID() >> 24);
+ bytes.uncheckedAppend(frame.streamID() >> 16);
+ bytes.uncheckedAppend(frame.streamID() >> 8);
+ bytes.uncheckedAppend(frame.streamID() >> 0);
+ bytes.appendVector(frame.payload());
+ tlsConnection.send(WTFMove(bytes), WTFMove(completionHandler));
+ };
+
+ if (m_sendServerConnectionPreface && frameType != Frame::Type::Settings) {
+ // https://http2.github.io/http2-spec/#rfc.section.3.5
+ m_sendServerConnectionPreface = false;
+ send(Frame(Frame::Type::Settings, 0, 0, { }), WTFMove(sendFrame));
+ } else
+ sendFrame();
+}
+
+void H2::Connection::receive(CompletionHandler<void(Frame&&)>&& completionHandler) const
+{
+ if (m_expectClientConnectionPreface) {
+ // https://http2.github.io/http2-spec/#rfc.section.3.5
+ constexpr size_t clientConnectionPrefaceLength = 24;
+ if (m_receiveBuffer.size() < clientConnectionPrefaceLength) {
+ m_tlsConnection.receiveBytes([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (Vector<uint8_t>&& bytes) mutable {
+ m_receiveBuffer.appendVector(bytes);
+ receive(WTFMove(completionHandler));
+ });
+ return;
+ }
+ ASSERT(!memcmp(m_receiveBuffer.data(), "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", clientConnectionPrefaceLength));
+ m_receiveBuffer.remove(0, clientConnectionPrefaceLength);
+ m_expectClientConnectionPreface = false;
+ return receive(WTFMove(completionHandler));
+ }
+
+ // https://http2.github.io/http2-spec/#rfc.section.4.1
+ constexpr size_t frameHeaderLength = 9;
+ if (m_receiveBuffer.size() >= frameHeaderLength) {
+ uint32_t payloadLength = (static_cast<uint32_t>(m_receiveBuffer[0]) << 16)
+ + (static_cast<uint32_t>(m_receiveBuffer[1]) << 8)
+ + (static_cast<uint32_t>(m_receiveBuffer[2]) << 0);
+ if (m_receiveBuffer.size() >= frameHeaderLength + payloadLength) {
+ auto type = static_cast<Frame::Type>(m_receiveBuffer[3]);
+ auto flags = m_receiveBuffer[4];
+ auto streamID = (static_cast<uint32_t>(m_receiveBuffer[5]) << 24)
+ + (static_cast<uint32_t>(m_receiveBuffer[6]) << 16)
+ + (static_cast<uint32_t>(m_receiveBuffer[7]) << 8)
+ + (static_cast<uint32_t>(m_receiveBuffer[8]) << 0);
+ Vector<uint8_t> payload;
+ payload.append(m_receiveBuffer.data() + frameHeaderLength, payloadLength);
+ m_receiveBuffer.remove(0, frameHeaderLength + payloadLength);
+ return completionHandler(Frame(type, flags, streamID, WTFMove(payload)));
+ }
+ }
+
+ m_tlsConnection.receiveBytes([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (Vector<uint8_t>&& bytes) mutable {
+ m_receiveBuffer.appendVector(bytes);
+ receive(WTFMove(completionHandler));
+ });
+}
+
} // namespace TestWebKitAPI
#endif // HAVE(NETWORK_FRAMEWORK)