Modified PubNub library to better utilize low bandwidth connections like a GSM modem. Instead of 10 socket connections sending 3 bytes only one is made and it sends all 30 bytes at the same time.

Fork of PubNub by PubNub

Revision:
7:55eb53c78b47
Parent:
6:09d0a383fdde
--- a/PubNub.cpp	Tue May 20 11:43:18 2014 +0000
+++ b/PubNub.cpp	Wed Oct 01 11:36:11 2014 +0000
@@ -8,6 +8,7 @@
 
 #define DBG if (0) // change to if (1) to enable debug prints on USB serial
 
+//#define USE_ORIGINAL_CODE
 
 /* We roll our own HTTP communication stack as donatien's HTTPClient has
  * some hard limits on URL length and this will allow a more frugal RAM
@@ -21,6 +22,24 @@
  * we still show picojson usage for end-user parsing of complex incoming
  * messages. */
 
+#include "rtos.h"
+
+void pnub_err_handler(PubNubRes code, int line);
+extern Serial pc;
+extern Mutex stdio_mutex; 
+#define safe_printf(...) do { \
+        stdio_mutex.lock(); \
+        pc.printf(__VA_ARGS__); \
+        stdio_mutex.unlock(); \
+    } while(0)
+
+#define PNUB_RETURN_STAT(__stat) do { \
+        if ((__stat) != PNR_OK) { \
+            pnub_err_handler((__stat), __LINE__); \
+        } \
+        return (__stat); \
+    } while(0)
+
 
 /** Custom no-frills HTTP stream. */
 
@@ -34,6 +53,8 @@
     void sendstr(const char *string);
     /* Send a given NUL-terminated string over the http socket, URL encoded. */
     void sendstr_urlenc(const char *string);
+    /* URL encode a given NUL-terminated string into the given buffer. */
+    void prepare_urlenc(const char *string, char* buff);
 
     /* A common HTTP request "bottom half" - send the rest of headers
      * and wait for reply + receive. */
@@ -57,7 +78,9 @@
     int ret = connect(hostname, 80);
     if (ret < 0) {
         close();
-        return PNR_IO_ERROR;
+        //return PNR_IO_ERROR;
+        safe_printf("http_connect(%s): ret = %d\n", hostname, ret);
+        PNUB_RETURN_STAT(PNR_IO_ERROR);
     }
     return PNR_OK;
 }
@@ -90,6 +113,32 @@
     }
 }
 
