Example of HTTPServer with additional features: * SNTPClient, DST rules * Link status indication * Local or SDCard-based WebServer * RPC-able class * Static and Dynamic HTML page

Dependencies:   mbed

Files at this revision

API Documentation at this revision

Comitter:
iva2k
Date:
Tue Jan 12 07:41:55 2010 +0000
Parent:
1:ec4170739967
Commit message:

Changed in this revision

lwip/Core/lwipopts.h Show annotated file Show diff for this revision Revisions of this file
lwip/HTTPServer/HTTPRPC.h Show annotated file Show diff for this revision Revisions of this file
lwip/SNTPClient/DstZones.h Show annotated file Show diff for this revision Revisions of this file
lwip/SNTPClient/SNTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
lwip/SNTPClient/SNTPClient.h Show annotated file Show diff for this revision Revisions of this file
myrpc.cpp Show annotated file Show diff for this revision Revisions of this file
myrpc.h Show annotated file Show diff for this revision Revisions of this file
--- a/lwip/Core/lwipopts.h	Fri Jan 08 00:28:14 2010 +0000
+++ b/lwip/Core/lwipopts.h	Tue Jan 12 07:41:55 2010 +0000
@@ -86,8 +86,8 @@
 #endif
 
 #include <time.h>
-extern unsigned int gSntpRecvTimeout_ms;
-extern unsigned int gSntpUpdateDelay_ms;  // Never set this below 15000
+extern unsigned int gSntpRecvTimeout_s;
+extern unsigned int gSntpUpdateDelay_s;   // Never set this below 15
 void SntpClientSet(time_t sec);           // Receives NTP timestamp
 
 #ifdef __cplusplus
@@ -96,8 +96,7 @@
 
 // Do not change these:
 #define SNTP_SOCKET                 0
-#define SNTP_DEBUG                  LWIP_DBG_ON
-#define PBUF_DEBUG                  LWIP_DBG_ON
+#define SNTP_DEBUG                  LWIP_DBG_OFF
 #define SNTP_PORT                   123
 #define SNTP_SERVER_DNS             1   // Override. Use URLs. Do not use IP addresses!
 #define SNTP_SUPPORT_MULTIPLE_SERVERS 1 // Override. Allow multiple servers.
@@ -106,8 +105,8 @@
  * to send in request and compare in response.
  */
 #define SNTP_GET_SYSTEM_TIME(sec, us)     do { (sec) = 0; (us) = 0; } while(0)
-#define SNTP_RECV_TIMEOUT           gSntpRecvTimeout_ms
-#define SNTP_UPDATE_DELAY           gSntpUpdateDelay_ms
+#define SNTP_RECV_TIMEOUT           gSntpRecvTimeout_s
+#define SNTP_UPDATE_DELAY           gSntpUpdateDelay_s
 #define SNTP_SUPPRESS_DELAY_CHECK   1 // needed to substitute define with global
 
 // You can change these:
@@ -118,7 +117,7 @@
   "time-b.nist.gov", \
     // comma-separated list of server URLs
 #define SNTP_CHECK_RESPONSE         2       // For now use more checks. Do not set above 2.
-#define SNTP_STARTUP_DELAY          0       // ms, Should be randomized
+#define SNTP_STARTUP_DELAY          0       // seconds, Should be randomized
 
 // END [iva2k] Defines for LWIP/sntp, using SNTPClient wrapper
 //------------------------------------------------------------------------------
--- a/lwip/HTTPServer/HTTPRPC.h	Fri Jan 08 00:28:14 2010 +0000
+++ b/lwip/HTTPServer/HTTPRPC.h	Tue Jan 12 07:41:55 2010 +0000
@@ -1,3 +1,8 @@
+// HTTPRPC.h
+// Modified by iva2k
+// Improved URL string handling
+//
+
 #ifndef HTTPRPC_H
 #define HTTPRPC_H
 
@@ -6,6 +11,10 @@
 #ifdef MBED_RPC
 #include "rpc.h"
 
