A HTTP/HTTPS Client for the mbed networking/CyaSSL ssl library

Dependents:   Anpi dropbox_access php_access_auth TwitterReader ... more

Fork of HTTPClient by Donatien Garnier

HTTP and HTTPS Client Class with wolfSSL, embedded SSL library.

/media/uploads/wolfSSL/wolfssl_logo.png

The class was forked from http://mbed.org/users/donatien/code/HTTPClient/

It, now, accepts url both with "http://" and "https://".

Allocate caller thread with 16kbytes or larger stack for "https" requests.

Rest of the API stays compatible with HTTPClient.

For more about the library, see http://www.wolfssl.com. http://wolfssl.com/yaSSL/Docs.html.

Extended methods:

  • HTTPResult basicAuth(const char* user, const char* password); /* set id/passwd for basic Authentication */
  • void setHeader(char *header) ; /* set http headers */
  • HTTPResult setSSLversion(int minorV) ; /* set SSL/TLS version. 0: SSL3, 1: TLS1.0, 2: TLS1.1, 3: TLS1.2 */

Files at this revision

API Documentation at this revision

Comitter:
wolfSSL
Date:
Mon Apr 07 23:30:35 2014 +0000
Parent:
16:1f743885e7de
Child:
18:d89df40b4cf3
Commit message:
Added SSL by CyaSSL library; Added header method; Improved send buffer; Added retry connection

Changed in this revision

HTTPClient.cpp Show annotated file Show diff for this revision Revisions of this file
HTTPClient.h Show annotated file Show diff for this revision Revisions of this file
--- a/HTTPClient.cpp	Thu Aug 30 15:38:57 2012 +0000
+++ b/HTTPClient.cpp	Mon Apr 07 23:30:35 2014 +0000
@@ -34,22 +34,68 @@
 #endif
 
 #define HTTP_PORT 80
+#define HTTPS_PORT 443
 
 #define OK 0
 
 #define MIN(x,y) (((x)<(y))?(x):(y))
 #define MAX(x,y) (((x)>(y))?(x):(y))
 
-#define CHUNK_SIZE 256
+#include <cstring>
 
-#include <cstring>
+#include  <../CyaSSL/cyassl/ctaocrypt/settings.h>
+#include <../CyaSSL/cyassl/ctaocrypt/types.h>
+#include <../CyaSSL/cyassl/internal.h>
+#include <../CyaSSL/cyassl/ssl.h>
 
 #include "HTTPClient.h"
+#include "TCPSocketConnection.h"
+
+class TCPSocketConnection_fd: public TCPSocketConnection 
+{
+public:
+    int get_fd() {
+        return _sock_fd ;
+    }
+} ;
+
+static  TCPSocketConnection_fd m_sock;
+
+#define CHUNK_SIZE    256
+#define SEND_BUF_SIZE 512
+static char send_buf[SEND_BUF_SIZE] ;
+static char *send_buf_p ;
+
+static int SocketReceive(CYASSL* ssl, char *buf, int sz, void *ctx)
+{
+    int n ;
+    int i ;
+    #define RECV_RETRY 3
+    for(i=0; i<RECV_RETRY; i++) {
+        n = m_sock.receive(buf, sz) ;
+        if(n >= 0)return n  ;
+    }
+    ERR("SocketReceive:%d/%d\n", n, sz)  ;
+    return n ;
+}
+
+static int SocketSend(CYASSL* ssl, char *buf, int sz, void *ctx)
+{
+    int n ;
+
+    n = m_sock.send(buf, sz);
+    if(n > 0) {
+        return n ;
+    } else  ERR("SocketSend:%d/%d\n", n, sz);
+    return n ;
+}
 
 HTTPClient::HTTPClient() :
-m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
+m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
 {
-
+  //CyaSSL_Debugging_ON() ;
+  ctx = 0 ;
+  ssl = 0 ;
 }
 
 HTTPClient::~HTTPClient()
@@ -97,9 +143,16 @@
   return m_httpResponseCode;
 }
 
