|
@@ -30,27 +30,53 @@
|
|
|
#include "http_client.h"
|
|
|
#include "io/stream_peer_ssl.h"
|
|
|
|
|
|
+const char *HTTPClient::_methods[METHOD_MAX] = {
|
|
|
+ "GET",
|
|
|
+ "HEAD",
|
|
|
+ "POST",
|
|
|
+ "PUT",
|
|
|
+ "DELETE",
|
|
|
+ "OPTIONS",
|
|
|
+ "TRACE",
|
|
|
+ "CONNECT",
|
|
|
+ "PATCH"
|
|
|
+};
|
|
|
+
|
|
|
#ifndef JAVASCRIPT_ENABLED
|
|
|
Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl, bool p_verify_host) {
|
|
|
|
|
|
close();
|
|
|
+
|
|
|
conn_port = p_port;
|
|
|
conn_host = p_host;
|
|
|
|
|
|
- if (conn_host.begins_with("http://")) {
|
|
|
+ ssl = p_ssl;
|
|
|
+ ssl_verify_host = p_verify_host;
|
|
|
+
|
|
|
+ String host_lower = conn_host.to_lower();
|
|
|
+ if (host_lower.begins_with("http://")) {
|
|
|
|
|
|
conn_host = conn_host.replace_first("http://", "");
|
|
|
- } else if (conn_host.begins_with("https://")) {
|
|
|
- //use https
|
|
|
+ } else if (host_lower.begins_with("https://")) {
|
|
|
+
|
|
|
+ ssl = true;
|
|
|
conn_host = conn_host.replace_first("https://", "");
|
|
|
}
|
|
|
|
|
|
- ssl = p_ssl;
|
|
|
- ssl_verify_host = p_verify_host;
|
|
|
+ ERR_FAIL_COND_V(conn_host.length() < HOST_MIN_LEN, ERR_INVALID_PARAMETER);
|
|
|
+
|
|
|
+ if (conn_port < 0) {
|
|
|
+ if (ssl) {
|
|
|
+ conn_port = PORT_HTTPS;
|
|
|
+ } else {
|
|
|
+ conn_port = PORT_HTTP;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
connection = tcp_connection;
|
|
|
|
|
|
if (conn_host.is_valid_ip_address()) {
|
|
|
- //is ip
|
|
|
+ // Host contains valid IP
|
|
|
Error err = tcp_connection->connect_to_host(IP_Address(conn_host), p_port);
|
|
|
if (err) {
|
|
|
status = STATUS_CANT_CONNECT;
|
|
@@ -59,7 +85,7 @@ Error HTTPClient::connect_to_host(const String &p_host, int p_port, bool p_ssl,
|
|
|
|
|
|
status = STATUS_CONNECTING;
|
|
|
} else {
|
|
|
- //is hostname
|
|
|
+ // Host contains hostname and needs to be resolved to IP
|
|
|
resolving = IP::get_singleton()->resolve_hostname_queue_item(conn_host);
|
|
|
status = STATUS_RESOLVING;
|
|
|
}
|
|
@@ -82,24 +108,13 @@ Ref<StreamPeer> HTTPClient::get_connection() const {
|
|
|
Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector<String> &p_headers, const PoolVector<uint8_t> &p_body) {
|
|
|
|
|
|
ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
|
|
|
+ ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
|
|
|
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
|
|
|
ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
|
|
|
|
|
|
- static const char *_methods[METHOD_MAX] = {
|
|
|
- "GET",
|
|
|
- "HEAD",
|
|
|
- "POST",
|
|
|
- "PUT",
|
|
|
- "DELETE",
|
|
|
- "OPTIONS",
|
|
|
- "TRACE",
|
|
|
- "CONNECT",
|
|
|
- "PATCH"
|
|
|
- };
|
|
|
-
|
|
|
String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n";
|
|
|
- if ((ssl && conn_port == 443) || (!ssl && conn_port == 80)) {
|
|
|
- // don't append the standard ports
|
|
|
+ if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
|
|
|
+ // Don't append the standard ports
|
|
|
request += "Host: " + conn_host + "\r\n";
|
|
|
} else {
|
|
|
request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
|
|
@@ -113,17 +128,20 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector
|
|
|
}
|
|
|
if (add_clen) {
|
|
|
request += "Content-Length: " + itos(p_body.size()) + "\r\n";
|
|
|
- //should it add utf8 encoding? not sure
|
|
|
+ // Should it add utf8 encoding?
|
|
|
}
|
|
|
request += "\r\n";
|
|
|
CharString cs = request.utf8();
|
|
|
|
|
|
PoolVector<uint8_t> data;
|
|
|
-
|
|
|
- //Maybe this goes faster somehow?
|
|
|
- for (int i = 0; i < cs.length(); i++) {
|
|
|
- data.append(cs[i]);
|
|
|
+ data.resize(cs.length());
|
|
|
+ {
|
|
|
+ PoolVector<uint8_t>::Write data_write = data.write();
|
|
|
+ for (int i = 0; i < cs.length(); i++) {
|
|
|
+ data_write[i] = cs[i];
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
data.append_array(p_body);
|
|
|
|
|
|
PoolVector<uint8_t>::Read r = data.read();
|
|
@@ -143,24 +161,13 @@ Error HTTPClient::request_raw(Method p_method, const String &p_url, const Vector
|
|
|
Error HTTPClient::request(Method p_method, const String &p_url, const Vector<String> &p_headers, const String &p_body) {
|
|
|
|
|
|
ERR_FAIL_INDEX_V(p_method, METHOD_MAX, ERR_INVALID_PARAMETER);
|
|
|
+ ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER);
|
|
|
ERR_FAIL_COND_V(status != STATUS_CONNECTED, ERR_INVALID_PARAMETER);
|
|
|
ERR_FAIL_COND_V(connection.is_null(), ERR_INVALID_DATA);
|
|
|
|
|
|
- static const char *_methods[METHOD_MAX] = {
|
|
|
- "GET",
|
|
|
- "HEAD",
|
|
|
- "POST",
|
|
|
- "PUT",
|
|
|
- "DELETE",
|
|
|
- "OPTIONS",
|
|
|
- "TRACE",
|
|
|
- "CONNECT",
|
|
|
- "PATCH"
|
|
|
- };
|
|
|
-
|
|
|
String request = String(_methods[p_method]) + " " + p_url + " HTTP/1.1\r\n";
|
|
|
- if ((ssl && conn_port == 443) || (!ssl && conn_port == 80)) {
|
|
|
- // don't append the standard ports
|
|
|
+ if ((ssl && conn_port == PORT_HTTPS) || (!ssl && conn_port == PORT_HTTP)) {
|
|
|
+ // Don't append the standard ports
|
|
|
request += "Host: " + conn_host + "\r\n";
|
|
|
} else {
|
|
|
request += "Host: " + conn_host + ":" + itos(conn_port) + "\r\n";
|
|
@@ -174,7 +181,7 @@ Error HTTPClient::request(Method p_method, const String &p_url, const Vector<Str
|
|
|
}
|
|
|
if (add_clen) {
|
|
|
request += "Content-Length: " + itos(p_body.utf8().length()) + "\r\n";
|
|
|
- //should it add utf8 encoding? not sure
|
|
|
+ // Should it add utf8 encoding?
|
|
|
}
|
|
|
request += "\r\n";
|
|
|
request += p_body;
|
|
@@ -253,7 +260,7 @@ Error HTTPClient::poll() {
|
|
|
IP::ResolverStatus rstatus = IP::get_singleton()->get_resolve_item_status(resolving);
|
|
|
switch (rstatus) {
|
|
|
case IP::RESOLVER_STATUS_WAITING:
|
|
|
- return OK; //still resolving
|
|
|
+ return OK; // Still resolving
|
|
|
|
|
|
case IP::RESOLVER_STATUS_DONE: {
|
|
|
|
|
@@ -285,7 +292,7 @@ Error HTTPClient::poll() {
|
|
|
switch (s) {
|
|
|
|
|
|
case StreamPeerTCP::STATUS_CONNECTING: {
|
|
|
- return OK; //do none
|
|
|
+ return OK;
|
|
|
} break;
|
|
|
case StreamPeerTCP::STATUS_CONNECTED: {
|
|
|
if (ssl) {
|
|
@@ -296,7 +303,6 @@ Error HTTPClient::poll() {
|
|
|
status = STATUS_SSL_HANDSHAKE_ERROR;
|
|
|
return ERR_CANT_CONNECT;
|
|
|
}
|
|
|
- //print_line("SSL! TURNED ON!");
|
|
|
connection = ssl;
|
|
|
}
|
|
|
status = STATUS_CONNECTED;
|
|
@@ -312,7 +318,7 @@ Error HTTPClient::poll() {
|
|
|
}
|
|
|
} break;
|
|
|
case STATUS_CONNECTED: {
|
|
|
- //request something please
|
|
|
+ // Connection established, requests can now be made
|
|
|
return OK;
|
|
|
} break;
|
|
|
case STATUS_REQUESTING: {
|
|
@@ -328,7 +334,7 @@ Error HTTPClient::poll() {
|
|
|
}
|
|
|
|
|
|
if (rec == 0)
|
|
|
- return OK; //keep trying!
|
|
|
+ return OK; // Still requesting, keep trying!
|
|
|
|
|
|
response_str.push_back(byte);
|
|
|
int rs = response_str.size();
|
|
@@ -336,11 +342,10 @@ Error HTTPClient::poll() {
|
|
|
(rs >= 2 && response_str[rs - 2] == '\n' && response_str[rs - 1] == '\n') ||
|
|
|
(rs >= 4 && response_str[rs - 4] == '\r' && response_str[rs - 3] == '\n' && response_str[rs - 2] == '\r' && response_str[rs - 1] == '\n')) {
|
|
|
|
|
|
- //end of response, parse.
|
|
|
+ // End of response, parse.
|
|
|
response_str.push_back(0);
|
|
|
String response;
|
|
|
response.parse_utf8((const char *)response_str.ptr());
|
|
|
- //print_line("END OF RESPONSE? :\n"+response+"\n------");
|
|
|
Vector<String> responses = response.split("\n");
|
|
|
body_size = 0;
|
|
|
chunked = false;
|
|
@@ -363,7 +368,6 @@ Error HTTPClient::poll() {
|
|
|
|
|
|
if (s.begins_with("transfer-encoding:")) {
|
|
|
String encoding = header.substr(header.find(":") + 1, header.length()).strip_edges();
|
|
|
- //print_line("TRANSFER ENCODING: "+encoding);
|
|
|
if (encoding == "chunked") {
|
|
|
chunked = true;
|
|
|
}
|
|
@@ -381,14 +385,14 @@ Error HTTPClient::poll() {
|
|
|
|
|
|
if (body_size == 0 && !chunked) {
|
|
|
|
|
|
- status = STATUS_CONNECTED; //ask for something again?
|
|
|
+ status = STATUS_CONNECTED; // Ready for new requests
|
|
|
} else {
|
|
|
status = STATUS_BODY;
|
|
|
}
|
|
|
return OK;
|
|
|
}
|
|
|
}
|
|
|
- //wait for response
|
|
|
+ // Wait for response
|
|
|
return OK;
|
|
|
} break;
|
|
|
case STATUS_DISCONNECTED: {
|
|
@@ -424,7 +428,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
|
|
|
while (true) {
|
|
|
|
|
|
if (chunk_left == 0) {
|
|
|
- //reading len
|
|
|
+ // Reading length
|
|
|
uint8_t b;
|
|
|
int rec = 0;
|
|
|
err = _get_http_data(&b, 1, rec);
|
|
@@ -467,7 +471,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
|
|
|
}
|
|
|
|
|
|
if (len == 0) {
|
|
|
- //end!
|
|
|
+ // End reached!
|
|
|
status = STATUS_CONNECTED;
|
|
|
chunk.clear();
|
|
|
return PoolByteArray();
|
|
@@ -525,7 +529,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
|
|
|
to_read -= rec;
|
|
|
_offset += rec;
|
|
|
} else {
|
|
|
- if (to_read > 0) //ended up reading less
|
|
|
+ if (to_read > 0) // Ended up reading less
|
|
|
ret.resize(_offset);
|
|
|
break;
|
|
|
}
|
|
@@ -540,7 +544,7 @@ PoolByteArray HTTPClient::read_response_body_chunk() {
|
|
|
close();
|
|
|
if (err == ERR_FILE_EOF) {
|
|
|
|
|
|
- status = STATUS_DISCONNECTED; //server disconnected
|
|
|
+ status = STATUS_DISCONNECTED; // Server disconnected
|
|
|
} else {
|
|
|
|
|
|
status = STATUS_CONNECTION_ERROR;
|
|
@@ -593,7 +597,7 @@ HTTPClient::HTTPClient() {
|
|
|
tcp_connection = StreamPeerTCP::create_ref();
|
|
|
resolving = IP::RESOLVER_INVALID_ID;
|
|
|
status = STATUS_DISCONNECTED;
|
|
|
- conn_port = 80;
|
|
|
+ conn_port = -1;
|
|
|
body_size = 0;
|
|
|
chunked = false;
|
|
|
body_left = 0;
|
|
@@ -653,7 +657,7 @@ PoolStringArray HTTPClient::_get_response_headers() {
|
|
|
|
|
|
void HTTPClient::_bind_methods() {
|
|
|
|
|
|
- ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_ssl", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(false), DEFVAL(true));
|
|
|
+ ClassDB::bind_method(D_METHOD("connect_to_host", "host", "port", "use_ssl", "verify_host"), &HTTPClient::connect_to_host, DEFVAL(-1), DEFVAL(false), DEFVAL(true));
|
|
|
ClassDB::bind_method(D_METHOD("set_connection", "connection"), &HTTPClient::set_connection);
|
|
|
ClassDB::bind_method(D_METHOD("get_connection"), &HTTPClient::get_connection);
|
|
|
ClassDB::bind_method(D_METHOD("request_raw", "method", "url", "headers", "body"), &HTTPClient::request_raw);
|
|
@@ -689,13 +693,13 @@ void HTTPClient::_bind_methods() {
|
|
|
BIND_ENUM_CONSTANT(METHOD_MAX);
|
|
|
|
|
|
BIND_ENUM_CONSTANT(STATUS_DISCONNECTED);
|
|
|
- BIND_ENUM_CONSTANT(STATUS_RESOLVING); //resolving hostname (if passed a hostname)
|
|
|
+ BIND_ENUM_CONSTANT(STATUS_RESOLVING); // Resolving hostname (if hostname was passed in)
|
|
|
BIND_ENUM_CONSTANT(STATUS_CANT_RESOLVE);
|
|
|
- BIND_ENUM_CONSTANT(STATUS_CONNECTING); //connecting to ip
|
|
|
+ BIND_ENUM_CONSTANT(STATUS_CONNECTING); // Connecting to IP
|
|
|
BIND_ENUM_CONSTANT(STATUS_CANT_CONNECT);
|
|
|
- BIND_ENUM_CONSTANT(STATUS_CONNECTED); //connected ); requests only accepted here
|
|
|
- BIND_ENUM_CONSTANT(STATUS_REQUESTING); // request in progress
|
|
|
- BIND_ENUM_CONSTANT(STATUS_BODY); // request resulted in body ); which must be read
|
|
|
+ BIND_ENUM_CONSTANT(STATUS_CONNECTED); // Connected, now accepting requests
|
|
|
+ BIND_ENUM_CONSTANT(STATUS_REQUESTING); // Request in progress
|
|
|
+ BIND_ENUM_CONSTANT(STATUS_BODY); // Request resulted in body which must be read
|
|
|
BIND_ENUM_CONSTANT(STATUS_CONNECTION_ERROR);
|
|
|
BIND_ENUM_CONSTANT(STATUS_SSL_HANDSHAKE_ERROR);
|
|
|
|