/* * Copyright (C) 2016 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. */ #include "config.h" #include "NetworkCaptureEvent.h" #if ENABLE(NETWORK_CAPTURE) #include "NetworkCaptureLogging.h" #include "json.hpp" #include #include #include #include #include #include #include namespace WebKit { namespace NetworkCapture { const char RequestSentEvent::typeName[] = "RequestSentEvent"; const char ResponseReceivedEvent::typeName[] = "ResponseReceivedEvent"; const char RedirectReceivedEvent::typeName[] = "RedirectReceivedEvent"; const char RedirectSentEvent::typeName[] = "RedirectSentEvent"; const char DataReceivedEvent::typeName[] = "DataReceivedEvent"; const char FinishedEvent::typeName[] = "FinishedEvent"; static Headers copyHeaders(const WebCore::HTTPHeaderMap& httpHeaders) { Headers headers; for (const auto& header : httpHeaders) headers.append(std::make_pair(header.key, header.value)); return headers; } // ---------- Request::Request(String&& url, String&& referrer, int policy, String&& method, Headers&& headers) : url(WTFMove(url)) , referrer(WTFMove(referrer)) , policy(WTFMove(policy)) , method(WTFMove(method)) , headers(WTFMove(headers)) { } Request::Request(const WebCore::ResourceRequest& request) : url(request.url().string()) , referrer(request.httpReferrer()) , policy(static_cast(request.cachePolicy())) , method(request.httpMethod()) , headers(copyHeaders(request.httpHeaderFields())) { } Request::operator WebCore::ResourceRequest() const { WebCore::URLParser parser(url); WebCore::ResourceRequest request(parser.result(), referrer, static_cast(policy)); request.setHTTPMethod(method); for (const auto& header : headers) request.setHTTPHeaderField(header.first, header.second); return request; } // ---------- Response::Response(String&& url, String&& mimeType, long long expectedLength, String&& textEncodingName, String&& version, int status, String&& reason, Headers&& headers) : url(WTFMove(url)) , mimeType(WTFMove(mimeType)) , expectedLength(WTFMove(expectedLength)) , textEncodingName(WTFMove(textEncodingName)) , status(WTFMove(status)) , reason(WTFMove(reason)) , headers(WTFMove(headers)) { } Response::Response(const WebCore::ResourceResponse& response) : url(response.url().string()) , mimeType(response.mimeType()) , expectedLength(response.expectedContentLength()) , textEncodingName(response.textEncodingName()) , version(response.httpVersion()) , status(response.httpStatusCode()) , reason(response.httpStatusText()) , headers(copyHeaders(response.httpHeaderFields())) { } Response::operator WebCore::ResourceResponse() const { WebCore::URLParser parser(url); WebCore::ResourceResponse response(parser.result(), mimeType, expectedLength, textEncodingName); response.setHTTPVersion(version); response.setHTTPStatusCode(status); response.setHTTPStatusText(reason); for (const auto& header : headers) response.setHTTPHeaderField(header.first, header.second); return response; } // ---------- Error::Error(String&& domain, String&& failingURL, String&& localizedDescription, int errorCode, int type) : domain(WTFMove(domain)) , failingURL(WTFMove(failingURL)) , localizedDescription(WTFMove(localizedDescription)) , errorCode(WTFMove(errorCode)) , type(WTFMove(type)) { } Error::Error(const WebCore::ResourceError& error) : domain(error.domain()) , failingURL(error.failingURL().string()) , localizedDescription(error.localizedDescription()) , errorCode(error.errorCode()) , type(static_cast(error.type())) { } Error::operator WebCore::ResourceError() const { WebCore::URLParser parser(failingURL); WebCore::ResourceError error(domain, errorCode, parser.result(), localizedDescription, static_cast(type)); return error; } // ---------- // SEE THE NOTE IN json.hpp REGARDING ITS USE IN THIS PROJECT. IN SHORT, DO NOT // USE json.hpp ANYWHERE ELSE. IT WILL BE GOING AWAY. using json = nlohmann::basic_json<>; template struct JSONCoder { static json encode(Type val) { return json(val); } static Type decode(const json& jVal) { return jVal.get(); } }; template<> struct JSONCoder { static json encode(const char* val) { return json(val); } }; template<> struct JSONCoder { static json encode(const String& val) { return json(static_cast(val.utf8().data())); } static String decode(const json& jVal) { return String(jVal.get_ref().c_str()); } }; template<> struct JSONCoder { static json encode(const CaptureTimeType& time) { return JSONCoder::encode(time.secondsSinceEpoch().seconds()); } static CaptureTimeType decode(const json& jTime) { return CaptureTimeType::fromRawSeconds(JSONCoder::decode(jTime)); } }; template<> struct JSONCoder { static json encode(const KeyValuePair& pair) { return json { JSONCoder::encode(pair.first), JSONCoder::encode(pair.second) }; } static KeyValuePair decode(const json& jPair) { return KeyValuePair { JSONCoder::decode(jPair[0]), JSONCoder::decode(jPair[1]) }; } }; template struct JSONCoder> { static json encode(const Vector& vector) { json jVector; for (const auto& element : vector) jVector.push_back(JSONCoder::encode(element)); return jVector; } static Vector decode(const json& jVector) { Vector vector; for (const auto& element : jVector) vector.append(JSONCoder::decode(element)); return vector; } }; template<> struct JSONCoder { static json encode(const Request& request) { return json { { "url", JSONCoder::encode(request.url) }, { "referrer", JSONCoder::encode(request.referrer) }, { "policy", JSONCoder::encode(request.policy) }, { "method", JSONCoder::encode(request.method) }, { "headers", JSONCoder::encode(request.headers) } }; } static Request decode(const json& jRequest) { return Request { JSONCoder::decode(jRequest["url"]), JSONCoder::decode(jRequest["referrer"]), JSONCoder::decode(jRequest["policy"]), JSONCoder::decode(jRequest["method"]), JSONCoder::decode(jRequest["headers"]) }; } }; template<> struct JSONCoder { static json encode(const Response& response) { return json { { "url", JSONCoder::encode(response.url) }, { "mimeType", JSONCoder::encode(response.mimeType) }, { "expectedLength", JSONCoder::encode(response.expectedLength) }, { "textEncodingName", JSONCoder::encode(response.textEncodingName) }, { "version", JSONCoder::encode(response.version) }, { "status", JSONCoder::encode(response.status) }, { "reason", JSONCoder::encode(response.reason) }, { "headers", JSONCoder::encode(response.headers) } }; } static Response decode(const json& jResponse) { return Response { JSONCoder::decode(jResponse["url"]), JSONCoder::decode(jResponse["mimeType"]), JSONCoder::decode(jResponse["expectedLength"]), JSONCoder::decode(jResponse["textEncodingName"]), JSONCoder::decode(jResponse["version"]), JSONCoder::decode(jResponse["status"]), JSONCoder::decode(jResponse["reason"]), JSONCoder::decode(jResponse["headers"]) }; } }; template<> struct JSONCoder { static json encode(const Error& error) { return json { { "domain", JSONCoder::encode(error.domain) }, { "failingURL", JSONCoder::encode(error.failingURL) }, { "localizedDescription", JSONCoder::encode(error.localizedDescription) }, { "errorCode", JSONCoder::encode(error.errorCode) }, { "type", JSONCoder::encode(error.type) } }; } static Error decode(const json& jError) { return Error { JSONCoder::decode(jError["domain"]), JSONCoder::decode(jError["failingURL"]), JSONCoder::decode(jError["localizedDescription"]), JSONCoder::decode(jError["errorCode"]), JSONCoder::decode(jError["type"]) }; } }; template<> struct JSONCoder { static json encode(const WebCore::SharedBuffer& data) { Vector buffer; base64Encode(data.data(), data.size(), buffer); return json(&buffer[0], buffer.size()); } static Ref decode(const json& jData) { Vector data; const auto& str = jData.get_ref(); auto result = base64Decode(str.c_str(), str.size(), data); ASSERT_UNUSED(result, result); return WebCore::SharedBuffer::adoptVector(data); } }; template<> struct JSONCoder { static json encode(const RequestSentEvent& event) { return json { { "type", JSONCoder::encode(event.typeName) }, { "time", JSONCoder::encode(event.time) }, { "request", JSONCoder::encode(event.request) } }; } static RequestSentEvent decode(const json& jEvent) { return RequestSentEvent { JSONCoder::decode(jEvent["time"]), JSONCoder::decode(jEvent["request"]) }; } }; template<> struct JSONCoder { static json encode(const ResponseReceivedEvent& event) { return json { { "type", JSONCoder::encode(event.typeName) }, { "time", JSONCoder::encode(event.time) }, { "response", JSONCoder::encode(event.response) } }; } static ResponseReceivedEvent decode(const json& jEvent) { return ResponseReceivedEvent { JSONCoder::decode(jEvent["time"]), JSONCoder::decode(jEvent["response"]) }; } }; template<> struct JSONCoder { static json encode(const RedirectReceivedEvent& event) { return json { { "type", JSONCoder::encode(event.typeName) }, { "time", JSONCoder::encode(event.time) }, { "request", JSONCoder::encode(event.request) }, { "response", JSONCoder::encode(event.response) } }; } static RedirectReceivedEvent decode(const json& jEvent) { return RedirectReceivedEvent { JSONCoder::decode(jEvent["time"]), JSONCoder::decode(jEvent["request"]), JSONCoder::decode(jEvent["response"]) }; } }; template<> struct JSONCoder { static json encode(const RedirectSentEvent& event) { return json { { "type", JSONCoder::encode(event.typeName) }, { "time", JSONCoder::encode(event.time) }, { "request", JSONCoder::encode(event.request) }, }; } static RedirectSentEvent decode(const json& jEvent) { return RedirectSentEvent { JSONCoder::decode(jEvent["time"]), JSONCoder::decode(jEvent["request"]) }; } }; template<> struct JSONCoder { static json encode(const DataReceivedEvent& event) { return json { { "type", JSONCoder::encode(event.typeName) }, { "time", JSONCoder::encode(event.time) }, { "data", JSONCoder::encode(event.data.get()) } }; } static DataReceivedEvent decode(const json& jEvent) { return DataReceivedEvent { JSONCoder::decode(jEvent["time"]), JSONCoder::decode(jEvent["data"]) }; } }; template<> struct JSONCoder { static json encode(const FinishedEvent& event) { return json { { "type", JSONCoder::encode(event.typeName) }, { "time", JSONCoder::encode(event.time) }, { "error", JSONCoder::encode(event.error) } }; } static FinishedEvent decode(const json& jEvent) { return FinishedEvent { JSONCoder::decode(jEvent["time"]), JSONCoder::decode(jEvent["error"]) }; } }; std::string eventToString(const CaptureEvent& event) { json result; WTF::visit([&result](const auto& event) { using EventType = std::decay_t; result = JSONCoder::encode(event); }, event); return result.dump(4); } OptionalCaptureEvent stringToEvent(const char* jsonStr) { auto jValue = json::parse(jsonStr); const auto& type = jValue["type"].get_ref(); OptionalCaptureEvent result { std::nullopt }; brigand::for_each([&](auto T) { using Type = typename decltype(T)::type; if (!result && type == Type::typeName) result = OptionalCaptureEvent(JSONCoder::decode(jValue)); }); return result; } } // namespace NetworkCapture } // namespace WebKit #endif // ENABLE(NETWORK_CAPTURE)