+void HTTPClient::setHeader(char * h)
+{
+    header = h ;
+}
+
+
 #define CHECK_CONN_ERR(ret) \
   do{ \
     if(ret) { \
+      cyassl_free() ;\
       m_sock.close(); \
       ERR("Connection error (%d)", ret); \
       return HTTP_CONN; \
@@ -108,11 +161,21 @@
 
 #define PRTCL_ERR() \
   do{ \
+    cyassl_free() ;\
     m_sock.close(); \
     ERR("Protocol error"); \
     return HTTP_PRTCL; \
   } while(0)
 
+void HTTPClient::cyassl_free(void)
+{
+    if(ssl)
+        CyaSSL_free(ssl) ;
+    if(ctx)
+        CyaSSL_CTX_free(ctx) ;
+}
+
+
 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
 { 
   m_httpResponseCode = 0; //Invalidate code
@@ -125,10 +188,12 @@
   }
 
   char scheme[8];
-  uint16_t port;
   char host[32];
   char path[64];
-  //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
+
+  int ret ;
+
+  //First we need to parse the url (http[s]://host[:port][/[path]]) 
   HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
   if(res != HTTP_OK)
   {
@@ -138,7 +203,10 @@
 
   if(port == 0) //TODO do handle HTTPS->443
   {
-    port = 80;
+    if(strcmp(scheme, "http") == 0)
+      port = HTTP_PORT ;
+    else if(strcmp(scheme, "https") == 0)
+      port = HTTPS_PORT ;
   }
 
   DBG("Scheme: %s", scheme);
@@ -148,17 +216,59 @@
 
   //Connect
   DBG("Connecting socket to server");
-  int ret = m_sock.connect(host, port);
-  if (ret < 0)
+  sockfd = m_sock.get_fd() ;
+  
+  #define MAX_RETRY 5
+  int retry ;
+
+  for(retry=0; retry<MAX_RETRY; retry++) {
+    int ret = m_sock.connect(host, port);
+    if(ret == 0)break ;
+  }
+  if(retry == MAX_RETRY)
   {
     m_sock.close();
     ERR("Could not connect");
     return HTTP_CONN;
   }
 
+  if(port == HTTPS_PORT) { 
+  /* Start SSL connect */
+    ctx = CyaSSL_CTX_new(
+    CyaTLSv1_2_client_method
+                      //CyaSSLv3_client_method
+    ());
+    if (ctx == NULL) {
+      ERR("unable to get ctx");
+      return HTTP_CONN;
+    }
+    CyaSSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
+
+    ssl = CyaSSL_new(ctx);
+    if (ssl == NULL) {
+       ERR("unable to get SSL object");
+       cyassl_free() ;
+       return HTTP_CONN;
+    }
+
+    CyaSSL_SetVersion(ssl, CYASSL_TLSV1_2) ;
+    CyaSSL_set_fd(ssl, sockfd);
+    CyaSSL_SetIORecv(ctx, SocketReceive) ;
+    CyaSSL_SetIOSend(ctx, SocketSend) ;
+    DBG("ctx=%x, ssl=%x, ssl->ctx->CBIORecv, CBIOSend=%x, %x\n",
+    ctx, ssl, SocketReceive, SocketSend ) ;
+    if (CyaSSL_connect(ssl) != SSL_SUCCESS) {
+      ERR("SSL_connect failed");
+      cyassl_free() ;
+      return HTTP_CONN;
+    }
+  } /* SSL connect complete */
+  
   //Send request
   DBG("Sending request");
   char buf[CHUNK_SIZE];
+  send_buf_p = send_buf ; // Reset send buffer ;
+  
   const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
   snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
   ret = send(buf);
@@ -195,6 +305,12 @@
     }
   }
   
+  //Add user headers
+  if(header) {
+    ret = send(header);
+    CHECK_CONN_ERR(ret);
+  }
+
   //Close headers
   DBG("Headers sent");
   ret = send("\r\n");
@@ -210,6 +326,8 @@
     {
       size_t writtenLen = 0;
       pDataOut->read(buf, CHUNK_SIZE, &trfLen);
+      buf[trfLen] = 0x0 ;
+      DBG("buf:%s", buf) ;
       if( pDataOut->getIsChunked() )
       {
         //Write chunk header
@@ -249,9 +367,12 @@
     }
 
   }
+  ret = flush() ; // flush the send buffer ;
+  CHECK_CONN_ERR(ret);
   
   //Receive response
   DBG("Receiving response");
+
   ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
   CHECK_CONN_ERR(ret);
 
@@ -364,6 +485,7 @@
 
   //Receive data
   DBG("Receiving data");
+
   while(true)
   {
     size_t readLen = 0;
@@ -473,7 +595,8 @@
     }
 
   }
