Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HttpClientConnection class #1624

Merged
merged 4 commits into from
Feb 15, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Sming/SmingCore/Network/Http/HttpClientConnection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/****
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
* Created 2015 by Skurydin Alexey
* http://github.com/anakod/Sming
* All files of the Sming Core are provided under the LGPL v3 license.
*
* HttpClientConnection.cpp
*
****/

#include "HttpClientConnection.h"
#include "Data/Stream/FileStream.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the FileStream include needed here?


HttpClientConnection::~HttpClientConnection()
{
// ObjectQueue doesn't own its objects
while(requestQueue.count() != 0) {
delete requestQueue.dequeue();
}
#ifdef ENABLE_SSL
delete sslSessionId;
#endif
}

bool HttpClientConnection::send(HttpRequest* request)
{
if(!requestQueue.enqueue(request)) {
// the queue is full and we cannot add more requests at the time.
debug_e("The request queue is full at the moment");
delete request;
return false;
}

bool useSsl = (request->uri.Protocol == HTTPS_URL_PROTOCOL);

#ifdef ENABLE_SSL
// Based on the URL decide if we should reuse the SSL and TCP pool
if(useSsl) {
if(sslSessionId == nullptr) {
sslSessionId = new SslSessionId;
}
addSslOptions(request->getSslOptions());
pinCertificate(request->sslFingerprints);
setSslKeyCert(request->sslKeyCertPair);
}
#endif

return connect(request->uri.Host, request->uri.Port, useSsl);
}
43 changes: 43 additions & 0 deletions Sming/SmingCore/Network/Http/HttpClientConnection.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/****
* Sming Framework Project - Open Source framework for high efficiency native ESP8266 development.
* Created 2015 by Skurydin Alexey
* http://github.com/anakod/Sming
* All files of the Sming Core are provided under the LGPL v3 license.
*
* HttpClientConnection.h
*
****/

/** @defgroup HTTP client connection
* @brief Provides HTTP/S client connection
* @ingroup http
* @{
*/

#ifndef _SMING_CORE_NETWORK_HTTP_HTTP_CLIENT_CONNECTION_H_
#define _SMING_CORE_NETWORK_HTTP_HTTP_CLIENT_CONNECTION_H_

#include "HttpConnection.h"

class HttpClientConnection : public HttpConnection
{
public:
HttpClientConnection() : HttpConnection(&requestQueue)
{
}

virtual ~HttpClientConnection();

/** @brief Queue a request
* @param request
* @retval bool true on success
* @note we take ownership of the request. On error, it is destroyed before returning.
*/
bool send(HttpRequest* request);

private:
RequestQueue requestQueue;
};

