diff options
Diffstat (limited to 'Source/WebKit2/NetworkProcess/capture/NetworkCaptureEvent.cpp')
-rw-r--r-- | Source/WebKit2/NetworkProcess/capture/NetworkCaptureEvent.cpp | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/Source/WebKit2/NetworkProcess/capture/NetworkCaptureEvent.cpp b/Source/WebKit2/NetworkProcess/capture/NetworkCaptureEvent.cpp new file mode 100644 index 000000000..cb7bbdc49 --- /dev/null +++ b/Source/WebKit2/NetworkProcess/capture/NetworkCaptureEvent.cpp @@ -0,0 +1,504 @@ +/* + * 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 <WebCore/ResourceError.h> +#include <WebCore/ResourceRequest.h> +#include <WebCore/ResourceResponse.h> +#include <WebCore/URLParser.h> +#include <wtf/Assertions.h> +#include <wtf/Brigand.h> +#include <wtf/text/Base64.h> + +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<int>(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<WebCore::ResourceRequestCachePolicy>(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<int>(error.type())) +{ +} + +Error::operator WebCore::ResourceError() const +{ + WebCore::URLParser parser(failingURL); + WebCore::ResourceError error(domain, errorCode, parser.result(), localizedDescription, static_cast<WebCore::ResourceError::Type>(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<typename Type> +struct JSONCoder { + static json encode(Type val) + { + return json(val); + } + + static Type decode(const json& jVal) + { + return jVal.get<Type>(); + } +}; + +template<> +struct JSONCoder<const char*> { + static json encode(const char* val) + { + return json(val); + } +}; + +template<> +struct JSONCoder<String> { + static json encode(const String& val) + { + return json(static_cast<const char*>(val.utf8().data())); + } + + static String decode(const json& jVal) + { + return String(jVal.get_ref<const std::string&>().c_str()); + } +}; + +template<> +struct JSONCoder<CaptureTimeType> { + static json encode(const CaptureTimeType& time) + { + return JSONCoder<double>::encode(time.secondsSinceEpoch().seconds()); + } + + static CaptureTimeType decode(const json& jTime) + { + return CaptureTimeType::fromRawSeconds(JSONCoder<double>::decode(jTime)); + } +}; + +template<> +struct JSONCoder<KeyValuePair> { + static json encode(const KeyValuePair& pair) + { + return json { + JSONCoder<String>::encode(pair.first), + JSONCoder<String>::encode(pair.second) + }; + } + + static KeyValuePair decode(const json& jPair) + { + return KeyValuePair { + JSONCoder<String>::decode(jPair[0]), + JSONCoder<String>::decode(jPair[1]) + }; + } +}; + +template<typename T> +struct JSONCoder<Vector<T>> { + static json encode(const Vector<T>& vector) + { + json jVector; + + for (const auto& element : vector) + jVector.push_back(JSONCoder<T>::encode(element)); + + return jVector; + } + + static Vector<T> decode(const json& jVector) + { + Vector<T> vector; + + for (const auto& element : jVector) + vector.append(JSONCoder<T>::decode(element)); + + return vector; + } +}; + +template<> +struct JSONCoder<Request> { + static json encode(const Request& request) + { + return json { + { "url", JSONCoder<String>::encode(request.url) }, + { "referrer", JSONCoder<String>::encode(request.referrer) }, + { "policy", JSONCoder<int>::encode(request.policy) }, + { "method", JSONCoder<String>::encode(request.method) }, + { "headers", JSONCoder<Headers>::encode(request.headers) } + }; + } + + static Request decode(const json& jRequest) + { + return Request { + JSONCoder<String>::decode(jRequest["url"]), + JSONCoder<String>::decode(jRequest["referrer"]), + JSONCoder<int>::decode(jRequest["policy"]), + JSONCoder<String>::decode(jRequest["method"]), + JSONCoder<Headers>::decode(jRequest["headers"]) + }; + } +}; + +template<> +struct JSONCoder<Response> { + static json encode(const Response& response) + { + return json { + { "url", JSONCoder<String>::encode(response.url) }, + { "mimeType", JSONCoder<String>::encode(response.mimeType) }, + { "expectedLength", JSONCoder<long long>::encode(response.expectedLength) }, + { "textEncodingName", JSONCoder<String>::encode(response.textEncodingName) }, + { "version", JSONCoder<String>::encode(response.version) }, + { "status", JSONCoder<int>::encode(response.status) }, + { "reason", JSONCoder<String>::encode(response.reason) }, + { "headers", JSONCoder<Headers>::encode(response.headers) } + }; + } + + static Response decode(const json& jResponse) + { + return Response { + JSONCoder<String>::decode(jResponse["url"]), + JSONCoder<String>::decode(jResponse["mimeType"]), + JSONCoder<long long>::decode(jResponse["expectedLength"]), + JSONCoder<String>::decode(jResponse["textEncodingName"]), + JSONCoder<String>::decode(jResponse["version"]), + JSONCoder<int>::decode(jResponse["status"]), + JSONCoder<String>::decode(jResponse["reason"]), + JSONCoder<Headers>::decode(jResponse["headers"]) + }; + } +}; + +template<> +struct JSONCoder<Error> { + static json encode(const Error& error) + { + return json { + { "domain", JSONCoder<String>::encode(error.domain) }, + { "failingURL", JSONCoder<String>::encode(error.failingURL) }, + { "localizedDescription", JSONCoder<String>::encode(error.localizedDescription) }, + { "errorCode", JSONCoder<int>::encode(error.errorCode) }, + { "type", JSONCoder<int>::encode(error.type) } + }; + } + + static Error decode(const json& jError) + { + return Error { + JSONCoder<String>::decode(jError["domain"]), + JSONCoder<String>::decode(jError["failingURL"]), + JSONCoder<String>::decode(jError["localizedDescription"]), + JSONCoder<int>::decode(jError["errorCode"]), + JSONCoder<int>::decode(jError["type"]) + }; + } +}; + +template<> +struct JSONCoder<WebCore::SharedBuffer> { + static json encode(const WebCore::SharedBuffer& data) + { + Vector<char> buffer; + base64Encode(data.data(), data.size(), buffer); + return json(&buffer[0], buffer.size()); + } + + static Ref<WebCore::SharedBuffer> decode(const json& jData) + { + Vector<char> data; + const auto& str = jData.get_ref<const std::string&>(); + auto result = base64Decode(str.c_str(), str.size(), data); + ASSERT_UNUSED(result, result); + return WebCore::SharedBuffer::adoptVector(data); + } +}; + +template<> +struct JSONCoder<RequestSentEvent> { + static json encode(const RequestSentEvent& event) + { + return json { + { "type", JSONCoder<const char*>::encode(event.typeName) }, + { "time", JSONCoder<CaptureTimeType>::encode(event.time) }, + { "request", JSONCoder<Request>::encode(event.request) } + }; + } + + static RequestSentEvent decode(const json& jEvent) + { + return RequestSentEvent { + JSONCoder<CaptureTimeType>::decode(jEvent["time"]), + JSONCoder<Request>::decode(jEvent["request"]) + }; + } +}; + +template<> +struct JSONCoder<ResponseReceivedEvent> { + static json encode(const ResponseReceivedEvent& event) + { + return json { + { "type", JSONCoder<const char*>::encode(event.typeName) }, + { "time", JSONCoder<CaptureTimeType>::encode(event.time) }, + { "response", JSONCoder<Response>::encode(event.response) } + }; + } + + static ResponseReceivedEvent decode(const json& jEvent) + { + return ResponseReceivedEvent { + JSONCoder<CaptureTimeType>::decode(jEvent["time"]), + JSONCoder<Response>::decode(jEvent["response"]) + }; + } +}; + +template<> +struct JSONCoder<RedirectReceivedEvent> { + static json encode(const RedirectReceivedEvent& event) + { + return json { + { "type", JSONCoder<const char*>::encode(event.typeName) }, + { "time", JSONCoder<CaptureTimeType>::encode(event.time) }, + { "request", JSONCoder<Request>::encode(event.request) }, + { "response", JSONCoder<Response>::encode(event.response) } + }; + } + + static RedirectReceivedEvent decode(const json& jEvent) + { + return RedirectReceivedEvent { + JSONCoder<CaptureTimeType>::decode(jEvent["time"]), + JSONCoder<Request>::decode(jEvent["request"]), + JSONCoder<Response>::decode(jEvent["response"]) + }; + } +}; + +template<> +struct JSONCoder<RedirectSentEvent> { + static json encode(const RedirectSentEvent& event) + { + return json { + { "type", JSONCoder<const char*>::encode(event.typeName) }, + { "time", JSONCoder<CaptureTimeType>::encode(event.time) }, + { "request", JSONCoder<Request>::encode(event.request) }, + }; + } + + static RedirectSentEvent decode(const json& jEvent) + { + return RedirectSentEvent { + JSONCoder<CaptureTimeType>::decode(jEvent["time"]), + JSONCoder<Request>::decode(jEvent["request"]) + }; + } +}; + +template<> +struct JSONCoder<DataReceivedEvent> { + static json encode(const DataReceivedEvent& event) + { + return json { + { "type", JSONCoder<const char*>::encode(event.typeName) }, + { "time", JSONCoder<CaptureTimeType>::encode(event.time) }, + { "data", JSONCoder<WebCore::SharedBuffer>::encode(event.data.get()) } + }; + } + + static DataReceivedEvent decode(const json& jEvent) + { + return DataReceivedEvent { + JSONCoder<CaptureTimeType>::decode(jEvent["time"]), + JSONCoder<WebCore::SharedBuffer>::decode(jEvent["data"]) + }; + } +}; + +template<> +struct JSONCoder<FinishedEvent> { + static json encode(const FinishedEvent& event) + { + return json { + { "type", JSONCoder<const char*>::encode(event.typeName) }, + { "time", JSONCoder<CaptureTimeType>::encode(event.time) }, + { "error", JSONCoder<Error>::encode(event.error) } + }; + } + + static FinishedEvent decode(const json& jEvent) + { + return FinishedEvent { + JSONCoder<CaptureTimeType>::decode(jEvent["time"]), + JSONCoder<Error>::decode(jEvent["error"]) + }; + } +}; + +std::string eventToString(const CaptureEvent& event) +{ + json result; + + WTF::visit([&result](const auto& event) { + using EventType = std::decay_t<decltype(event)>; + result = JSONCoder<EventType>::encode(event); + }, event); + + return result.dump(4); +} + +OptionalCaptureEvent stringToEvent(const char* jsonStr) +{ + auto jValue = json::parse(jsonStr); + const auto& type = jValue["type"].get_ref<const std::string&>(); + + OptionalCaptureEvent result { std::nullopt }; + brigand::for_each<CaptureEvent>([&](auto T) { + using Type = typename decltype(T)::type; + if (!result && type == Type::typeName) + result = OptionalCaptureEvent(JSONCoder<Type>::decode(jValue)); + }); + return result; +} + +} // namespace NetworkCapture +} // namespace WebKit + +#endif // ENABLE(NETWORK_CAPTURE) |