HTTPClient for CyaSSL

Dependencies:   CyaSSL

Dependents:   kintone-POST kintone-POST

Fork of HTTPClient by wolf SSL

Revision:
17:c73d8e61d391
Parent:
16:1f743885e7de
Child:
18:d89df40b4cf3
--- 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)