+#ifndef HTTPRPC_USE_URI_FIELDS
+#define HTTPRPC_USE_URI_FIELDS 1
+#endif
+
 /**
  * A datastorage helper for the HTTPRPC class
  */
@@ -25,7 +34,7 @@
      */
     HTTPRPC(const char *path = "/rpc") : HTTPHandler(path) {}
     HTTPRPC(HTTPServer *server, const char *path = "/rpc") : HTTPHandler(path) { server->addHandler(this); }
-    
+
   private:
     /**
      * If you need some Headerfeelds you have tor register the feelds here.
@@ -44,7 +53,7 @@
       HTTPRPCData *data = new HTTPRPCData();
       con->data = data;
       char *query = con->getURL()+strlen(_prefix);
-      clean(query);
+      query = clean(query);
       rpc(query, data->result);
 
       const char *nfields = "Cache-Control: no-cache, no-store, must-revalidate\r\nPragma: no-cache\r\nExpires: Thu, 01 Dec 1994 16:00:00 GM";
@@ -83,7 +92,7 @@
      * If we have not enought space wait for next time.
      */
     virtual HTTPHandle send(HTTPConnection *con, int maximum) const {
-      HTTPRPCData *data = static_cast<HTTPRPCData *>(con->data);    
+      HTTPRPCData *data = static_cast<HTTPRPCData *>(con->data);
       if(maximum>64) {
         con->write(data->result, con->getLength());
         return HTTP_SuccessEnded;
@@ -93,21 +102,144 @@
       }
     }
 
+    inline bool is_hex(const char a) const {
+        return (a>='0' && a<='9') || (a>='A' && a<='F') || (a>='a' && a<='f');
+    }
+    inline char hex_nib(const char a) const {
+        return 0xf & (
+          (a>='0' && a<='9') ? (a-'0'   ) :
+          (a>='A' && a<='F') ? (a-'A'+10) :
+          (a>='a' && a<='f') ? (a-'a'+10) : 0
+        );
+    }
+    inline char hex_byte(const char *str) const {
+        return (hex_nib(*str) << 4) | hex_nib(*(str+1));
+    }
     /**
-     * To reduce memory usage we sodify the URL directly 
+     * To reduce memory usage we sodify the URL directly
      * and replace '%20',',','+','=' with spaces.
      */
