diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 24012660d2f..58092efd4b8 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -579,7 +579,7 @@ Error HTTPClient::_get_http_data(uint8_t* p_buffer, int p_bytes,int &r_received)
void HTTPClient::_bind_methods() {
- ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true));
+ ObjectTypeDB::bind_method(_MD("connect:Error","host","port","use_ssl","verify_host"),&HTTPClient::connect,DEFVAL(false),DEFVAL(true));
ObjectTypeDB::bind_method(_MD("set_connection","connection:StreamPeer"),&HTTPClient::set_connection);
ObjectTypeDB::bind_method(_MD("request","method","url","headers","body"),&HTTPClient::request,DEFVAL(String()));
ObjectTypeDB::bind_method(_MD("send_body_text","body"),&HTTPClient::send_body_text);
@@ -601,6 +601,8 @@ void HTTPClient::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_status"),&HTTPClient::get_status);
ObjectTypeDB::bind_method(_MD("poll:Error"),&HTTPClient::poll);
+ ObjectTypeDB::bind_method(_MD("query_string_from_dict:String","fields"),&HTTPClient::query_string_from_dict);
+
BIND_CONSTANT( METHOD_GET );
BIND_CONSTANT( METHOD_HEAD );
@@ -689,6 +691,16 @@ void HTTPClient::set_read_chunk_size(int p_size) {
read_chunk_size=p_size;
}
+String HTTPClient::query_string_from_dict(const Dictionary& p_dict) {
+ String query = "";
+ Array keys = p_dict.keys();
+ for (int i = 0; i < keys.size(); ++i) {
+ query += "&" + String(keys[i]).http_escape() + "=" + String(p_dict[keys[i]]).http_escape();
+ }
+ query.erase(0, 1);
+ return query;
+}
+
HTTPClient::HTTPClient(){
tcp_connection = StreamPeerTCP::create_ref();
@@ -710,4 +722,3 @@ HTTPClient::~HTTPClient(){
}
-
diff --git a/core/io/http_client.h b/core/io/http_client.h
index 21281f38c5c..b103dc43fc2 100644
--- a/core/io/http_client.h
+++ b/core/io/http_client.h
@@ -192,6 +192,8 @@ public:
Error poll();
+ String query_string_from_dict(const Dictionary& p_dict);
+
HTTPClient();
~HTTPClient();
};
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 7582376fe0b..2dffdf066cd 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -3079,6 +3079,48 @@ String String::world_wrap(int p_chars_per_line) const {
return ret;
}
+String String::http_escape() const {
+ const CharString temp = utf8();
+ String res;
+ for (int i = 0; i < length(); ++i) {
+ CharType ord = temp[i];
+ if (ord == '.' || ord == '-' || ord == '_' || ord == '~' ||
+ (ord >= 'a' && ord <= 'z') ||
+ (ord >= 'A' && ord <= 'Z') ||
+ (ord >= '0' && ord <= '9')) {
+ res += ord;
+ } else {
+ char h_Val[3];
+ snprintf(h_Val, 3, "%.2X", ord);
+ res += "%";
+ res += h_Val;
+ }
+ }
+ return res;
+}
+
+String String::http_unescape() const {
+ String res;
+ for (int i = 0; i < length(); ++i) {
+ if (ord_at(i) == '%' && i+2 < length()) {
+ CharType ord1 = ord_at(i+1);
+ if ((ord1 >= '0' && ord1 <= '9') || (ord1 >= 'A' && ord1 <= 'Z')) {
+ CharType ord2 = ord_at(i+2);
+ if ((ord2 >= '0' && ord2 <= '9') || (ord2 >= 'A' && ord2 <= 'Z')) {
+ char bytes[2] = {ord1, ord2};
+ res += (char)strtol(bytes, NULL, 16);
+ i+=2;
+ }
+ } else {
+ res += ord_at(i);
+ }
+ } else {
+ res += ord_at(i);
+ }
+ }
+ return String::utf8(res.ascii());
+}
+
String String::c_unescape() const {
String escaped=*this;
diff --git a/core/ustring.h b/core/ustring.h
index fa25a07eb07..2f3c4bff4d7 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -207,6 +207,8 @@ public:
String xml_escape(bool p_escape_quotes=false) const;
String xml_unescape() const;
+ String http_escape() const;
+ String http_unescape() const;
String c_escape() const;
String c_unescape() const;
String world_wrap(int p_chars_per_line) const;
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index 24c5799350f..e805adce6dc 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -12518,9 +12518,13 @@ This approximation makes straight segments between each point, then subdivides t
-
+
+ Connect to a host. This needs to be done before any requests are sent.
+The host should not have http:// prepended but will strip the protocol identifier if provided.
+
+verify_host will check the SSL identity of the host if set to true.
@@ -12541,6 +12545,20 @@ This approximation makes straight segments between each point, then subdivides t
+ Sends a request to the connected host. The url is the what is normally behind the hostname,
+i.e;
+http://somehost.com/index.php
+url would be "index.php"
+
+Headers are HTTP request headers
+
+To create a POST request with query strings to push to the server, do:
+var fields = {"username" : "user",
+ "password" : "pass"}
+var queryString = httpClient.query_string_from_dict(fields)
+var headers = ["Content-Type: application/x-www-form-urlencoded",
+ "Content-Length: " + str(queryString.length())]
+var result = httpClient.request(httpClient.METHOD_POST, "index.php", headers, queryString)
@@ -12548,7 +12566,8 @@ This approximation makes straight segments between each point, then subdivides t
-
+
+ Stub function
@@ -12557,6 +12576,7 @@ This approximation makes straight segments between each point, then subdivides t
+ Stub function
@@ -12609,12 +12629,14 @@ This approximation makes straight segments between each point, then subdivides t
+ Sets the size of the buffer used and maximum bytes to read per iteration
+ If set to true, execute will wait until all data is read from the response.
@@ -12627,14 +12649,30 @@ This approximation makes straight segments between each point, then subdivides t
+ Returns a status string like STATUS_REQUESTING. Need to call [method poll] in order to get status updates.
-
+
+ This needs to be called in order to have any request processed. Check results with [method get_status]
+
+
+
+
+
+
+ Generates a GET/POST application/x-www-form-urlencoded style query string from a provided dictionary.
+
+var fields = {"username": "user", "password": "pass"}
+String queryString = httpClient.query_string_from_dict(fields)
+
+returns:= "username=user&password=pass"
+
+
diff --git a/tools/doc/doc_data.cpp b/tools/doc/doc_data.cpp
index 432f3586275..c1d3e5e3148 100644
--- a/tools/doc/doc_data.cpp
+++ b/tools/doc/doc_data.cpp
@@ -189,9 +189,11 @@ void DocData::generate(bool p_basic_types) {
arginfo=E->get().return_val;
if (arginfo.type==Variant::NIL)
continue;
+#ifdef DEBUG_METHODS_ENABLED
if (m && m->get_return_type()!=StringName())
method.return_type=m->get_return_type();
else
+#endif
method.return_type=(arginfo.hint==PROPERTY_HINT_RESOURCE_TYPE)?arginfo.hint_string:Variant::get_type_name(arginfo.type);
} else {
diff --git a/tools/docdump/makehtml.py b/tools/docdump/makehtml.py
index d533ca1b8b7..9b9c62f33b1 100644
--- a/tools/docdump/makehtml.py
+++ b/tools/docdump/makehtml.py
@@ -1,5 +1,19 @@
import sys
import xml.etree.ElementTree as ET
+from xml.sax.saxutils import escape, unescape
+
+html_escape_table = {
+ '"': """,
+ "'": "'"
+}
+
+html_unescape_table = {v:k for k, v in html_escape_table.items()}
+
+def html_escape(text):
+ return escape(text, html_escape_table)
+
+def html_unescape(text):
+ return unescape(text, html_unescape_table)
input_list = []
@@ -96,7 +110,7 @@ def make_html_class_list(class_list,columns):
idx=0
for n in class_list:
- col = idx/col_max
+ col = int(idx/col_max)
if (col>=columns):
col=columns-1
fit_columns[col]+=[n]
@@ -299,6 +313,7 @@ def make_type(p_type,p_parent):
def make_text_def(class_name,parent,text):
+ text = html_escape(text)
pos=0
while(True):
pos = text.find("[",pos)
@@ -598,7 +613,6 @@ def make_html_class(node):
descr=node.find("description")
if (descr!=None and descr.text.strip()!=""):
-
h4=ET.SubElement(div,"h4")
h4.text="Description:"
@@ -644,7 +658,6 @@ def make_html_class(node):
class_names=[]
classes={}
-
for file in input_list:
tree = ET.parse(file)
doc=tree.getroot()