Browse Source

Fixes the make_doc.sh, <, > and & signs in descriptions that cause the parser to break.
Documentation for HTTPClient.
Added a query_string_from_dict method to HTTPClient to create a x-www-form-urlencoded valid query string for GET and POST messages.
String now has http_escape() and http_unescape() methods to help facilitate the above query_string_from_dict method.

Aren Villanueva 9 years ago
parent
commit
5c7e9e7e63
7 changed files with 120 additions and 10 deletions
  1. 13 2
      core/io/http_client.cpp
  2. 2 0
      core/io/http_client.h
  3. 42 0
      core/ustring.cpp
  4. 2 0
      core/ustring.h
  5. 43 5
      doc/base/classes.xml
  6. 2 0
      tools/doc/doc_data.cpp
  7. 16 3
      tools/docdump/makehtml.py

+ 13 - 2
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(){
 
 }
 
-

+ 2 - 0
core/io/http_client.h

@@ -192,6 +192,8 @@ public:
 
 	Error poll();
 
+    String query_string_from_dict(const Dictionary& p_dict);
+
 	HTTPClient();
 	~HTTPClient();
 };

+ 42 - 0
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;

+ 2 - 0
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;

+ 43 - 5
doc/base/classes.xml

@@ -12518,9 +12518,13 @@ This approximation makes straight segments between each point, then subdivides t
 			</argument>
 			<argument index="2" name="use_ssl" type="bool" default="false">
 			</argument>
-			<argument index="3" name="arg3" type="bool" default="true">
+			<argument index="3" name="verify_host" type="bool" default="true">
 			</argument>
 			<description>
+					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.
 			</description>
 		</method>
 		<method name="set_connection">
@@ -12541,6 +12545,20 @@ This approximation makes straight segments between each point, then subdivides t
 			<argument index="3" name="body" type="String" default="&quot;&quot;">
 			</argument>
 			<description>
+					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 &quot;index.php&quot;
+					
+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)
 			</description>
 		</method>
 		<method name="send_body_text">
@@ -12548,7 +12566,8 @@ This approximation makes straight segments between each point, then subdivides t
 			</return>
 			<argument index="0" name="body" type="String">
 			</argument>
-			<description>
+				<description>
+					Stub function
 			</description>
 		</method>
 		<method name="send_body_data">
@@ -12557,6 +12576,7 @@ This approximation makes straight segments between each point, then subdivides t
 			<argument index="0" name="body" type="RawArray">
 			</argument>
 			<description>
+					Stub function
 			</description>
 		</method>
 		<method name="close">
@@ -12609,12 +12629,14 @@ This approximation makes straight segments between each point, then subdivides t
 			<argument index="0" name="bytes" type="int">
 			</argument>
 			<description>
+					Sets the size of the buffer used and maximum bytes to read per iteration
 			</description>
 		</method>
 		<method name="set_blocking_mode">
 			<argument index="0" name="enabled" type="bool">
 			</argument>
 			<description>
+					If set to true, execute will wait until all data is read from the response.
 			</description>
 		</method>
 		<method name="is_blocking_mode_enabled" qualifiers="const">
@@ -12627,14 +12649,30 @@ This approximation makes straight segments between each point, then subdivides t
 			<return type="int">
 			</return>
 			<description>
+					Returns a status string like STATUS_REQUESTING. Need to call [method poll] in order to get status updates.
 			</description>
 		</method>
 		<method name="poll">
 			<return type="Error">
 			</return>
-			<description>
-			</description>
-		</method>
+				<description>
+					This needs to be called in order to have any request processed. Check results with [method get_status]
+			</description>
+		</method>
+        <method name="query_string_from_dict">
+            <return type="String">
+            </return>
+            <argument index="0" name="fields" type="Dictionary">
+            </argument>
+            <description>
+					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&amp;password=pass"
+            </description>
+        </method>
 	</methods>
 	<constants>
 		<constant name="METHOD_GET" value="0">

+ 2 - 0
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 {

+ 16 - 3
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 = {
+ '"': "&quot;",
+ "'": "&apos;"
+}
+
+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()