-    inline void clean(char *str) const {
-      while(*str++) {
-        if(*str=='%'&&*(str+1)=='2'&&*(str+2)=='0') {
-          *str = ' ';
-          *(str+1) = ' ';
-          *(str+2) = ' ';
+//    mutable char _buf[1024];
+    inline char *clean(char *str) const {
+      char *in = str;
+//      char *out = _buf;
+      char *out = str;    // this will do conversions in-place.
+      bool inquotes=false;
+      bool backslash=false;
+      bool hasquery=false;
+      bool cantquery=false;
+        // cantquery=true will indicate that the URI already has symbols 
+        // incompatible with query, so it disables checking for query.
+#if HTTPRPC_USE_URI_FIELDS
+      bool infield=false;
+//      bool rpcquery=false;
+      char *field=out;
+#endif
+
+printf("\r\nDEBUG HTTPRPC::clean() IN=:%s:\r\n",str);
+      while (*in) {
+#if HTTPRPC_USE_URI_FIELDS
+        // Check if URI has query part
+        // in form "/rpc/obj/method?arg1=val1&arg2=val2&arg3=val3"
+        if (!inquotes && !cantquery && !hasquery && *in == '?') {
+          hasquery = true;
+          *out = ' '; out++;  // delimit base URI part
+          infield = true; in++; field=in; continue;
+          // New field started. Do nothing yet
+        }
+        // Check if URI with query part is delimited
+        if (!inquotes && !infield && hasquery && *in == '&') {
+          *out = ' '; out++;  // delimit current arg
+          infield = true; in++; field=in; continue;
+          // New field started
         }
-        if(*str==','||*str=='+'||*str=='=') {
-          *str = ' ';
+        if (infield) {
+          // Process the query - skip till '='
+          // Also check if it is in form "/rpc/obj/method?val1&val2&val3"
+          if (!inquotes && *in == '&') {
+            // modified query - catch up
+            while (field<in) {
+              *out = *field;
+              if (*field=='%' && is_hex(*(field+1)) && is_hex(*(field+2)) ) {
+                *out = hex_byte(++field);
+                field++;    // field is incremented by 2 total
+              }
+              if (!backslash && *out == '"') { inquotes = !inquotes; }
+              backslash = inquotes && !backslash && (*out == '\\');
+
+              out++;
+              field++;
+            }
+            
+            *out = ' '; out++;  // delimit current arg
+            infield = true; in++; field=in; continue;
+            // New field started
+          } else
+          if (!inquotes && *in == '=') {
+            infield = false;
+            *in = '\0';  // this will mark the field name
+printf("    - field: %s\r\n", field);
+// FIXME: here we have a field/arg name. Can we use it to reorder the arguments for the rpc?
+
+          } else {
+            // Keep tracking quotes
+            char tmp = *in;
+            if (*in=='%' && is_hex(*(in+1)) && is_hex(*(in+2)) ) {
+              tmp = hex_byte(++in);
+              in++;    // in is incremented by 2 total
+            }
+            if (!backslash && tmp == '"') { inquotes = !inquotes; }
+            backslash = inquotes && !backslash && (tmp == '\\');
+          }
+        } else
+#endif    // HTTPRPC_USE_URI_FIELDS
+        {
+          // Keep processing the stream
+          *out = *in;
+
+          if (*in=='%' && is_hex(*(in+1)) && is_hex(*(in+2)) ) {
+            *out = hex_byte(++in);
+            in++;    // in is incremented by 2 total
+            cantquery = !hasquery;   // any %-encoded before '?' means it can't be a query
+          } else
+          if (!inquotes && !hasquery && (*in==',' || *in=='+' || *in=='=')) {
+            *out = ' ';
+//            cantquery = !hasquery;   // any non-query delimiter disallows URI query
+          }
+          if (!backslash && *out == '"') { inquotes = !inquotes; }
+          backslash = inquotes && !backslash && (*out == '\\');
+
+          out++;
+        }
+
+        in++;
+      } // while
+
+#if HTTPRPC_USE_URI_FIELDS
+      if (infield) {
+        // modified query last arg - catch up
+        while (field < in) {
+          *out = *field;
+          if (*field=='%' && is_hex(*(field+1)) && is_hex(*(field+2)) ) {
+            *out = hex_byte(++field);
+            field++;    // field is incremented by 2 total
+          }
+          if (!backslash && *out == '"') { inquotes = !inquotes; }
+          backslash = inquotes && !backslash && (*out == '\\');
+
+          out++;
+          field++;
         }
       }
+#endif    // HTTPRPC_USE_URI_FIELDS
+
+      hasquery = cantquery; // only removes compiler warning
+      *out = '\0';
+
+//      out = _buf;
+      out = str;
+printf("DEBUG HTTPRPC::clean() OUT=:%s:\r\n", out);
+      return out;
     }
 };
 #endif
--- a/lwip/SNTPClient/DstZones.h	Fri Jan 08 00:28:14 2010 +0000
+++ b/lwip/SNTPClient/DstZones.h	Tue Jan 12 07:41:55 2010 +0000
@@ -23,14 +23,14 @@
     // USA: 3600; 2, Second SUN, March; 2, First SUN, November
 _( DST_USA,    NULL, 0, 3600,    2, 2, 0, 2,    2, 1, 0, 10 )
 
-    // USA: 3600; 2, First SUN, April; 2, Last SUN, October
+    // USA: 3600; 2, First SUN, April; 2, Last SUN, October (Pre-2007)
 _( DST_USA_06, NULL, 0, 3600,    2, 1, 0, 3,    2, 0, 0,  9 )
 
     // EU: 3600; 1GMT, Last SUN, March; 1GMT, Last SUN, October
 _( DST_EU_GMT, NULL, 1, 3600,    1, 0, 0, 2,    1, 0, 0,  9 )
 
-    // Just for test
-_( DST_TEST,   NULL, 0, 3600,   18, 1, 2, 0,   12, 1, 4,  0 )
+//    // Just for test
+//_( DST_TEST,   NULL, 0, 3600,   18, 1, 2, 0,   12, 1, 4,  0 )
 
 
 #undef _
--- a/lwip/SNTPClient/SNTPClient.cpp	Fri Jan 08 00:28:14 2010 +0000
+++ b/lwip/SNTPClient/SNTPClient.cpp	Tue Jan 12 07:41:55 2010 +0000
@@ -76,8 +76,8 @@
 extern "C" {
 #endif
 tDST_ZONE    gSntpDstZone = DST_NONE;       // DST zone - rule selector
-unsigned int gSntpRecvTimeout_ms = 3000;    // 3 sec; SNTP_RECV_TIMEOUT
-unsigned int gSntpUpdateDelay_ms = 3600000; // 1 hour; SNTP_UPDATE_DELAY
+unsigned int gSntpRecvTimeout_s = 3;        // 3 sec; SNTP_RECV_TIMEOUT
+unsigned int gSntpUpdateDelay_s = 3600;     // 1 hour; SNTP_UPDATE_DELAY
 signed int   gSntpTimezone =  0*3600;       // seconds from UTC to local time
 signed int   gSntpDST      =  0*3600;       // seconds from UTC to local time
 bool         gSntpRtcUtc   = false;         // true to keep RTC in UTC, false to keep in local time
@@ -525,7 +525,7 @@
 static void (*sntp_addresses_free)(void*) = NULL;
 static Timeout _sntp_timer1;
 static Timeout _sntp_timer2;
-void sntp_sys_timeout(u32_t timeout_ms, void (*func)(void *arg), void *arg) {
+void sntp_sys_timeout(u32_t timeout_s, void (*func)(void *arg), void *arg) {
 // all we really need to track is only 2 functions: sntp_request, sntp_try_next_server
     Timeout *t = NULL;
     if (func == &sntp_request) {
@@ -537,7 +537,7 @@
     }
     if (t) {
         t->detach();
-        t->attach((void(*)(void))func, 0.001*timeout_ms);
+        t->attach((void(*)(void))func, 1.0*timeout_s);
         // Another shortcut - we have no arg to pass, so just typecast the func.
     }
 }
@@ -574,8 +574,8 @@
     }
 }
 
-void SNTPSetRecvTimeout(unsigned int val_ms) { gSntpRecvTimeout_ms = val_ms; }
-void SNTPSetUpdateDelay(unsigned int val_ms) { gSntpUpdateDelay_ms = val_ms; }
+void SNTPSetRecvTimeout(unsigned int val_s) { gSntpRecvTimeout_s = val_s; }
+void SNTPSetUpdateDelay(unsigned int val_s) { gSntpUpdateDelay_s = val_s; }
 void SNTPSetTimezone(float hours_from_utc, bool adjust_clock) {
     if (adjust_clock && !gSntpRtcUtc) {
         time_t seconds = time(NULL);
@@ -672,8 +672,8 @@
     fprintf(f, "Timezone=%0.1f" CRLF, gSntpTimezone / 3600.0);
     fprintf(f, "DstZone=%d" CRLF, gSntpDstZone);
     fprintf(f, "# %s" CRLF, SNTPDstZoneName(gSntpDstZone));
-    fprintf(f, "UpdateDelay=%d" CRLF, gSntpUpdateDelay_ms);
-    fprintf(f, "RecvTimeout=%d" CRLF, gSntpRecvTimeout_ms);
+    fprintf(f, "UpdateDelay=%d" CRLF, gSntpUpdateDelay_s);
+    fprintf(f, "RecvTimeout=%d" CRLF, gSntpRecvTimeout_s);
     fprintf(f, CRLF "##END" CRLF);
 }
 
@@ -733,9 +733,9 @@
                     if (0 == strncmp(buf1, "Timezone", sizeof("Timezone")-1)) {
                         gSntpTimezone = strtod(buf2, &buf2) * 3600;
                     } else if (0 == strncmp(buf1, "UpdateDelay", sizeof("UpdateDelay")-1)) {
-                        gSntpUpdateDelay_ms = strtoul(buf2, &buf2, 10);
+                        gSntpUpdateDelay_s = strtoul(buf2, &buf2, 10);
                     } else if (0 == strncmp(buf1, "RecvTimeout", sizeof("RecvTimeout")-1)) {
-                        gSntpRecvTimeout_ms = strtoul(buf2, &buf2, 10);
+                        gSntpRecvTimeout_s = strtoul(buf2, &buf2, 10);
                     } else if (0 == strncmp(buf1, "RtcUtc", sizeof("RtcUtc")-1)) {
                         gSntpRtcUtc = (bool)strtol(buf2, &buf2, 10);
                     } else if (0 == strncmp(buf1, "DstZone", sizeof("DstZone")-1)) {
@@ -772,8 +772,8 @@
 #endif
 
     // SNTPv4 RFC 4330 enforces a minimum update time of 15 seconds
-    if (gSntpUpdateDelay_ms < 15000) {
-        gSntpUpdateDelay_ms = 15000;
+    if (gSntpUpdateDelay_s < 15) {
+        gSntpUpdateDelay_s = 15;
     }
 
     gSntpRunning = true;
--- a/lwip/SNTPClient/SNTPClient.h	Fri Jan 08 00:28:14 2010 +0000
+++ b/lwip/SNTPClient/SNTPClient.h	Tue Jan 12 07:41:55 2010 +0000
@@ -21,8 +21,8 @@
 void SNTPSetDstZone(tDST_ZONE zone, bool adjust_clock=false);
 const char *SNTPDstZoneName(tDST_ZONE zone);
 
-void SNTPSetRecvTimeout(unsigned int val_ms);
-void SNTPSetUpdateDelay(unsigned int val_ms);
+void SNTPSetRecvTimeout(unsigned int val_s);
+void SNTPSetUpdateDelay(unsigned int val_s);
 void SNTPSetTimezone(float hours_from_utc, bool adjust_clock=false);
 void SNTPSetDST(float hours_from_utc, bool adjust_clock=false);
 int  SNTPSetAddresses(const char* server_addresses[], int count, void (*p_free)(void*));
--- a/myrpc.cpp	Fri Jan 08 00:28:14 2010 +0000
+++ b/myrpc.cpp	Tue Jan 12 07:41:55 2010 +0000
@@ -32,6 +32,12 @@
   }
 }
 
+char *myrpc::echo(const char * in) {
+printf("DEBUG myrpc::echo() IN=%s\r\n",in);
+    sprintf(_buffer, "%s", in);
+    return _buffer;
+}
+
 void myrpc::settime(const char *t) {
   time_t seconds = time(NULL);
   if (seconds == (unsigned)-1 || seconds == 0) {
@@ -58,6 +64,7 @@
   static const rpc_method rpc_methods[] = {
     { "debug", rpc_method_caller<myrpc, int, &myrpc::debug> },
     { "blink", rpc_method_caller<myrpc, int, &myrpc::blink> },
+    { "echo", rpc_method_caller<char *, myrpc, const char *, &myrpc::echo> },
     { "settime", rpc_method_caller<myrpc, const char *, &myrpc::settime> },
     { "gettime", rpc_method_caller<char *, myrpc, const char *, &myrpc::gettime> },
     RPC_METHOD_SUPER(Base)
--- a/myrpc.h	Fri Jan 08 00:28:14 2010 +0000
+++ b/myrpc.h	Tue Jan 12 07:41:55 2010 +0000
@@ -16,6 +16,7 @@
   myrpc(PinName pin, const char* name = NULL);
   void debug(int val);
   void blink(int n);
+  char * echo(const char *);
   void settime(const char *t);
   char *gettime(const char *fmt);
   
@@ -26,7 +27,7 @@
 
 protected:
     DigitalOut _pin;
-    char _buffer[64];     // String buffer
+    char _buffer[256];     // String buffer
 };
 
 }    // namespace mbed