+void
+PubNubHTTP::prepare_urlenc(const char *s, char* buff)
+{
+    char* b = buff;
+    while (s[0]) {
+        /* RFC 3986 Unreserved characters plus few
+         * safe reserved ones. */
+        size_t okspan = strspn(s, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" ",=:;@[]");
+        if (okspan > 0) {
+            strncpy(b, s, okspan);
+            b += okspan;
+            //send_all((char *) s, okspan);
+            s += okspan;
+        }
+        if (s[0]) {
+            /* %-encode a non-ok character. */
+            *b++ = '%';
+            *b++ = "0123456789ABCDEF"[s[0] / 16];
+            *b++ = "0123456789ABCDEF"[s[0] % 16];
+            //send_all((char *) enc, 3);
+            s++;
+        }
+    }
+    *b = '\0';
+}
+
 PubNubRes
 PubNubHTTP::http_request_bh(const char *host, char **reply, int *replylen)
 {
@@ -100,21 +149,24 @@
     res = http_send_headers(host);
     if (res != PNR_OK) {
         close();
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
     }
 
     res = http_recv_headers();
     if (res != PNR_OK) {
         if (m_replybuf) free(m_replybuf);
         close();
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
     }
 
     res = http_recv_content();
     if (res != PNR_OK) {
         if (m_replybuf) free(m_replybuf);
         close();
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
     }
 
     close();
@@ -126,6 +178,7 @@
 PubNubRes
 PubNubHTTP::http_send_headers(const char *host)
 {
+#ifdef USE_ORIGINAL_CODE
     /* Finish the first line of the request. */
     sendstr(" HTTP/1.1\r\n");
     /* Finish HTTP request. */
@@ -133,8 +186,18 @@
     sendstr(host);
     sendstr("\r\nUser-Agent: PubNub-mbed/0.1\r\nConnection: close\r\n\r\n");
     if (!is_connected())
-        return PNR_IO_ERROR;
+        //return PNR_IO_ERROR;
+        PNUB_RETURN_STAT(PNR_IO_ERROR);
     return PNR_OK;
+#else
+    char buff[200];
+    snprintf(buff, 200, " HTTP/1.1\r\nHost: %s\r\nUser-Agent: PubNub-mbed/0.1\r\nConnection: close\r\n\r\n", host);
+    sendstr(buff);
+    if (!is_connected())
+        //return PNR_IO_ERROR;
+        PNUB_RETURN_STAT(PNR_IO_ERROR);
+    return PNR_OK;
+#endif
 }
 
 PubNubRes
@@ -147,20 +210,24 @@
     /* XXX: For now, we assume complete headers will fit in m_replybuf. */
     m_replylen = receive_all(m_replybuf, PUBNUB_REPLY_MAXLEN);
     if (m_replylen < 0)
-        return PNR_IO_ERROR;
+        //return PNR_IO_ERROR;
+        PNUB_RETURN_STAT(PNR_IO_ERROR);
     m_replybuf[m_replylen] = 0;
 
     char *bufptr = m_replybuf;
 
     /* Parse the first line. */
     if (strncmp(bufptr, "HTTP/1.", 7) || !bufptr[7] || !bufptr[8])
-        return PNR_IO_ERROR;
+        //return PNR_IO_ERROR;
+        PNUB_RETURN_STAT(PNR_IO_ERROR);
     int http_code = atoi(bufptr+9);
     if (http_code / 100 != 2)
-        return PNR_HTTP_ERROR;
+        //return PNR_HTTP_ERROR;
+        PNUB_RETURN_STAT(PNR_HTTP_ERROR);
     bufptr = strstr(bufptr, "\r\n");
     if (!bufptr)
-        return PNR_IO_ERROR;
+        //return PNR_IO_ERROR;
+        PNUB_RETURN_STAT(PNR_IO_ERROR);
     bufptr += 2;
 
     /* Parse the rest of the lines. */
@@ -170,7 +237,8 @@
     for (;; bufptr = newline+2) {
         newline = strstr(bufptr, "\r\n");
         if (!newline)
-            return PNR_IO_ERROR;
+            //return PNR_IO_ERROR;
+            PNUB_RETURN_STAT(PNR_IO_ERROR);
         *newline = 0;
 
         char h_chunked[] = "Transfer-Encoding: chunked";
@@ -193,7 +261,8 @@
         m_content_length = atoi(bufptr);
         bufptr = strstr(bufptr, "\r\n");
         if (!bufptr)
-            return PNR_IO_ERROR;
+            //return PNR_IO_ERROR;
+            PNUB_RETURN_STAT(PNR_IO_ERROR);
         bufptr += 2;
     }
 
@@ -212,19 +281,22 @@
     if (m_content_length > PUBNUB_REPLY_MAXLEN) {
         /* XXX: Actually, too much data, sorry. */
         DBG printf("too much data\r\n");
-        return PNR_FORMAT_ERROR;
+        //return PNR_FORMAT_ERROR;
+        PNUB_RETURN_STAT(PNR_FORMAT_ERROR);
     }
 
     if (m_replylen < m_content_length) {
         /* More data coming. */
         int recv2len = receive_all(m_replybuf + m_replylen, PUBNUB_REPLY_MAXLEN - m_replylen);
         if (recv2len < 0)
-            return PNR_IO_ERROR;
+            //return PNR_IO_ERROR;
+            PNUB_RETURN_STAT(PNR_IO_ERROR);
         m_replylen += recv2len;
     }
 
     if (m_replylen < m_content_length)
-        return PNR_IO_ERROR; /* Incomplete data. */
+        //return PNR_IO_ERROR; /* Incomplete data. */
+        PNUB_RETURN_STAT(PNR_IO_ERROR);
     return PNR_OK;
 }
 
@@ -260,7 +332,8 @@
     }
     DBG printf("parse %d %d %d\r\n", escaped, in_string, bracket_level);
     if (escaped || in_string || bracket_level > 0)