-
+  
+  cyassl_free() ;
   m_sock.close();
   DBG("Completed HTTP transaction");
 
@@ -492,6 +615,24 @@
   }
     
   int ret;
+  
+  if(port == HTTPS_PORT) {
+    DBG("Enter CyaSSL_read") ;
+    
+    m_sock.set_blocking(false, m_timeout);
+    readLen = CyaSSL_read(ssl, buf, maxLen);
+    if (readLen > 0) {
+      buf[readLen] = 0;
+      DBG("CyaSSL_read:%s\n", buf);
+    } else {
+      ERR("CyaSSL_read, ret = %d", readLen) ;
+      return HTTP_ERROR ;
+    }
+    DBG("Read %d bytes", readLen);
+    *pReadLen = readLen;
+    return HTTP_OK;
+  }
+  
   while(readLen < maxLen)
   {
     if(readLen < minLen)
@@ -510,7 +651,7 @@
     if( ret > 0)
     {
       readLen += ret;
-    }
+    } 
     else if( ret == 0 )
     {
       break;
@@ -541,11 +682,42 @@
 
 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
 {
+  HTTPResult ret ;
+  int cp_len ;
+  
   if(len == 0)
   {
     len = strlen(buf);
   }
-  DBG("Trying to write %d bytes", len);
+  
+  do {
+    if((SEND_BUF_SIZE - (send_buf_p - send_buf)) >= len){
+      cp_len = len ;
+    } else {
+      cp_len = send_buf_p - send_buf ;
+    }
+    memcpy(send_buf_p, buf, cp_len) ;
+    send_buf_p += cp_len ;
+    len -= cp_len ;
+
+    if(send_buf_p == send_buf + SEND_BUF_SIZE){
+      ret = flush() ;
+      if(ret)return(ret) ;
+    }
+  } while(len) ;
+  return HTTP_OK ;
+}
+
+HTTPResult HTTPClient::flush() //0 on success, err code on failure
+{
+  int len ;
+  char * buf ;
+  
+  buf = send_buf ;
+  len = send_buf_p - send_buf ;
+  send_buf_p = send_buf ; // reset send buffer
+  
+  DBG("Trying to write %d bytes:%s\n", len, buf);
   size_t writtenLen = 0;
     
   if(!m_sock.is_connected())
@@ -554,6 +726,15 @@
     return HTTP_CLOSED; //Connection was closed by server 
   }
   
+  if(port == HTTPS_PORT) {
+    DBG("Enter CyaSSL_write") ;
+    if (CyaSSL_write(ssl, buf, len) != len) {
+      ERR("SSL_write failed");
+      return HTTP_ERROR ;
+    }
+    DBG("Written %d bytes", writtenLen);
+    return HTTP_OK;
+  }
   m_sock.set_blocking(false, m_timeout);
   int ret = m_sock.send_all(buf, len);
   if(ret > 0)
--- a/HTTPClient.h	Thu Aug 30 15:38:57 2012 +0000
+++ b/HTTPClient.h	Mon Apr 07 23:30:35 2014 +0000
@@ -24,8 +24,6 @@
 #ifndef HTTP_CLIENT_H
 #define HTTP_CLIENT_H
 
-#include "TCPSocketConnection.h"
-
 #define HTTP_CLIENT_DEFAULT_TIMEOUT 15000
 
 class HTTPData;
@@ -125,6 +123,7 @@
   @return The HTTP response code of the last request
   */
   int getHTTPResponseCode();
+  void setHeader(char *header) ;
   
 private:
   enum HTTP_METH
@@ -139,10 +138,11 @@
   HTTPResult connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout); //Execute request
   HTTPResult recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen); //0 on success, err code on failure
   HTTPResult send(char* buf, size_t len = 0); //0 on success, err code on failure
+  HTTPResult flush(void); //0 on success, err code on failure
   HTTPResult parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen); //Parse URL
-
+  void cyassl_free(void) ;
+  
   //Parameters
-  TCPSocketConnection m_sock;
   
   int m_timeout;
 
@@ -150,6 +150,12 @@
   const char* m_basicAuthPassword;
   int m_httpResponseCode;
 
+  char * header ;
+  /* for CyaSSL */
+  int      sockfd;
+  uint16_t port;
+  struct CYASSL_CTX* ctx ;
+  struct CYASSL    * ssl ;
 };
 
 //Including data containers here for more convenience