| /* |
| * Copyright (C) 2019 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 |
| |
| #import "NetworkConnection.h" |
| #import <wtf/CompletionHandler.h> |
| #import <wtf/Forward.h> |
| #import <wtf/HashMap.h> |
| #import <wtf/RetainPtr.h> |
| #import <wtf/cocoa/VectorCocoa.h> |
| #import <wtf/text/StringHash.h> |
| |
| OBJC_CLASS NSURLRequest; |
| |
| namespace TestWebKitAPI { |
| |
| class WebTransportServer; |
| struct HTTPResponse; |
| |
| class HTTPServer { |
| WTF_DEPRECATED_MAKE_FAST_ALLOCATED(HTTPServer); |
| public: |
| struct RequestData; |
| enum class Protocol : uint8_t { Http, Https, HttpsWithLegacyTLS, Http2, HttpsProxy, HttpsProxyWithAuthentication }; |
| 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, SecIdentityRef = nullptr, std::optional<uint16_t> port = { }); |
| HTTPServer(Function<void(Connection)>&&, Protocol = Protocol::Http); |
| enum class UseCoroutines : bool { Yes }; |
| HTTPServer(UseCoroutines, Function<ConnectionTask(Connection)>&&, Protocol = Protocol::Http); |
| ~HTTPServer(); |
| uint16_t port() const; |
| String origin() const; |
| NSURLRequest *request(StringView path = "/"_s) const; |
| NSURLRequest *requestWithLocalhost(StringView path = "/"_s) const; |
| WKWebViewConfiguration *httpsProxyConfiguration() const; |
| size_t totalConnections() const; |
| size_t totalRequests() const; |
| String lastRequestCookies() const; |
| void cancel(); |
| void terminateAllConnections(CompletionHandler<void()>&&); |
| |
| void addResponse(String&& path, HTTPResponse&&); |
| void setResponse(String&& path, HTTPResponse&&); |
| |
| static void respondWithOK(Connection); |
| static void respondWithChallengeThenOK(Connection); |
| static String parseCookies(const Vector<char>& request); |
| static String parsePath(const Vector<char>& request); |
| static String parseBody(const Vector<char>&); |
| static Vector<uint8_t> testPrivateKey(); |
| static Vector<uint8_t> testCertificate(); |
| |
| private: |
| static RetainPtr<nw_parameters_t> listenerParameters(Protocol, CertificateVerifier&&, RetainPtr<SecIdentityRef>&&, std::optional<uint16_t> port); |
| static void respondToRequests(Connection, Ref<RequestData>); |
| const char* scheme() const; |
| |
| Ref<RequestData> m_requestData; |
| RetainPtr<nw_listener_t> m_listener; |
| Protocol m_protocol { Protocol::Http }; |
| }; |
| |
| struct HTTPResponse { |
| enum class Behavior : uint8_t { |
| SendResponseNormally, |
| TerminateConnectionAfterReceivingRequest, |
| NeverSendResponse |
| }; |
| |
| HTTPResponse(Vector<uint8_t>&& body) |
| : body(WTF::move(body)) { } |
| HTTPResponse(const String& body) |
| : body(bodyFromString(body)) { } |
| HTTPResponse(HashMap<String, String>&& headerFields, const String& body) |
| : headerFields(WTF::move(headerFields)) |
| , body(bodyFromString(body)) { } |
| HTTPResponse(unsigned statusCode, HashMap<String, String>&& headerFields = { }, const String& body = { }) |
| : statusCode(statusCode) |
| , headerFields(WTF::move(headerFields)) |
| , body(bodyFromString(body)) { } |
| HTTPResponse(Behavior behavior) |
| : behavior(behavior) { } |
| HTTPResponse(NSData *data) |
| : body(makeVector(data)) { } |
| |
| HTTPResponse(const HTTPResponse&) = default; |
| HTTPResponse(HTTPResponse&&) = default; |
| HTTPResponse() = default; |
| HTTPResponse& operator=(const HTTPResponse&) = default; |
| HTTPResponse& operator=(HTTPResponse&&) = default; |
| |
| void setShouldRespondWith304ToConditionalRequests(HashMap<String, String>&& headerFields = { }) |
| { |
| shouldRespondWith304ToConditionalRequests = true; |
| headerFieldsFor304 = WTF::move(headerFields); |
| } |
| |
| enum class IncludeContentLength : bool { No, Yes }; |
| Vector<uint8_t> serialize(IncludeContentLength = IncludeContentLength::Yes) const; |
| static Vector<uint8_t> bodyFromString(const String&); |
| |
| unsigned statusCode { 200 }; |
| HashMap<String, String> headerFields; |
| Vector<uint8_t> body; |
| Behavior behavior { Behavior::SendResponseNormally }; |
| bool shouldRespondWith304ToConditionalRequests { false }; |
| HashMap<String, String> headerFieldsFor304; |
| }; |
| |
| 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 = 0x0, |
| 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(WTF::move(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 |
| |
| RetainPtr<SecCertificateRef> testCertificate(); |
| RetainPtr<SecIdentityRef> testIdentity(); |
| RetainPtr<SecIdentityRef> testIdentity2(); |
| void verifyCertificateAndPublicKey(SecTrustRef); |