Skip to content

Add support for http proxy to esp8266 http client #8094

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
247 changes: 148 additions & 99 deletions libraries/ESP8266HTTPClient/src/ESP8266HTTPClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ HTTPClient::~HTTPClient()
if(_currentHeaders) {
delete[] _currentHeaders;
}
if (_uri) {
delete _uri;
}
if (_proxyUri) {
delete _proxyUri;
}
}

void HTTPClient::clear()
Expand All @@ -82,21 +88,13 @@ void HTTPClient::clear()
bool HTTPClient::begin(WiFiClient &client, const String& url) {
_client = &client;

// check for : (http: or https:)
int index = url.indexOf(':');
if(index < 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n");
_uri = parseURI(url);
if(_uri == nullptr) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse url '%s'\n", url.c_str());
return false;
}

String protocol = url.substring(0, index);
if(protocol != "http" && protocol != "https") {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] unknown protocol '%s'\n", protocol.c_str());
return false;
}

_port = (protocol == "https" ? 443 : 80);
return beginInternal(url, protocol.c_str());
return true;
}


Expand All @@ -114,75 +112,16 @@ bool HTTPClient::begin(WiFiClient &client, const String& host, uint16_t port, co
_client = &client;

clear();
_host = host;
_port = port;
_uri = uri;
_protocol = (https ? "https" : "http");
return true;
}


bool HTTPClient::beginInternal(const String& __url, const char* expectedProtocol)
{
String url(__url);

DEBUG_HTTPCLIENT("[HTTP-Client][begin] url: %s\n", url.c_str());
clear();

// check for : (http: or https:
int index = url.indexOf(':');
if(index < 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] failed to parse protocol\n");
return false;
}

_protocol = url.substring(0, index);
url.remove(0, (index + 3)); // remove http:// or https://

if (_protocol == "http") {
// set default port for 'http'
_port = 80;
} else if (_protocol == "https") {
// set default port for 'https'
_port = 443;
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] unsupported protocol: %s\n", _protocol.c_str());
return false;
}

index = url.indexOf('/');
String host = url.substring(0, index);
url.remove(0, index); // remove host part

// get Authorization
index = host.indexOf('@');
if(index >= 0) {
// auth info
String auth = host.substring(0, index);
host.remove(0, index + 1); // remove auth part including @
_base64Authorization = base64::encode(auth, false /* doNewLines */);
}

// get port
index = host.indexOf(':');
if(index >= 0) {
_host = host.substring(0, index); // hostname
host.remove(0, (index + 1)); // remove hostname + :
_port = host.toInt(); // get port
} else {
_host = host;
}
_uri = url;

if ( expectedProtocol != nullptr && _protocol != expectedProtocol) {
DEBUG_HTTPCLIENT("[HTTP-Client][begin] unexpected protocol: %s, expected %s\n", _protocol.c_str(), expectedProtocol);
return false;
}
DEBUG_HTTPCLIENT("[HTTP-Client][begin] host: %s port: %d url: %s\n", _host.c_str(), _port, _uri.c_str());
_uri = new URI{
.host = host,
.port = port,
.protocol = (https ? "https" : "http"),
.path = uri,
.base64Authorization = String(),
};
return true;
}


/**
* end
* called after the payload is handled
Expand Down Expand Up @@ -269,7 +208,7 @@ void HTTPClient::setAuthorization(const char * user, const char * password)
String auth = user;
auth += ':';
auth += password;
_base64Authorization = base64::encode(auth, false /* doNewLines */);
_uri->base64Authorization = base64::encode(auth, false /* doNewLines */);
}
}

Expand All @@ -280,8 +219,8 @@ void HTTPClient::setAuthorization(const char * user, const char * password)
void HTTPClient::setAuthorization(const char * auth)
{
if(auth) {
_base64Authorization = auth;
_base64Authorization.replace(String('\n'), emptyString);
_uri->base64Authorization = auth;
_uri->base64Authorization.replace(String('\n'), emptyString);
}
}