/** @} */
#endif /* _SMING_CORE_NETWORK_HTTP_HTTP_CLIENT_CONNECTION_H_ */
2 changes: 1 addition & 1 deletion Sming/SmingCore/Network/Http/HttpRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ typedef Delegate<int(HttpConnection& client, bool successful)> RequestCompletedD
*/
class HttpRequest
{
friend class HttpClient;
friend class HttpConnection;
friend class HttpClientConnection;
friend class HttpServerConnection;

public:
Expand Down
146 changes: 43 additions & 103 deletions Sming/SmingCore/Network/HttpClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,141 +13,81 @@
#include "HttpClient.h"
#include "Data/Stream/FileStream.h"

HashMap<String, HttpClientConnection*> HttpClient::httpConnectionPool;

/* Low Level Methods */
bool HttpClient::send(HttpRequest* request)
{
String cacheKey = getCacheKey(request->uri);
bool useSsl = (request->uri.Protocol == HTTPS_URL_PROTOCOL);

if(!queue.contains(cacheKey)) {
queue[cacheKey] = new RequestQueue;
}

if(!queue[cacheKey]->enqueue(request)) {
// the queue is full and we cannot add more requests at the time.
debug_e("The request queue is full at the moment");
delete request;
return false;
}

if(httpConnectionPool.contains(cacheKey) && httpConnectionPool[cacheKey]->getConnectionState() > eTCS_Connecting &&
!httpConnectionPool[cacheKey]->isActive()) {
debug_d("Removing stale connection: State: %d, Active: %d",
(int)httpConnectionPool[cacheKey]->getConnectionState(),
(httpConnectionPool[cacheKey]->isActive() ? 1 : 0));
delete httpConnectionPool[cacheKey];
httpConnectionPool[cacheKey] = NULL;
httpConnectionPool.remove(cacheKey);
}

if(!httpConnectionPool.contains(cacheKey)) {
debug_d("Creating new httpConnection");
httpConnectionPool[cacheKey] = new HttpConnection(queue[cacheKey]);
HttpClientConnection* connection = nullptr;

int i = httpConnectionPool.indexOf(cacheKey);
if(i >= 0) {
// Check existing connection
connection = httpConnectionPool.valueAt(i);
if(connection->getConnectionState() > eTCS_Connecting && !connection->isActive()) {
debug_d("Removing stale connection: State: %d, Active: %d", connection->getConnectionState(),
connection->isActive());
delete connection;
connection = nullptr;
httpConnectionPool.removeAt(i);
}
}

#ifdef ENABLE_SSL
// Based on the URL decide if we should reuse the SSL and TCP pool
if(useSsl) {
if(!sslSessionIdPool.contains(cacheKey)) {
sslSessionIdPool[cacheKey] = new SslSessionId;
if(connection == nullptr) {
debug_d("Creating new HttpClientConnection");
connection = new HttpClientConnection();
if(connection == nullptr) {
// Out of memory
delete request;
return false;
}
httpConnectionPool[cacheKey]->addSslOptions(request->getSslOptions());
httpConnectionPool[cacheKey]->pinCertificate(request->sslFingerprints);
httpConnectionPool[cacheKey]->setSslKeyCert(request->sslKeyCertPair);
httpConnectionPool[cacheKey]->sslSessionId = sslSessionIdPool[cacheKey];
httpConnectionPool[cacheKey] = connection;
}
#endif

return httpConnectionPool[cacheKey]->connect(request->uri.Host, request->uri.Port, useSsl);
return connection->send(request);
}

// Convenience methods

bool HttpClient::downloadString(const String& url, RequestCompletedDelegate requestComplete)
bool HttpClient::downloadFile(const String& url, const String& saveFileName, RequestCompletedDelegate requestComplete)
{
return send(request(url)->setMethod(HTTP_GET)->onRequestComplete(requestComplete));
}

bool HttpClient::downloadFile(const String& url, const String& saveFileName,
RequestCompletedDelegate requestComplete /* = NULL */)
{
URL uri = URL(url);
URL uri(url);

String file;
if(saveFileName.length() == 0) {
file = uri.Path;
int p = file.lastIndexOf('/');
if(p != -1)
file = file.substring(p + 1);
} else
if(p >= 0) {
file.remove(0, p + 1);
}
} else {
file = saveFileName;
}

FileStream* fileStream = new FileStream();
fileStream->open(file, eFO_CreateNewAlways | eFO_WriteOnly);
auto fileStream = new FileStream();
if(!fileStream->open(file, eFO_CreateNewAlways | eFO_WriteOnly)) {
debug_e("HttpClient failed to open \"%s\"", file.c_str());
return false;
}

return send(request(url)->setResponseStream(fileStream)->setMethod(HTTP_GET)->onRequestComplete(requestComplete));
return send(
createRequest(url)->setResponseStream(fileStream)->setMethod(HTTP_GET)->onRequestComplete(requestComplete));
}

// end convenience methods

HttpRequest* HttpClient::request(const String& url)
{
return new HttpRequest(URL(url));
}

HashMap<String, HttpConnection*> HttpClient::httpConnectionPool;
HashMap<String, RequestQueue*> HttpClient::queue;

#ifdef ENABLE_SSL
HashMap<String, SslSessionId*> HttpClient::sslSessionIdPool;

void HttpClient::freeSslSessionPool()
{
for(unsigned i = 0; i < sslSessionIdPool.count(); i++) {
String key = sslSessionIdPool.keyAt(i);
delete sslSessionIdPool[key];
sslSessionIdPool[key] = NULL;
}
sslSessionIdPool.clear();
}
#endif

void HttpClient::freeRequestQueue()
{
for(unsigned i = 0; i < queue.count(); i++) {
String key = queue.keyAt(i);
RequestQueue* requestQueue = queue[key];
HttpRequest* request = requestQueue->dequeue();
while(request != NULL) {
delete request;
request = requestQueue->dequeue();
}
queue[key]->flush();
delete queue[key];
}
queue.clear();
}

void HttpClient::freeHttpConnectionPool()
void HttpClient::cleanup()
{
for(unsigned i = 0; i < httpConnectionPool.count(); i++) {
String key = httpConnectionPool.keyAt(i);
delete httpConnectionPool[key];
httpConnectionPool[key] = NULL;
httpConnectionPool.remove(key);
auto& connection = httpConnectionPool.valueAt(i);
delete connection;
connection = nullptr;
}
httpConnectionPool.clear();
}

void HttpClient::cleanup()
{
#ifdef ENABLE_SSL
freeSslSessionPool();
#endif
freeHttpConnectionPool();
freeRequestQueue();
}

HttpClient::~HttpClient()
{
// DON'T call cleanup.
Expand All @@ -158,5 +98,5 @@ HttpClient::~HttpClient()

String HttpClient::getCacheKey(URL url)
{
return String(url.Host) + ":" + String(url.Port);
return url.Host + ':' + url.Port;
}
55 changes: 28 additions & 27 deletions Sming/SmingCore/Network/HttpClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,39 +22,43 @@
#include "TcpClient.h"
#include "Http/HttpCommon.h"
#include "Http/HttpRequest.h"
#include "Http/HttpConnection.h"
#include "Http/HttpClientConnection.h"

class HttpClient
{
public:
/* High-Level Method */
/* High-Level Methods */

__forceinline bool sendRequest(const String& url, RequestCompletedDelegate requestComplete)
bool sendRequest(const String& url, RequestCompletedDelegate requestComplete)
{
return send(request(url)->setMethod(HTTP_GET)->onRequestComplete(requestComplete));
return send(createRequest(url)->setMethod(HTTP_GET)->onRequestComplete(requestComplete));
}

__forceinline bool sendRequest(const HttpMethod method, const String& url, const HttpHeaders& headers,
RequestCompletedDelegate requestComplete)
bool sendRequest(const HttpMethod method, const String& url, const HttpHeaders& headers,
RequestCompletedDelegate requestComplete)
{
return send(request(url)->setMethod(method)->setHeaders(headers)->onRequestComplete(requestComplete));
return send(createRequest(url)->setMethod(method)->setHeaders(headers)->onRequestComplete(requestComplete));
}

__forceinline bool sendRequest(const HttpMethod method, const String& url, const HttpHeaders& headers,
const String& body, RequestCompletedDelegate requestComplete)
bool sendRequest(const HttpMethod method, const String& url, const HttpHeaders& headers, const String& body,
RequestCompletedDelegate requestComplete)
{
return send(
request(url)->setMethod(method)->setHeaders(headers)->setBody(body)->onRequestComplete(requestComplete));
return send(createRequest(url)->setMethod(method)->setHeaders(headers)->setBody(body)->onRequestComplete(
requestComplete));
}

bool downloadString(const String& url, RequestCompletedDelegate requestComplete);
bool downloadString(const String& url, RequestCompletedDelegate requestComplete)
{
return send(createRequest(url)->setMethod(HTTP_GET)->onRequestComplete(requestComplete));
}

__forceinline bool downloadFile(const String& url, RequestCompletedDelegate requestComplete = NULL)
bool downloadFile(const String& url, RequestCompletedDelegate requestComplete = nullptr)
{
return downloadFile(url, "", requestComplete);
return downloadFile(url, nullptr, requestComplete);
}

bool downloadFile(const String& url, const String& saveFileName, RequestCompletedDelegate requestComplete = NULL);
bool downloadFile(const String& url, const String& saveFileName,
RequestCompletedDelegate requestComplete = nullptr);

/* Low Level Methods */

Expand All @@ -68,13 +72,15 @@ class HttpClient
*/
bool send(HttpRequest* request);

HttpRequest* request(const String& url);
HttpRequest* request(const String& url) __attribute__((deprecated))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, put here in doc comments also @deprecated. This way doxygen will add the method automatically to the deprecated list.

{
return createRequest(url);
}

#ifdef ENABLE_SSL
static void freeSslSessionPool();
#endif
static void freeHttpConnectionPool();
static void freeRequestQueue();
HttpRequest* createRequest(const String& url)
{
return new HttpRequest(URL(url));
}

/**
* Use this method to clean all request queues and object pools
Expand All @@ -87,12 +93,7 @@ class HttpClient
String getCacheKey(URL url);

protected:
static HashMap<String, HttpConnection*> httpConnectionPool;
static HashMap<String, RequestQueue*> queue;

#ifdef ENABLE_SSL
static HashMap<String, SslSessionId*> sslSessionIdPool;
#endif
static HashMap<String, HttpClientConnection*> httpConnectionPool;
};

/** @} */
Expand Down