From 641cb063bc71acc7f29d25b12c8713a8beb2018c Mon Sep 17 00:00:00 2001 From: Mattes D Date: Mon, 22 Aug 2016 19:53:34 +0200 Subject: cTCPLink supports TLS out of the box. --- src/HTTP/UrlClient.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++++++---- src/HTTP/UrlClient.h | 28 +++++++++++++------ 2 files changed, 88 insertions(+), 14 deletions(-) (limited to 'src/HTTP') diff --git a/src/HTTP/UrlClient.cpp b/src/HTTP/UrlClient.cpp index f9e642b22..9346882f1 100644 --- a/src/HTTP/UrlClient.cpp +++ b/src/HTTP/UrlClient.cpp @@ -7,6 +7,8 @@ #include "UrlClient.h" #include "UrlParser.h" #include "HTTPMessageParser.h" +#include "../PolarSSL++/X509Cert.h" +#include "../PolarSSL++/CryptoKey.h" @@ -67,6 +69,38 @@ public: bool ShouldAllowRedirects() const; + cX509CertPtr GetOwnCert() const + { + auto itr = m_Options.find("OwnCert"); + if (itr == m_Options.end()) + { + return nullptr; + } + cX509CertPtr cert; + if (!cert->Parse(itr->second.data(), itr->second.size())) + { + LOGD("OwnCert failed to parse"); + return nullptr; + } + return cert; + } + + cCryptoKeyPtr GetOwnPrivKey() const + { + auto itr = m_Options.find("OwnPrivKey"); + if (itr == m_Options.end()) + { + return nullptr; + } + cCryptoKeyPtr key; + auto passItr = m_Options.find("OwnPrivKeyPassword"); + auto pass = (passItr == m_Options.end()) ? AString() : passItr->second; + if (!key->ParsePrivate(itr->second.data(), itr->second.size(), pass)) + { + return nullptr; + } + return key; + } protected: @@ -148,6 +182,9 @@ protected: } + // cTCPLink::cCallbacks override: TLS handshake completed on the link: + virtual void OnTlsHandshakeCompleted(void) override; + /** Called when there's data incoming from the remote peer. */ virtual void OnReceivedData(const char * a_Data, size_t a_Length) override; @@ -188,6 +225,9 @@ public: /** Called when there's data incoming from the remote peer. */ virtual void OnReceivedData(const char * a_Data, size_t a_Length) = 0; + /** Called when the TLS handshake has completed on the underlying link. */ + virtual void OnTlsHandshakeCompleted(void) = 0; + /** Called when the remote end closes the connection. The link is still available for connection information query (IP / port). Sending data on the link is not an error, but the data won't be delivered. */ @@ -223,7 +263,7 @@ public: m_Link = &a_Link; if (m_IsTls) { - // TODO: Start TLS + m_Link->StartTLSClient(m_ParentRequest.GetOwnCert(), m_ParentRequest.GetOwnPrivKey()); } else { @@ -231,9 +271,12 @@ public: } } + + /** Sends the HTTP request over the link. + Common code for both HTTP and HTTPS. */ void SendRequest() { - // Send the request: + // Compose the request line: auto requestLine = m_ParentRequest.m_UrlPath; if (requestLine.empty()) { @@ -245,6 +288,8 @@ public: requestLine.append(m_ParentRequest.m_UrlQuery); } m_Link->Send(Printf("%s %s HTTP/1.1\r\n", m_ParentRequest.m_Method.c_str(), requestLine.c_str())); + + // Send the headers: m_Link->Send(Printf("Host: %s\r\n", m_ParentRequest.m_UrlHost.c_str())); m_Link->Send(Printf("Content-Length: %u\r\n", static_cast(m_ParentRequest.m_Body.size()))); for (auto itr = m_ParentRequest.m_Headers.cbegin(), end = m_ParentRequest.m_Headers.cend(); itr != end; ++itr) @@ -252,6 +297,8 @@ public: m_Link->Send(Printf("%s: %s\r\n", itr->first.c_str(), itr->second.c_str())); } // for itr - m_Headers[] m_Link->Send("\r\n", 2); + + // Send the body: m_Link->Send(m_ParentRequest.m_Body); // Notify the callbacks that the request has been sent: @@ -270,6 +317,12 @@ public: } + virtual void OnTlsHandshakeCompleted(void) override + { + SendRequest(); + } + + virtual void OnRemoteClosed(void) override { m_Link = nullptr; @@ -385,12 +438,12 @@ protected: /** The network link. */ cTCPLink * m_Link; - /** If true, the TLS should be started on the link before sending the request (used for https). */ - bool m_IsTls; - /** Parser of the HTTP response message. */ cHTTPMessageParser m_Parser; + /** If true, the TLS should be started on the link before sending the request (used for https). */ + bool m_IsTls; + /** Set to true if the first line contains a redirecting HTTP status code and the options specify to follow redirects. If true, and the parent request allows redirects, neither headers not the body contents are reported through the callbacks, and after the entire request is parsed, the redirect is attempted. */ @@ -475,6 +528,17 @@ void cUrlClientRequest::OnConnected(cTCPLink & a_Link) +void cUrlClientRequest::OnTlsHandshakeCompleted(void) +{ + // Notify the scheme handler and the callbacks: + m_SchemeHandler->OnTlsHandshakeCompleted(); + m_Callbacks.OnTlsHandshakeCompleted(); +} + + + + + void cUrlClientRequest::OnReceivedData(const char * a_Data, size_t a_Length) { auto handler = m_SchemeHandler; diff --git a/src/HTTP/UrlClient.h b/src/HTTP/UrlClient.h index 42086a4f1..652cc76f7 100644 --- a/src/HTTP/UrlClient.h +++ b/src/HTTP/UrlClient.h @@ -5,7 +5,10 @@ /* Options that can be set via the Options parameter to the cUrlClient calls: -"MaxRedirects": The maximum number of allowed redirects before the client refuses a redirect with an error +"MaxRedirects": The maximum number of allowed redirects before the client refuses a redirect with an error +"OwnCert": The client certificate to use, if requested by the server. Any string that can be parsed by cX509Cert. +"OwnPrivKey": The private key appropriate for OwnCert. Any string that can be parsed by cCryptoKey. +"OwnPrivKeyPassword": The password for OwnPrivKey. If not present or empty, no password is assumed. Behavior: - If a redirect is received, and redirection is allowed, the redirection is reported via OnRedirecting() callback @@ -34,8 +37,11 @@ public: class cCallbacks { public: + // Force a virtual destructor in descendants: + virtual ~cCallbacks() {} + /** Called when the TCP connection is established. */ - virtual void OnConnected(cTCPLink & a_Link) {}; + virtual void OnConnected(cTCPLink & a_Link) {} /** Called for TLS connections, when the server certificate is received. Return true to continue with the request, false to abort. @@ -43,30 +49,34 @@ public: TODO: The certificate parameter needs a representation! */ virtual bool OnCertificateReceived() { return true; } + /** Called for TLS connections, when the TLS handshake has been completed. + An empty default implementation is provided so that clients don't need to reimplement it unless they are interested in the event. */ + virtual void OnTlsHandshakeCompleted() { } + /** Called after the entire request has been sent to the remote peer. */ - virtual void OnRequestSent() {}; + virtual void OnRequestSent() {} /** Called after the first line of the response is parsed, unless the response is an allowed redirect. */ virtual void OnStatusLine(const AString & a_HttpVersion, int a_StatusCode, const AString & a_Rest) {} /** Called when a single HTTP header is received and parsed, unless the response is an allowed redirect Called once for each incoming header. */ - virtual void OnHeader(const AString & a_Key, const AString & a_Value) {}; + virtual void OnHeader(const AString & a_Key, const AString & a_Value) {} /** Called when the HTTP headers have been fully parsed, unless the response is an allowed redirect. There will be no more OnHeader() calls. */ - virtual void OnHeadersFinished() {}; + virtual void OnHeadersFinished() {} /** Called when the next fragment of the response body is received, unless the response is an allowed redirect. This can be called multiple times, as data arrives over the network. */ - virtual void OnBodyData(const void * a_Data, size_t a_Size) {}; + virtual void OnBodyData(const void * a_Data, size_t a_Size) {} /** Called after the response body has been fully reported by OnBody() calls, unless the response is an allowed redirect. There will be no more OnBody() calls. */ - virtual void OnBodyFinished() {}; + virtual void OnBodyFinished() {} /** Called when an asynchronous error is encountered. */ - virtual void OnError(const AString & a_ErrorMsg) {}; + virtual void OnError(const AString & a_ErrorMsg) {} /** Called when a redirect is to be followed. This is called even if the redirecting is prohibited by the options; in such an event, this call will be @@ -74,7 +84,7 @@ public: If a response indicates a redirect (and the request allows redirecting), the regular callbacks OnStatusLine(), OnHeader(), OnHeadersFinished(), OnBodyData() and OnBodyFinished() are not called for such a response; instead, the redirect is silently attempted. */ - virtual void OnRedirecting(const AString & a_NewLocation) {}; + virtual void OnRedirecting(const AString & a_NewLocation) {} }; -- cgit v1.2.3