Expand All @@ -305,19 +244,26 @@ bool HTTPClient::setURL(const String& url)
{
// if the new location is only a path then only update the URI
if (url && url[0] == '/') {
_uri = url;
_uri->path = url;
clear();
return true;
}

if (!url.startsWith(_protocol + ':')) {
DEBUG_HTTPCLIENT("[HTTP-Client][setURL] new URL not the same protocol, expected '%s', URL: '%s'\n", _protocol.c_str(), url.c_str());
if (!url.startsWith(_uri->protocol + ":")) {
DEBUG_HTTPCLIENT("[HTTP-Client][setURL] new URL not the same protocol, expected '%s', URL: '%s'\n", _uri->protocol.c_str(), url.c_str());
return false;
}
// disconnect but preserve _client (clear _canReuse so disconnect will close the connection)
_canReuse = false;
disconnect(true);
return beginInternal(url, nullptr);

_uri = parseURI(url);
if(_uri == nullptr) {
DEBUG_HTTPCLIENT("[HTTP-Client][setURL] failed to parse url '%s'\n", url.c_str());
return false;
}

return true;
}

/**
Expand Down Expand Up @@ -761,7 +707,7 @@ void HTTPClient::addHeader(const String& name, const String& value, bool first,
if (!name.equalsIgnoreCase(F("Connection")) &&
!name.equalsIgnoreCase(F("User-Agent")) &&
!name.equalsIgnoreCase(F("Host")) &&
!(name.equalsIgnoreCase(F("Authorization")) && _base64Authorization.length())) {
!(name.equalsIgnoreCase(F("Authorization")) && _uri->base64Authorization.length())) {

String headerLine;
headerLine.reserve(name.length() + value.length() + 4);
Expand Down Expand Up @@ -858,19 +804,106 @@ bool HTTPClient::connect(void)

_client->setTimeout(_tcpTimeout);

if(!_client->connect(_host.c_str(), _port)) {
DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", _host.c_str(), _port);
String* host = &_uri->host;
uint16_t port = _uri->port;
if (_proxyUri != nullptr) {
host = &_proxyUri->host;
port = _proxyUri->port;
DEBUG_HTTPCLIENT("[HTTP-Client] using proxy %s:%u\n", host->c_str(), port);
}

if(!_client->connect(host->c_str(), port)) {
DEBUG_HTTPCLIENT("[HTTP-Client] failed connect to %s:%u\n", host->c_str(), port);
return false;
}

DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", _host.c_str(), _port);
DEBUG_HTTPCLIENT("[HTTP-Client] connected to %s:%u\n", host->c_str(), port);

#ifdef ESP8266
_client->setNoDelay(true);
#endif
return connected();
}

HTTPClient::URI* HTTPClient::parseURI(const String& __url){
String url(__url);
URI* uri = new URI{};

// check for : (http: or https:)
int index = url.indexOf(':');
if(index < 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][parseURI] failed to parse protocol\n");
return nullptr;
}

String protocol = url.substring(0, index);
if(protocol != "http" && protocol != "https") {
DEBUG_HTTPCLIENT("[HTTP-Client][parseURI] unknown protocol '%s'\n", protocol.c_str());
return nullptr;
}

uri->port = (protocol == "https" ? 443 : 80);
// check for : (http: or https:
index = url.indexOf(':');
if(index < 0) {
DEBUG_HTTPCLIENT("[HTTP-Client][parseURI] failed to parse protocol\n");
return nullptr;
}

uri->protocol = url.substring(0, index);
url.remove(0, (index + 3)); // remove http:// or https://

if (uri->protocol == "http") {
// set default port for 'http'
uri->port = 80;
} else if (uri->protocol == "https") {
// set default port for 'https'
uri->port = 443;
} else {
DEBUG_HTTPCLIENT("[HTTP-Client][parseURI] unsupported protocol: %s\n", uri->protocol.c_str());
return nullptr;
}

index = url.indexOf('/');
String host = url.substring(0, index);
url.remove(0, index); // remove host part

// get Authorization
index = host.indexOf('@');
if(index >= 0) {
// auth info
String auth = host.substring(0, index);
host.remove(0, index + 1); // remove auth part including @
uri->base64Authorization = base64::encode(auth, false /* doNewLines */);
}

// get port
index = host.indexOf(':');
if(index >= 0) {
uri->host = host.substring(0, index); // hostname
host.remove(0, (index + 1)); // remove hostname + :
uri->port = host.toInt(); // get port
} else {
uri->host = host;
}
uri->path = url;

DEBUG_HTTPCLIENT("[HTTP-Client][parseURI] host: %s port: %d url: %s\n", uri->host.c_str(), uri->port, uri->path.c_str());
return uri;
}

bool HTTPClient::setProxyHost(const String &proxyUrl)
{
_proxyUri = parseURI(proxyUrl);
if (_proxyUri == nullptr) {
DEBUG_HTTPCLIENT("[HTTP-Client][proxy] failed to parse uri\n");
return false;
}

DEBUG_HTTPCLIENT("[HTTP-Client][proxy] host: %s port: %d\n", _proxyUri->host.c_str(), _proxyUri->port);
return true;
}

/**
* sends HTTP request header
* @param type (GET, POST, ...)
Expand All @@ -884,15 +917,31 @@ bool HTTPClient::sendHeader(const char * type)

String header;
// 128: Arbitrarily chosen to have enough buffer space for avoiding internal reallocations
header.reserve(_headers.length() + _uri.length() +
_base64Authorization.length() + _host.length() + _userAgent.length() + 128);
header.reserve(_headers.length() + _uri->path.length() +
_uri->base64Authorization.length() + _uri->host.length() + _userAgent.length() + 128);
header += type;
header += ' ';
if (_uri.length()) {
header += _uri;

String target;
if (_proxyUri != nullptr) {
target.reserve(_uri->protocol.length() + _uri->path.length() +_uri->host.length() + 8);
// If using proxy, use full uri in request line (http://foo:123/bar)
target += _uri->protocol;
target += F("://");
target += _uri->host;

if (_uri->port != 80 && _uri->port != 443) {
target += ':';
target += String(_uri->port);
}
target += _uri->path.length() ? _uri->path : F("/");
} else if (_uri->path.length()) {
target = _uri->path;
} else {
header += '/';
target = F("/");
}

header += target;
header += F(" HTTP/1.");

if(_useHTTP10) {
Expand All @@ -902,11 +951,11 @@ bool HTTPClient::sendHeader(const char * type)
}

header += F("\r\nHost: ");
header += _host;
if (_port != 80 && _port != 443)
header += _uri->host;
if (_uri->port != 80 && _uri->port != 443)
{
header += ':';
header += String(_port);
header += String(_uri->port);
}
header += F("\r\nUser-Agent: ");
header += _userAgent;
Expand All @@ -915,9 +964,9 @@ bool HTTPClient::sendHeader(const char * type)
header += F("\r\nAccept-Encoding: identity;q=1,chunked;q=0.1,*;q=0");
}

if (_base64Authorization.length()) {
if (_uri->base64Authorization.length()) {
header += F("\r\nAuthorization: Basic ");
header += _base64Authorization;
header += _uri->base64Authorization;
}

header += F("\r\nConnection: ");
Expand Down
Loading