blob: a4f8c0282767b0b48770a048ee852f166e07a7f5 [file] [log] [blame]
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
* Copyright (C) 2018 Sony Interactive Entertainment Inc.
*
* 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. ``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
* 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
#include "CurlProxySettings.h"
#include "CurlSSLHandle.h"
#include <wtf/Lock.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/Noncopyable.h>
#include <wtf/Seconds.h>
#include <wtf/Threading.h>
#include <wtf/URL.h>
#if OS(WINDOWS)
#include <windows.h>
#include <winsock2.h>
#endif
#include <curl/curl.h>
namespace WebCore {
// Values taken from http://www.browserscope.org/ following
// the rule "Do What Every Other Modern Browser Is Doing".
const long CurlDefaultMaxConnects { -1 }; // -1 : Does not set CURLMOPT_MAXCONNECTS
const long CurlDefaultMaxTotalConnections { 17 };
const long CurlDefaultMaxHostConnections { 6 };
// CurlGlobal --------------------------------------------
// to make the initialization of libcurl happen before other initialization of CurlContext
class CurlGlobal {
protected:
CurlGlobal()
{
curl_global_init(CURL_GLOBAL_ALL);
}
virtual ~CurlGlobal()
{
curl_global_cleanup();
}
};
// CurlShareHandle --------------------------------------------
class CurlShareHandle {
WTF_MAKE_NONCOPYABLE(CurlShareHandle);
public:
CurlShareHandle();
~CurlShareHandle();
CURLSH* handle() const { return m_shareHandle; }
private:
static void lockCallback(CURL*, curl_lock_data, curl_lock_access, void*);
static void unlockCallback(CURL*, curl_lock_data, void*);
static Lock* mutexFor(curl_lock_data);
CURLSH* m_shareHandle { nullptr };
};
// CurlContext --------------------------------------------
class CurlRequestScheduler;
class CurlContext : public CurlGlobal {
WTF_MAKE_NONCOPYABLE(CurlContext);
friend NeverDestroyed<CurlContext>;
public:
WEBCORE_EXPORT static CurlContext& singleton();
virtual ~CurlContext();
const CurlShareHandle& shareHandle() { return m_shareHandle; }
CurlRequestScheduler& scheduler() { return *m_scheduler; }
// Proxy
const CurlProxySettings& proxySettings() const { return m_proxySettings; }
void setProxySettings(CurlProxySettings&& settings) { m_proxySettings = WTFMove(settings); }
void setProxyUserPass(const String& user, const String& password) { m_proxySettings.setUserPass(user, password); }
void setDefaultProxyAuthMethod() { m_proxySettings.setDefaultAuthMethod(); }
void setProxyAuthMethod(long authMethod) { m_proxySettings.setAuthMethod(authMethod); }
// SSL
CurlSSLHandle& sslHandle() { return m_sslHandle; }
// HTTP/2
bool isHttp2Enabled() const;
// Timeout
Seconds dnsCacheTimeout() const { return m_dnsCacheTimeout; }
Seconds connectTimeout() const { return m_connectTimeout; }
Seconds defaultTimeoutInterval() const { return m_defaultTimeoutInterval; }
#ifndef NDEBUG
FILE* getLogFile() const { return m_logFile; }
bool isVerbose() const { return m_verbose; }
#endif
private:
CurlContext();
void initShareHandle();
CurlProxySettings m_proxySettings;
CurlShareHandle m_shareHandle;
CurlSSLHandle m_sslHandle;
std::unique_ptr<CurlRequestScheduler> m_scheduler;
Seconds m_dnsCacheTimeout { Seconds::fromMinutes(5) };
Seconds m_connectTimeout { 30.0 };
Seconds m_defaultTimeoutInterval { 60.0 };
#ifndef NDEBUG
FILE* m_logFile { nullptr };
bool m_verbose { false };
#endif
};
// CurlMultiHandle --------------------------------------------
class CurlMultiHandle {
WTF_MAKE_FAST_ALLOCATED;
WTF_MAKE_NONCOPYABLE(CurlMultiHandle);
public:
CurlMultiHandle();
~CurlMultiHandle();
void setMaxConnects(long);
void setMaxTotalConnections(long);
void setMaxHostConnections(long);
CURLMcode addHandle(CURL*);
CURLMcode removeHandle(CURL*);
CURLMcode getFdSet(fd_set&, fd_set&, fd_set&, int&);
CURLMcode perform(int&);
CURLMsg* readInfo(int&);
private:
CURLM* m_multiHandle { nullptr };
};
// CurlSList -------------------------------------------------
class CurlSList {
public:
CurlSList() { }
~CurlSList() { clear(); }
operator struct curl_slist** () { return &m_list; }
const struct curl_slist* head() const { return m_list; }
bool isEmpty() const { return !m_list; }
void clear()
{
if (m_list) {
curl_slist_free_all(m_list);
m_list = nullptr;
}
}
void append(const char* str) { m_list = curl_slist_append(m_list, str); }
void append(const String& str) { append(str.latin1().data()); }
private:
struct curl_slist* m_list { nullptr };
};
// CurlHandle -------------------------------------------------
class CertificateInfo;
class CurlSSLVerifier;
class HTTPHeaderMap;
class NetworkLoadMetrics;
class CurlHandle {
WTF_MAKE_FAST_ALLOCATED;
WTF_MAKE_NONCOPYABLE(CurlHandle);
public:
enum class VerifyPeer {
Disable = 0L,
Enable = 1L
};
enum class VerifyHost {
LooseNameCheck = 0,
StrictNameCheck = 2
};
CurlHandle();
virtual ~CurlHandle();
CURL* handle() const { return m_handle; }
const URL& url() const { return m_url; }
CURLcode perform();
CURLcode pause(int);
static const String errorDescription(CURLcode);
void enableShareHandle();
void setUrl(const URL&);
void enableSSLForHost(const String&);
void appendRequestHeaders(const HTTPHeaderMap&);
void appendRequestHeader(const String& name, const String& value);
void appendRequestHeader(const String& name);
void removeRequestHeader(const String& name);
void enableHttp();
void enableHttpGetRequest();
void enableHttpHeadRequest();
void enableHttpPostRequest();
void setPostFields(const char*, long);
void setPostFieldLarge(curl_off_t);
void enableHttpPutRequest();
void setInFileSizeLarge(curl_off_t);
void setHttpCustomRequest(const String&);
void enableConnectionOnly();
void enableAcceptEncoding();
void enableAllowedProtocols();
void setHttpAuthUserPass(const String&, const String&, long authType = CURLAUTH_ANY);
void disableServerTrustEvaluation();
void setCACertPath(const char*);
void setSslVerifyPeer(VerifyPeer);
void setSslVerifyHost(VerifyHost);
void setSslCert(const char*);
void setSslCertType(const char*);
void setSslKeyPassword(const char*);
void setSslCipherList(const char*);
void enableProxyIfExists();
void setDnsCacheTimeout(Seconds);
void setConnectTimeout(Seconds);
void setTimeout(Seconds);
// Callback function
void setHeaderCallbackFunction(curl_write_callback, void*);
void setWriteCallbackFunction(curl_write_callback, void*);
void setReadCallbackFunction(curl_read_callback, void*);
void setSslCtxCallbackFunction(curl_ssl_ctx_callback, void*);
void setDebugCallbackFunction(curl_debug_callback, void*);
// Status
Optional<String> getProxyUrl();
Optional<long> getResponseCode();
Optional<long> getHttpConnectCode();
Optional<long long> getContentLength();
Optional<long> getHttpAuthAvail();
Optional<long> getProxyAuthAvail();
Optional<long> getHttpVersion();
Optional<NetworkLoadMetrics> getNetworkLoadMetrics(const WTF::Seconds& domainLookupStart);
void addExtraNetworkLoadMetrics(NetworkLoadMetrics&);
int sslErrors() const;
Optional<CertificateInfo> certificateInfo() const;
static long long maxCurlOffT();
#ifndef NDEBUG
void enableVerboseIfUsed();
void enableStdErrIfUsed();
#endif
private:
void enableRequestHeaders();
static int expectedSizeOfCurlOffT();
static CURLcode willSetupSslCtxCallback(CURL*, void* sslCtx, void* userData);
CURLcode willSetupSslCtx(void* sslCtx);
CURL* m_handle { nullptr };
char m_errorBuffer[CURL_ERROR_SIZE] { };
URL m_url;
CurlSList m_requestHeaders;
std::unique_ptr<CurlSSLVerifier> m_sslVerifier;
};
class CurlSocketHandle : public CurlHandle {
WTF_MAKE_NONCOPYABLE(CurlSocketHandle);
public:
struct WaitResult {
bool readable { false };
bool writable { false };
};
CurlSocketHandle(const URL&, Function<void(CURLcode)>&& errorHandler);
bool connect();
size_t send(const uint8_t*, size_t);
Optional<size_t> receive(uint8_t*, size_t);
Optional<WaitResult> wait(const Seconds& timeout, bool alsoWaitForWrite);
private:
Function<void(CURLcode)> m_errorHandler;
};
} // namespace WebCore