-        return PNR_FORMAT_ERROR;
+        //return PNR_FORMAT_ERROR;
+        PNUB_RETURN_STAT(PNR_FORMAT_ERROR);
     return PNR_OK;
 }
 
@@ -294,8 +367,10 @@
     PubNubHTTP http;
     PubNubRes res = http.http_connect(origin_hostname());
     if (res != PNR_OK)
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
 
+#ifdef USE_ORIGINAL_CODE
     http.sendstr("GET /publish/");
     http.sendstr(m_publish_key);
     http.sendstr("/");
@@ -304,6 +379,13 @@
     http.sendstr(channel);
     http.sendstr("/0/");
     http.sendstr_urlenc(message);
+#else
+    char* buff = (char*)malloc(2000);
+    int len = sprintf(buff, "GET /publish/%s/%s/0/%s/0/",m_publish_key,m_subscribe_key,channel);
+    http.prepare_urlenc(message, buff+len);
+    http.sendstr(buff);
+    free(buff);
+#endif
 
     char *locreply;
     if (!reply)
@@ -312,13 +394,56 @@
 
     res = http.http_request_bh(origin_hostname(), reply, &replylen);
     if (res != PNR_OK)
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
     bool success = (*reply)[1] == '1' && (*reply)[2] == ',';
     if (reply == &locreply)
         free(locreply);
-    return success ? PNR_OK : PNR_PUBNUB_ERROR;
+    //return success ? PNR_OK : PNR_PUBNUB_ERROR;
+    PNUB_RETURN_STAT(success ? PNR_OK : PNR_PUBNUB_ERROR);
 }
 
+PubNubRes
+PubNub::publish_urlenc(const char *channel, const char *message, char **reply)
+{
+    PubNubHTTP http;
+    PubNubRes res = http.http_connect(origin_hostname());
+    if (res != PNR_OK)
+        //return res;
+        PNUB_RETURN_STAT(res);
+
+#ifdef USE_ORIGINAL_CODE
+    http.sendstr("GET /publish/");
+    http.sendstr(m_publish_key);
+    http.sendstr("/");
+    http.sendstr(m_subscribe_key);
+    http.sendstr("/0/");
+    http.sendstr(channel);
+    http.sendstr("/0/");
+    http.sendstr_urlenc(message);
+#else
+    char* buff = (char*)malloc(2000);
+    /*int len = */sprintf(buff, "GET /publish/%s/%s/0/%s/0/%s",m_publish_key,m_subscribe_key,channel,message);
+    //http.prepare_urlenc(message, buff+len);
+    http.sendstr(buff);
+    free(buff);
+#endif
+
+    char *locreply;
+    if (!reply)
+        reply = &locreply;
+    int replylen;
+
+    res = http.http_request_bh(origin_hostname(), reply, &replylen);
+    if (res != PNR_OK)
+        //return res;
+        PNUB_RETURN_STAT(res);
+    bool success = (*reply)[1] == '1' && (*reply)[2] == ',';
+    if (reply == &locreply)
+        free(locreply);
+    //return success ? PNR_OK : PNR_PUBNUB_ERROR;
+    PNUB_RETURN_STAT(success ? PNR_OK : PNR_PUBNUB_ERROR);
+}
 
 PubNubRes
 PubNub::subscribe(const char *channel, char **reply)
@@ -350,14 +475,21 @@
     PubNubRes res;
     res = http.http_connect(origin_hostname());
     if (res != PNR_OK)
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
 
