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

Files at this revision

API Documentation at this revision

Comitter:
embeddedartists
Date:
Wed Oct 01 11:36:11 2014 +0000
Parent:
6:09d0a383fdde
Commit message:
Modified PubNub library to better utilize low bandwidth connections like a GSM modem. Instead of 10 sockets connections sending 3 bytes only one is made and it sends all 30 bytes at the same time.

Changed in this revision

PubNub.cpp Show annotated file Show diff for this revision Revisions of this file
PubNub.h Show annotated file Show diff for this revision Revisions of this file
--- 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
+}
--- a/PubNub.h	Tue May 20 11:43:18 2014 +0000
+++ b/PubNub.h	Wed Oct 01 11:36:11 2014 +0000
@@ -110,6 +110,10 @@
      * @param reply optional pointer for passing the returned reply (free() after use).
      * @return PNR_OK on success. */
     PubNubRes publish(const char *channel, const char *message, char **reply = NULL);
+    
+    /* Same as publish() but the user guarantees that the message is URL encoded already.
+     * This saves time compared to having to parse and convert the string every call. */
+    PubNubRes publish_urlenc(const char *channel, const char *message, char **reply = NULL);
 
     /** Subscribe API call
      *
@@ -197,10 +201,10 @@
     char m_timetoken[32];
     char *m_replybuf;
     int m_replylen;
-
+        
     /* Back-end post-processing of subscribe(). free()s *reply
      * in the process. */
     PubNubRes subscribe_processjson(char *reply, int replylen);
 };
 
-#endif
\ No newline at end of file
+#endif