⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 263879 in webkit


Ignore:
Timestamp:
Jul 2, 2020, 7:01:21 PM (6 years ago)
Author:
commit-queue@webkit.org
Message:

Add testing infrastructure and SPI declaration for HTTP/2 ping
https://bugs.webkit.org/show_bug.cgi?id=213913

Patch by Alex Christensen <achristensen@webkit.org> on 2020-07-02
Reviewed by Jer Noble.

Source/WebCore/PAL:

  • pal/spi/cf/CFNetworkSPI.h:

Source/WTF:

This is work towards rdar://problem/64495827

  • wtf/PlatformHave.h:

Tools:

  • 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):

Location:
trunk
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WTF/ChangeLog

    r263819 r263879  
     12020-07-02  Alex Christensen  <achristensen@webkit.org>
     2
     3        Add testing infrastructure and SPI declaration for HTTP/2 ping
     4        https://bugs.webkit.org/show_bug.cgi?id=213913
     5
     6        Reviewed by Jer Noble.
     7
     8        This is work towards rdar://problem/64495827
     9
     10        * wtf/PlatformHave.h:
     11
    1122020-07-01  Tim Horton  <timothy_horton@apple.com>
    213
  • trunk/Source/WTF/wtf/PlatformHave.h

    r263819 r263879  
    381381#endif
    382382
     383#if (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101600) \
     384    || (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000) \
     385    || (PLATFORM(WATCHOS) && __WATCH_OS_VERSION_MIN_REQUIRED >= 70000) \
     386    || (PLATFORM(APPLETV) && __TV_OS_VERSION_MIN_REQUIRED >= 140000) \
     387    || (PLATFORM(MACCATALYST) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 140000)
     388#define HAVE_PRECONNECT_PING 1
     389#endif
     390
    383391#if PLATFORM(COCOA) && !(PLATFORM(MAC) && !(__MAC_OS_X_VERSION_MIN_REQUIRED >= 101400 && __MAC_OS_X_VERSION_MAX_ALLOWED >= 101404))
    384392#define HAVE_CFNETWORK_OVERRIDE_SESSION_COOKIE_ACCEPT_POLICY 1
  • trunk/Source/WebCore/PAL/ChangeLog

    r263819 r263879  
     12020-07-02  Alex Christensen  <achristensen@webkit.org>
     2
     3        Add testing infrastructure and SPI declaration for HTTP/2 ping
     4        https://bugs.webkit.org/show_bug.cgi?id=213913
     5
     6        Reviewed by Jer Noble.
     7
     8        * pal/spi/cf/CFNetworkSPI.h:
     9
    1102020-07-01  Tim Horton  <timothy_horton@apple.com>
    211
  • trunk/Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h

    r263736 r263879  
    6767
    6868#else // !PLATFORM(WIN) && !USE(APPLE_INTERNAL_SDK)
     69
     70#if HAVE(PRECONNECT_PING)
     71@interface _NSHTTPConnectionInfo : NSObject
     72- (void)sendPingWithReceiveHandler:(void (^)(NSError * _Nullable error, NSTimeInterval interval))pongHandler;
     73@property (readonly) BOOL isValid;
     74@end
     75
     76@interface NSURLSessionTask (HTTPConnectionInfo)
     77- (void)getUnderlyingHTTPConnectionInfoWithCompletionHandler:(void (^)(_NSHTTPConnectionInfo *connectionInfo))completionHandler;
     78@end
     79#endif // HAVE(PRECONNECT_PING)
    6980
    7081#if HAVE(LOGGING_PRIVACY_LEVEL)
  • trunk/Tools/ChangeLog

    r263874 r263879  
     12020-07-02  Alex Christensen  <achristensen@webkit.org>
     2
     3        Add testing infrastructure and SPI declaration for HTTP/2 ping
     4        https://bugs.webkit.org/show_bug.cgi?id=213913
     5
     6        Reviewed by Jer Noble.
     7
     8        * TestWebKitAPI/Tests/WebKitCocoa/Preconnect.mm:
     9        (-[SessionDelegate URLSession:task:didReceiveChallenge:completionHandler:]):
     10        (TestWebKitAPI::pingPong):
     11        (TestWebKitAPI::TEST):
     12        * TestWebKitAPI/cocoa/HTTPServer.h:
     13        (TestWebKitAPI::H2::Frame::Frame):
     14        (TestWebKitAPI::H2::Frame::type const):
     15        (TestWebKitAPI::H2::Frame::flags const):
     16        (TestWebKitAPI::H2::Frame::streamID const):
     17        (TestWebKitAPI::H2::Frame::payload const):
     18        (TestWebKitAPI::H2::Connection::create):
     19        (TestWebKitAPI::H2::Connection::Connection):
     20        * TestWebKitAPI/cocoa/HTTPServer.mm:
     21        (TestWebKitAPI::HTTPServer::listenerParameters):
     22        (TestWebKitAPI::dataFromVector):
     23        (TestWebKitAPI::vectorFromData):
     24        (TestWebKitAPI::HTTPServer::request const):
     25        (TestWebKitAPI::Connection::receiveBytes const):
     26        (TestWebKitAPI::Connection::receiveHTTPRequest const):
     27        (TestWebKitAPI::Connection::send const):
     28        (TestWebKitAPI::H2::Connection::send const):
     29        (TestWebKitAPI::H2::Connection::receive const):
     30
    1312020-07-02  Kate Cheney  <katherine_cheney@apple.com>
    232
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/Preconnect.mm

    r263242 r263879  
    3333#import "Utilities.h"
    3434#import <WebKit/WKWebViewPrivate.h>
     35#import <pal/spi/cf/CFNetworkSPI.h>
    3536#import <wtf/RetainPtr.h>
     37
     38#if HAVE(PRECONNECT_PING)
     39@interface SessionDelegate : NSObject <NSURLSessionDataDelegate>
     40@end
     41
     42@implementation SessionDelegate
     43- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
     44{
     45    completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
     46}
     47@end
     48#endif
    3649
    3750namespace TestWebKitAPI {
     
    91104#endif
    92105
     106#if HAVE(PRECONNECT_PING)
     107static void pingPong(Ref<H2::Connection>&& connection, size_t* headersCount)
     108{
     109    connection->receive([connection, headersCount] (H2::Frame&& frame) mutable {
     110        switch (frame.type()) {
     111        case H2::Frame::Type::Headers:
     112            ++*headersCount;
     113            break;
     114        case H2::Frame::Type::Settings:
     115        case H2::Frame::Type::WindowUpdate:
     116            // These frame types are ok for a preconnect task.
     117            break;
     118        case H2::Frame::Type::Ping:
     119            {
     120                // https://http2.github.io/http2-spec/#rfc.section.6.7
     121                constexpr uint8_t ack = 0x1;
     122                connection->send(H2::Frame(H2::Frame::Type::Ping, ack, frame.streamID(), frame.payload()));
     123            }
     124            break;
     125        default:
     126            // If anything else is sent by the client, preconnect is doing too much.
     127            ASSERT_NOT_REACHED();
     128            break;
     129        }
     130        pingPong(WTFMove(connection), headersCount);
     131    });
     132}
     133
     134// This should remain disabled until rdar://problem/65055930 is integrated and bots are updated
     135TEST(Preconnect, DISABLED_H2Ping)
     136{
     137    size_t headersCount = 0;
     138    HTTPServer server([headersCount = &headersCount] (Connection tlsConnection) {
     139        pingPong(H2::Connection::create(tlsConnection), headersCount);
     140    }, HTTPServer::Protocol::Http2);
     141   
     142    auto delegate = adoptNS([SessionDelegate new]);
     143    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration] delegate:delegate.get() delegateQueue:[NSOperationQueue mainQueue]];
     144    NSURLSessionDataTask *task = [session dataTaskWithRequest:server.request()];
     145    task._preconnect = YES;
     146    __block bool done = false;
     147
     148    [task getUnderlyingHTTPConnectionInfoWithCompletionHandler:^(_NSHTTPConnectionInfo *connectionInfo) {
     149        EXPECT_TRUE(connectionInfo.isValid);
     150        [connectionInfo sendPingWithReceiveHandler:^(NSError *error, NSTimeInterval interval) {
     151            EXPECT_FALSE(error);
     152            EXPECT_GT(interval, 0.0);
     153            done = true;
     154        }];
     155    }];
     156    [task resume];
     157    Util::run(&done);
     158   
     159    // Make sure the client doesn't send anything except Settings, WindowUpdate, and Ping.
     160    // If Headers or Data were sent, then the preconnect wouldn't be preconnect.
     161    usleep(100000);
     162    Util::spinRunLoop(100);
     163    EXPECT_EQ(headersCount, 0u);
     164
     165    NSURLSessionDataTask *task2 = [session dataTaskWithRequest:server.request()];
     166    [task2 resume];
     167    while (!headersCount)
     168        Util::spinRunLoop();
     169    EXPECT_EQ(headersCount, 1u);
     170    usleep(100000);
     171    Util::spinRunLoop(100);
     172    EXPECT_EQ(headersCount, 1u);
     173}
     174#endif // HAVE(PRECONNECT_PING)
     175
    93176}
    94177
  • trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.h

    r262985 r263879  
    4444    struct HTTPResponse;
    4545    struct RequestData;
    46     enum class Protocol : uint8_t { Http, Https, HttpsWithLegacyTLS };
     46    enum class Protocol : uint8_t { Http, Https, HttpsWithLegacyTLS, Http2 };
    4747    using CertificateVerifier = Function<void(sec_protocol_metadata_t, sec_trust_t, sec_protocol_verify_complete_t)>;
    4848
     
    6868public:
    6969    void send(String&&, CompletionHandler<void()>&& = nullptr) const;
     70    void send(Vector<uint8_t>&&, CompletionHandler<void()>&& = nullptr) const;
     71    void send(RetainPtr<dispatch_data_t>&&, CompletionHandler<void()>&& = nullptr) const;
     72    void receiveBytes(CompletionHandler<void(Vector<uint8_t>&&)>&&) const;
    7073    void receiveHTTPRequest(CompletionHandler<void(Vector<char>&&)>&&, Vector<char>&& buffer = { }) const;
    7174    void terminate() const;
     
    106109};
    107110
     111namespace H2 {
     112
     113// https://http2.github.io/http2-spec/#rfc.section.4.1
     114class Frame {
     115public:
     116
     117    // https://http2.github.io/http2-spec/#rfc.section.6
     118    enum class Type : uint8_t {
     119        Data = 0x0,
     120        Headers = 0x1,
     121        Priority = 0x2,
     122        RSTStream = 0x3,
     123        Settings = 0x4,
     124        PushPromise = 0x5,
     125        Ping = 0x6,
     126        GoAway = 0x7,
     127        WindowUpdate = 0x8,
     128        Continuation = 0x9,
     129    };
     130
     131    Frame(Type type, uint8_t flags, uint32_t streamID, Vector<uint8_t> payload)
     132        : m_type(type)
     133        , m_flags(flags)
     134        , m_streamID(streamID)
     135        , m_payload(WTFMove(payload)) { }
     136
     137    Type type() const { return m_type; }
     138    uint8_t flags() const { return m_flags; }
     139    uint32_t streamID() const { return m_streamID; }
     140    const Vector<uint8_t>& payload() const { return m_payload; }
     141
     142private:
     143    Type m_type;
     144    uint8_t m_flags;
     145    uint32_t m_streamID;
     146    Vector<uint8_t> m_payload;
     147};
     148
     149class Connection : public RefCounted<Connection> {
     150public:
     151    static Ref<Connection> create(TestWebKitAPI::Connection tlsConnection) { return adoptRef(*new Connection(tlsConnection)); }
     152    void send(Frame&&, CompletionHandler<void()>&& = nullptr) const;
     153    void receive(CompletionHandler<void(Frame&&)>&&) const;
     154private:
     155    Connection(TestWebKitAPI::Connection tlsConnection)
     156        : m_tlsConnection(tlsConnection) { }
     157
     158    TestWebKitAPI::Connection m_tlsConnection;
     159    mutable bool m_expectClientConnectionPreface { true };
     160    mutable bool m_sendServerConnectionPreface { true };
     161    mutable Vector<uint8_t> m_receiveBuffer;
     162};
     163
     164} // namespace H2
     165
    108166} // namespace TestWebKitAPI
    109167
  • trunk/Tools/TestWebKitAPI/cocoa/HTTPServer.mm

    r262985 r263879  
    6767            }).get(), dispatch_get_main_queue());
    6868        }
     69        if (protocol == Protocol::Http2)
     70            sec_protocol_options_add_tls_application_protocol(options.get(), "h2");
    6971#else
    7072        UNUSED_PARAM(protocolOptions);
     
    166168}
    167169
    168 static Vector<char> vectorFromData(dispatch_data_t content)
    169 {
    170     __block Vector<char> request;
     170static RetainPtr<dispatch_data_t> dataFromVector(Vector<uint8_t>&& v)
     171{
     172    auto bufferSize = v.size();
     173    auto rawPointer = v.releaseBuffer().leakPtr();
     174    return adoptNS(dispatch_data_create(rawPointer, bufferSize, dispatch_get_main_queue(), ^{
     175        fastFree(rawPointer);
     176    }));
     177}
     178
     179static Vector<uint8_t> vectorFromData(dispatch_data_t content)
     180{
     181    ASSERT(content);
     182    __block Vector<uint8_t> request;
    171183    dispatch_data_apply(content, ^bool(dispatch_data_t, size_t, const void* buffer, size_t size) {
    172184        request.append(static_cast<const char*>(buffer), size);
     
    228240    case Protocol::Https:
    229241    case Protocol::HttpsWithLegacyTLS:
     242    case Protocol::Http2:
    230243        format = @"https://127.0.0.1:%d%s";
    231244        break;
     
    234247}
    235248
    236 void Connection::receiveHTTPRequest(CompletionHandler<void(Vector<char>&&)>&& completionHandler, Vector<char>&& buffer) const
    237 {
    238     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 {
     249void Connection::receiveBytes(CompletionHandler<void(Vector<uint8_t>&&)>&& completionHandler) const
     250{
     251    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 {
    239252        if (error || !content)
    240253            return completionHandler({ });
    241         buffer.appendVector(vectorFromData(content));
     254        completionHandler(vectorFromData(content));
     255    }).get());
     256}
     257
     258void Connection::receiveHTTPRequest(CompletionHandler<void(Vector<char>&&)>&& completionHandler, Vector<char>&& buffer) const
     259{
     260    receiveBytes([connection = *this, completionHandler = WTFMove(completionHandler), buffer = WTFMove(buffer)](Vector<uint8_t>&& bytes) mutable {
     261        buffer.appendVector(WTFMove(bytes));
    242262        if (auto* doubleNewline = strnstr(buffer.data(), "\r\n\r\n", buffer.size())) {
    243263            if (auto* contentLengthBegin = strnstr(buffer.data(), "Content-Length", buffer.size())) {
     
    250270        } else
    251271            connection.receiveHTTPRequest(WTFMove(completionHandler), WTFMove(buffer));
    252     }).get());
     272    });
    253273}
    254274
    255275void Connection::send(String&& message, CompletionHandler<void()>&& completionHandler) const
    256276{
    257     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 {
     277    send(dataFromString(WTFMove(message)), WTFMove(completionHandler));
     278}
     279
     280void Connection::send(Vector<uint8_t>&& message, CompletionHandler<void()>&& completionHandler) const
     281{
     282    send(dataFromVector(WTFMove(message)), WTFMove(completionHandler));
     283}
     284
     285void Connection::send(RetainPtr<dispatch_data_t>&& message, CompletionHandler<void()>&& completionHandler) const
     286{
     287    nw_connection_send(m_connection.get(), message.get(), NW_CONNECTION_DEFAULT_MESSAGE_CONTEXT, true, makeBlockPtr([completionHandler = WTFMove(completionHandler)](nw_error_t error) mutable {
    258288        ASSERT(!error);
    259289        if (completionHandler)
     
    267297}
    268298
     299void H2::Connection::send(Frame&& frame, CompletionHandler<void()>&& completionHandler) const
     300{
     301    auto frameType = frame.type();
     302    auto sendFrame = [tlsConnection = m_tlsConnection, frame = WTFMove(frame), completionHandler = WTFMove(completionHandler)] () mutable {
     303        // https://http2.github.io/http2-spec/#rfc.section.4.1
     304        Vector<uint8_t> bytes;
     305        constexpr size_t frameHeaderLength = 9;
     306        bytes.reserveInitialCapacity(frameHeaderLength + frame.payload().size());
     307        bytes.uncheckedAppend(frame.payload().size() >> 16);
     308        bytes.uncheckedAppend(frame.payload().size() >> 8);
     309        bytes.uncheckedAppend(frame.payload().size() >> 0);
     310        bytes.uncheckedAppend(static_cast<uint8_t>(frame.type()));
     311        bytes.uncheckedAppend(frame.flags());
     312        bytes.uncheckedAppend(frame.streamID() >> 24);
     313        bytes.uncheckedAppend(frame.streamID() >> 16);
     314        bytes.uncheckedAppend(frame.streamID() >> 8);
     315        bytes.uncheckedAppend(frame.streamID() >> 0);
     316        bytes.appendVector(frame.payload());
     317        tlsConnection.send(WTFMove(bytes), WTFMove(completionHandler));
     318    };
     319
     320    if (m_sendServerConnectionPreface && frameType != Frame::Type::Settings) {
     321        // https://http2.github.io/http2-spec/#rfc.section.3.5
     322        m_sendServerConnectionPreface = false;
     323        send(Frame(Frame::Type::Settings, 0, 0, { }), WTFMove(sendFrame));
     324    } else
     325        sendFrame();
     326}
     327
     328void H2::Connection::receive(CompletionHandler<void(Frame&&)>&& completionHandler) const
     329{
     330    if (m_expectClientConnectionPreface) {
     331        // https://http2.github.io/http2-spec/#rfc.section.3.5
     332        constexpr size_t clientConnectionPrefaceLength = 24;
     333        if (m_receiveBuffer.size() < clientConnectionPrefaceLength) {
     334            m_tlsConnection.receiveBytes([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (Vector<uint8_t>&& bytes) mutable {
     335                m_receiveBuffer.appendVector(bytes);
     336                receive(WTFMove(completionHandler));
     337            });
     338            return;
     339        }
     340        ASSERT(!memcmp(m_receiveBuffer.data(), "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n", clientConnectionPrefaceLength));
     341        m_receiveBuffer.remove(0, clientConnectionPrefaceLength);
     342        m_expectClientConnectionPreface = false;
     343        return receive(WTFMove(completionHandler));
     344    }
     345
     346    // https://http2.github.io/http2-spec/#rfc.section.4.1
     347    constexpr size_t frameHeaderLength = 9;
     348    if (m_receiveBuffer.size() >= frameHeaderLength) {
     349        uint32_t payloadLength = (static_cast<uint32_t>(m_receiveBuffer[0]) << 16)
     350            + (static_cast<uint32_t>(m_receiveBuffer[1]) << 8)
     351            + (static_cast<uint32_t>(m_receiveBuffer[2]) << 0);
     352        if (m_receiveBuffer.size() >= frameHeaderLength + payloadLength) {
     353            auto type = static_cast<Frame::Type>(m_receiveBuffer[3]);
     354            auto flags = m_receiveBuffer[4];
     355            auto streamID = (static_cast<uint32_t>(m_receiveBuffer[5]) << 24)
     356                + (static_cast<uint32_t>(m_receiveBuffer[6]) << 16)
     357                + (static_cast<uint32_t>(m_receiveBuffer[7]) << 8)
     358                + (static_cast<uint32_t>(m_receiveBuffer[8]) << 0);
     359            Vector<uint8_t> payload;
     360            payload.append(m_receiveBuffer.data() + frameHeaderLength, payloadLength);
     361            m_receiveBuffer.remove(0, frameHeaderLength + payloadLength);
     362            return completionHandler(Frame(type, flags, streamID, WTFMove(payload)));
     363        }
     364    }
     365   
     366    m_tlsConnection.receiveBytes([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)] (Vector<uint8_t>&& bytes) mutable {
     367        m_receiveBuffer.appendVector(bytes);
     368        receive(WTFMove(completionHandler));
     369    });
     370}
     371
    269372} // namespace TestWebKitAPI
    270373
Note: See TracChangeset for help on using the changeset viewer.