+#ifdef USE_ORIGINAL_CODE
     http.sendstr("GET /subscribe/");
     http.sendstr(m_subscribe_key);
     http.sendstr("/");
     http.sendstr(channel);
     http.sendstr("/0/");
     http.sendstr(m_timetoken);
+#else
+    char buff[200];
+    snprintf(buff, 200, "GET /subscribe/%s/%s/0/%s", m_subscribe_key, channel, m_timetoken);
+    http.sendstr(buff);
+#endif
 
     char *replybuf = NULL;
     int replylen;
@@ -394,7 +526,8 @@
         free(reply);
     m_replybuf = NULL;
     m_replylen = 0;
-    return res;
+    //return res;
+    PNUB_RETURN_STAT(res);
 }
 
 PubNubRes
@@ -403,7 +536,8 @@
     if (reply[0] != '[' || reply[replylen-1] != ']'
         || reply[replylen-2] != '"') {
         DBG printf("bad reply '%s'\r\n", reply);
-        return PNR_FORMAT_ERROR;
+        //return PNR_FORMAT_ERROR;
+        PNUB_RETURN_STAT(PNR_FORMAT_ERROR);
     }
 
     /* Extract timetoken. */
@@ -414,7 +548,8 @@
             break;
     if (!i || reply[i-1] != ',' || replylen-2 - (i+1) >= 64) {
         DBG printf("bad reply '%s'\r\n", reply);
-        return PNR_FORMAT_ERROR;
+        //return PNR_FORMAT_ERROR;
+        PNUB_RETURN_STAT(PNR_FORMAT_ERROR);
     }
     strcpy(m_timetoken, &reply[i+1]);
     reply[i-1] = 0; // terminate the [] message array
@@ -430,7 +565,8 @@
     /* Extract the messages array. */
     if (reply[1] != '[' || reply[i-2] != ']') {
         DBG printf("bad reply end '%s'\r\n", reply);
-        return PNR_FORMAT_ERROR;
+        //return PNR_FORMAT_ERROR;
+        PNUB_RETURN_STAT(PNR_FORMAT_ERROR);
     }
     reply[i-2] = 0;
 
@@ -450,8 +586,10 @@
     PubNubRes res;
     res = http.http_connect(origin_hostname());
     if (res != PNR_OK)
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
 
+#ifdef USE_ORIGINAL_CODE
     http.sendstr("GET /history/");
     http.sendstr(m_subscribe_key);
     http.sendstr("/");
@@ -459,6 +597,11 @@
     http.sendstr("/0/");
     char limitbuf[8]; snprintf(limitbuf, sizeof(limitbuf), "%d", limit);
     http.sendstr(limitbuf);
+#else
+    char buff[200];
+    snprintf(buff, 200, "GET /history/%s/%s/0/%d", m_subscribe_key, channel, limit);
+    http.sendstr(buff);
+#endif
 
     char *replybuf = NULL;
     int replylen;
@@ -494,7 +637,8 @@
     
 error:
     free(replybuf);
-    return res;
+    //return res;
+    PNUB_RETURN_STAT(res);
 }
 
 
@@ -504,7 +648,8 @@
     PubNubHTTP http;
     PubNubRes res = http.http_connect(origin_hostname());
     if (res != PNR_OK)
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
 
     http.sendstr("GET /time/0");
 
@@ -512,11 +657,13 @@
     int replylen;
     res = http.http_request_bh(origin_hostname(), &reply, &replylen);
     if (res != PNR_OK)
-        return res;
+        //return res;
+        PNUB_RETURN_STAT(res);
 
     if (replylen < 3 || replylen > 32 || reply[0] != '[' || reply[replylen-1] != ']') {
         free(reply);
-        return PNR_FORMAT_ERROR;
+        //return PNR_FORMAT_ERROR;
+        PNUB_RETURN_STAT(PNR_FORMAT_ERROR);
     }
 
     replylen -= 2;
@@ -525,4 +672,4 @@
 
     free(reply);
     return PNR_OK;
-}
\ No newline at end of file
+}