reverted HTTPCLient debug back to defaulted off

Dependencies:   CyaSSL

Fork of HTTPClient-SSL by Keith Ruenheck

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers HTTPClient.cpp Source File

HTTPClient.cpp

00001 /* HTTPClient.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019  
00020 // DMA: Added tunable to adapt size of larger input URLs
00021 #define MAX_URL_HOSTNAME_LENGTH 128
00022 #define MAX_URL_PATH_LENGTH     128
00023 
00024 //Debug is disabled by default
00025 //#define HTTP_DEBUG
00026 #ifdef HTTP_DEBUG
00027 //Enable debug
00028 #include <cstdio>
00029 #define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
00030 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
00031 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
00032 
00033 #else
00034 //Disable debug
00035 #define DBG(x, ...)
00036 #define WARN(x, ...)
00037 #define ERR(x, ...)
00038 
00039 #endif
00040 
00041 #define HTTP_PORT 80
00042 #define HTTPS_PORT 443
00043 
00044 #define OK 0
00045 
00046 #define MIN(x,y) (((x)<(y))?(x):(y))
00047 #define MAX(x,y) (((x)>(y))?(x):(y))
00048 
00049 #include <cstring>
00050 
00051 #include <settings.h>
00052 #include <types.h>
00053 #include <internal.h>
00054 #include <ssl.h>
00055 
00056 #include "HTTPClient.h"
00057 
00058 static  TCPSocketConnection* m_sock;
00059 #define CHUNK_SIZE    1024 // changed from 256 to work with large cookies
00060 #define SEND_BUF_SIZE 1024
00061 static char send_buf[SEND_BUF_SIZE] ;
00062 static char *send_buf_p = NULL;
00063 
00064 static int SocketReceive(CYASSL* ssl, char *buf, int sz, void *ctx)
00065 {
00066     int n ;
00067     int i ;
00068 #define RECV_RETRY 3
00069 
00070     for(i=0; i<RECV_RETRY; i++) {
00071         n = m_sock->receive(buf, sz) ;
00072         if(n >= 0)return n  ;
00073         wait(0.2) ;
00074     }
00075     ERR("SocketReceive:%d/%d\n", n, sz)  ;
00076     return n ;
00077 }
00078 
00079 static int SocketSend(CYASSL* ssl, char *buf, int sz, void *ctx)
00080 {
00081     int n ;
00082         
00083     wait(0.1);
00084     n = m_sock->send(buf, sz);
00085     if(n > 0) {
00086         wait(0.3);
00087         return n;
00088     } else {
00089     ERR("SocketSend:%d/%d\n", n, sz);
00090     }
00091     return n;
00092 }
00093 
00094 static void base64enc(char *out, const char *in) {
00095     const char code[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" ;
00096     int i = 0, x = 0, l = 0;
00097 
00098     for (; *in; in++) {
00099         x = x << 8 | *in;
00100         for (l += 8; l >= 6; l -= 6) {
00101             out[i++] = code[(x >> (l - 6)) & 0x3f];
00102         }
00103     }
00104     if (l > 0) {
00105         x <<= 6 - l;
00106         out[i++] = code[x & 0x3f];
00107     }
00108     for (; i % 4;) {
00109         out[i++] = '=';
00110     }
00111     out[i] = '\0' ;
00112 }
00113 
00114 HTTPClient::HTTPClient() :
00115     m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
00116 {
00117     m_sock = &_m_sock;
00118     //CyaSSL_Debugging_ON() ;   //Turn on if the CyaSSL library isn't working, turns on debug printf's
00119     
00120     peerMethod = VERIFY_NONE;
00121     ctx = 0 ;
00122     ssl = 0 ;
00123     SSLver = 3 ; 
00124     certificates = NULL;
00125     redirect_url = NULL ;
00126     redirect = 0 ;
00127     header = NULL ;
00128 }
00129 
00130 HTTPClient::~HTTPClient()
00131 {
00132     if(m_basicAuthPassword) {
00133         free((void *)m_basicAuthPassword);
00134         m_basicAuthPassword = NULL;
00135     }
00136     if(m_basicAuthUser) {
00137         free((void *)m_basicAuthUser);
00138         m_basicAuthUser = NULL;
00139     }
00140     if(certificates) {
00141         free((void *)certificates);
00142         certificates = NULL;
00143     }
00144 }
00145 
00146 HTTPResult HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
00147 {
00148     #define AUTHB_SIZE 128
00149     if((strlen(user) + strlen(password)) >= AUTHB_SIZE)
00150         return HTTP_ERROR ;
00151         
00152     if (m_basicAuthUser) free((void *)m_basicAuthUser);
00153     if (user != NULL) {
00154         m_basicAuthUser = (char *)malloc(strlen(user)+1);
00155         strcpy((char *)m_basicAuthUser, user);
00156     } else {
00157         m_basicAuthUser = NULL;
00158     }
00159     
00160     if (m_basicAuthPassword) free((void *)m_basicAuthPassword);
00161     if (password != NULL) {
00162         m_basicAuthPassword = (char *)malloc(strlen(password)+1);
00163         strcpy((char *)m_basicAuthPassword, password); 
00164     }   else {
00165         m_basicAuthPassword = NULL;
00166     }
00167     
00168     return HTTP_OK ;
00169 }
00170 
00171 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00172 {
00173     return connect(url, HTTP_GET, NULL, pDataIn, timeout);
00174 }
00175 
00176 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00177 {
00178     HTTPText str(result, maxResultLen);
00179     return get(url, &str, timeout);
00180 }
00181 
00182 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00183 {
00184     return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00185 }
00186 
00187 HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00188 {
00189     return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
00190 }
00191 
00192 HTTPResult HTTPClient::del(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
00193 {
00194     return connect(url, HTTP_DELETE, NULL, pDataIn, timeout);
00195 }
00196 
00197 
00198 int HTTPClient::getHTTPResponseCode()
00199 {
00200     return m_httpResponseCode;
00201 }
00202 
00203 void HTTPClient::setHeader(const char * h)
00204 {
00205     header = h ;
00206 }
00207 
00208 void HTTPClient::setLocationBuf(char * url, int size)
00209 {
00210     redirect_url = url ;
00211     redirect_url_size = size ;
00212 }
00213 
00214 HTTPResult HTTPClient::setSSLversion(int minorV) 
00215 {
00216     if((minorV>=0) && (minorV<=3)) 
00217         SSLver = minorV ;
00218     else return HTTP_ERROR ;
00219     return HTTP_OK ;
00220 }
00221 
00222 HTTPResult HTTPClient::addRootCACertificate(const char* cert)
00223 {
00224     if(cert == NULL) {
00225         if(certificates != NULL) {
00226             free((void *)certificates);
00227         }
00228     } else {
00229         //Append certificate, else allocate new certificate
00230         if(certificates != NULL) {
00231             certificates = (char *)realloc((void *)certificates, strlen(cert) + 1 + strlen(certificates)); //+1 is for '\0' char
00232             if(certificates == NULL) {
00233                 return HTTP_ERROR;
00234             } else {
00235                 strcat((char *)certificates, cert);
00236             }
00237         } else {
00238             certificates = (char *)malloc(strlen(cert) + 1);
00239             if(certificates == NULL) {
00240                 return HTTP_ERROR;
00241             } else {
00242                 strcpy((char *)certificates, cert);
00243             }
00244         }
00245     }
00246     return HTTP_OK;
00247 }
00248 
00249 void HTTPClient::setPeerVerification(SSLMethod method) {
00250     peerMethod = method;
00251 }
00252 
00253 
00254 #define CHECK_CONN_ERR(ret) \
00255   do{ \
00256     if(ret) { \
00257       cyassl_free() ;\
00258       m_sock->close(); \
00259       ERR("Connection error (%d)", ret); \
00260       return HTTP_CONN; \
00261     } \
00262   } while(0)
00263 
00264 #define PRTCL_ERR() \
00265   do{ \
00266     cyassl_free() ;\
00267     m_sock->close(); \
00268     ERR("Protocol error"); \
00269     return HTTP_PRTCL; \
00270   } while(0)
00271 
00272 void HTTPClient::cyassl_free(void)
00273 {
00274     if(ssl) {
00275         CyaSSL_free(ssl) ;
00276         ssl = NULL ;
00277     }
00278     if(ctx) {
00279         CyaSSL_CTX_free(ctx) ;
00280         ctx = NULL ;
00281     }
00282     CyaSSL_Cleanup() ;
00283 } 
00284 
00285 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
00286 {
00287     CYASSL_METHOD * SSLmethod = 0;
00288     m_httpResponseCode = 0; //Invalidate code
00289     m_timeout = timeout;
00290     redirect = 0 ;
00291     
00292     pDataIn->writeReset();
00293     if( pDataOut ) {
00294         pDataOut->readReset();
00295     }
00296 
00297     char scheme[8];
00298     char host[MAX_URL_HOSTNAME_LENGTH];
00299     char path[MAX_URL_PATH_LENGTH];
00300 
00301     int ret ;
00302 
00303     //First we need to parse the url (http[s]://host[:port][/[path]])
00304     HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
00305     if(res != HTTP_OK) {
00306         ERR("parseURL returned %d", res);
00307         return res;
00308     }
00309 
00310     if(port == 0) {
00311         if(strcmp(scheme, "http") == 0)
00312             port = HTTP_PORT ;
00313         else if(strcmp(scheme, "https") == 0)
00314             port = HTTPS_PORT ;
00315     }
00316 
00317     DBG("Scheme: %s", scheme);
00318     DBG("Host: %s", host);
00319     DBG("Port: %d", port);
00320     DBG("Path: %s", path);
00321 
00322     //Connect
00323     DBG("Connecting socket to server");
00324 
00325 #define MAX_RETRY 5
00326     int retry ;
00327 
00328     for(retry=0; retry<MAX_RETRY; retry++) {
00329         int ret = m_sock->connect(host, port);
00330         if(ret == 0)break ;
00331         if(m_sock->is_connected()) {
00332             m_sock->close(true);
00333         }
00334     }
00335     if(retry == MAX_RETRY) {
00336         m_sock->close();
00337         ERR("Could not connect");
00338         return HTTP_CONN;
00339     }
00340 
00341     if(port == HTTPS_PORT) {
00342 
00343         /* Start SSL connect */
00344         DBG("SSLver=%d", SSLver) ;
00345         if(ctx == NULL) {
00346             switch(SSLver) {
00347             case 0 : SSLmethod = CyaSSLv3_client_method() ; break ;
00348             case 1 : SSLmethod = CyaTLSv1_client_method() ; break ;
00349             case 2 : SSLmethod = CyaTLSv1_1_client_method() ; break ;           
00350             case 3 : SSLmethod = CyaTLSv1_2_client_method() ; break ;   
00351             }
00352             ctx = CyaSSL_CTX_new((CYASSL_METHOD *)SSLmethod);
00353             if (ctx == NULL) {
00354                 ERR("unable to get ctx");
00355                 return HTTP_CONN;
00356             }
00357             
00358             if(certificates == NULL && peerMethod != VERIFY_NONE) {
00359                 ERR("No certificates passed for peer verification");
00360                 return HTTP_PROCESSING;
00361             }
00362             
00363             //SSL setup if being used
00364             { //Localize pMethod array for less overall memory time-use
00365                 std::string pMethod;
00366                 if(peerMethod == VERIFY_NONE) {
00367                     pMethod = "not verify peer";
00368                 } else if (peerMethod == VERIFY_PEER) {
00369                     pMethod = "verify peer if certificates available";
00370                     //Load the CA certificate(s) (If using multiple, concatenate them in the buffer being passed)
00371                     if(certificates != NULL) {
00372                         if (SSL_SUCCESS != CyaSSL_CTX_load_verify_buffer(ctx, (const unsigned char*)certificates, strlen(certificates), SSL_FILETYPE_PEM)) {
00373                             ERR("unable to load root certificates");
00374                             return HTTP_CONN;
00375                         }
00376                     }
00377                 }
00378                 DBG("SSL connection set to %s", pMethod.c_str());
00379             }
00380             
00381             CyaSSL_CTX_set_verify(ctx, peerMethod, 0); //SSL_VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_NONE, SSL_VERIFY_PEER
00382             
00383             CyaSSL_SetIORecv(ctx, SocketReceive) ;
00384             CyaSSL_SetIOSend(ctx, SocketSend) ;
00385         }
00386         if (ssl == NULL) {
00387             ssl = CyaSSL_new(ctx);
00388             if (ssl == NULL) {
00389                 ERR("unable to get SSL object");
00390                 cyassl_free() ;
00391                 return HTTP_CONN;
00392             }
00393         }
00394 
00395         DBG("ctx=%x, ssl=%x, ssl->ctx->CBIORecv, CBIOSend=%x, %x\r\n",
00396             ctx, ssl, SocketReceive, SocketSend ) ;
00397             int ret = CyaSSL_connect(ssl);
00398         if (ret != SSL_SUCCESS) {
00399             ERR("SSL_connect failed");
00400             int err = CyaSSL_get_error(ssl, ret);
00401             char data[32];
00402             char data_new[32];
00403             strcpy(data_new, CyaSSL_ERR_error_string(err, data));
00404             if(!strcmp(data,data_new)) {
00405                 printf("Error code [%d] is [%s]\r\n", err, data);
00406             } else {
00407                 printf("Failed to get error code [%d], Reason: [%s]\r\n", err, data_new);
00408             }
00409             cyassl_free() ;
00410             return HTTP_CONN;
00411         }
00412     } /* SSL connect complete */
00413 
00414     //Send request
00415     DBG("Sending request");
00416     char buf[CHUNK_SIZE];
00417     memset(buf,0,CHUNK_SIZE);
00418     send_buf_p = send_buf ; // Reset send buffer ;
00419 
00420     const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
00421     snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\nConnection: keep-alive\r\n", meth, path, host); //Write request
00422     ret = send(buf);
00423     if(ret) {
00424         m_sock->close();
00425         ERR("Could not write request");
00426         return HTTP_CONN;
00427     }
00428 
00429     //Send all headers
00430 
00431     //Send default headers
00432     DBG("Sending headers");
00433     if(m_basicAuthUser && m_basicAuthPassword) {
00434         bAuth() ; /* send out Basic Auth header */        
00435     }
00436     if( pDataOut != NULL ) {
00437         if( pDataOut->getIsChunked() ) {
00438             ret = send("Transfer-Encoding: chunked\r\n");
00439             CHECK_CONN_ERR(ret);
00440         } else {
00441             snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
00442             DBG("Content buf:%s", buf) ;
00443             ret = send(buf);
00444             CHECK_CONN_ERR(ret);
00445         }
00446         char type[48];
00447         if( pDataOut->getDataType(type, 48) == HTTP_OK ) {
00448             snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
00449             ret = send(buf);
00450             CHECK_CONN_ERR(ret);
00451         }
00452     }
00453 
00454     //Add user headers
00455     if(header) {
00456         ret = send((char *)header);
00457         CHECK_CONN_ERR(ret);
00458     }
00459 
00460     //Close headers
00461     DBG("Headers sent");
00462     ret = send("\r\n");
00463     CHECK_CONN_ERR(ret);
00464 
00465     size_t trfLen;
00466 
00467     //Send data (if available)
00468     if( pDataOut != NULL ) {
00469         DBG("Sending data");
00470         while(true) {
00471             size_t writtenLen = 0;
00472             pDataOut->read(buf, CHUNK_SIZE, &trfLen);
00473             buf[trfLen] = 0x0 ;
00474             DBG("buf:%s", buf) ;
00475             if( pDataOut->getIsChunked() ) {
00476                 //Write chunk header
00477                 char chunkHeader[64];
00478                 snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
00479                 ret = send(chunkHeader);
00480                 CHECK_CONN_ERR(ret);
00481             } else if( trfLen == 0 ) {
00482                 DBG("trfLen==0") ;
00483                 break;
00484             }
00485             DBG("trfLen 1=%d", trfLen) ;
00486             if( trfLen != 0 ) {
00487                 DBG("Sending 1") ;
00488                 ret = send(buf, trfLen);
00489                 DBG("Sent 1") ;
00490                 CHECK_CONN_ERR(ret);
00491             }
00492 
00493             if( pDataOut->getIsChunked()  ) {
00494                 ret = send("\r\n"); //Chunk-terminating CRLF
00495                 CHECK_CONN_ERR(ret);
00496             } else {
00497                 writtenLen += trfLen;
00498                 if( writtenLen >= pDataOut->getDataLen() ) {
00499                     DBG("writtenLen=%d", writtenLen) ;
00500                     break;
00501                 }
00502                 DBG("writtenLen+=trfLen = %d", writtenLen) ;
00503             }
00504             DBG("trfLen 2=%d", trfLen) ;
00505             if( trfLen == 0 ) {
00506                 DBG("trfLen == 0") ;
00507                 break;
00508             }
00509         }
00510 
00511     }
00512     ret = flush() ; // flush the send buffer ;
00513     CHECK_CONN_ERR(ret);
00514 
00515     //Receive response
00516     DBG("Receiving response");
00517 
00518     ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
00519     CHECK_CONN_ERR(ret);
00520 
00521     buf[trfLen] = '\0';
00522 
00523     char* crlfPtr = strstr(buf, "\r\n");
00524     if(crlfPtr == NULL) {
00525         PRTCL_ERR();
00526     }
00527 
00528     int crlfPos = crlfPtr - buf;
00529     buf[crlfPos] = '\0';
00530 
00531     //Parse HTTP response
00532     if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) {
00533         //Cannot match string, error
00534         ERR("Not a correct HTTP answer : %s\n", buf);
00535         PRTCL_ERR();
00536     }
00537 
00538     if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 400) ) {
00539         //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
00540         WARN("Response code %d", m_httpResponseCode);
00541         PRTCL_ERR();
00542     }
00543 
00544     DBG("Reading headers");
00545 
00546     memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00547     trfLen -= (crlfPos + 2);
00548 
00549     size_t recvContentLength = 0;
00550     bool recvChunked = false;
00551     //Now get headers
00552     while( true ) {
00553         crlfPtr = strstr(buf, "\r\n");
00554         if(crlfPtr == NULL) {
00555             if( trfLen < CHUNK_SIZE - 1 ) {
00556                 size_t newTrfLen = 0;
00557                 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00558                 trfLen += newTrfLen;
00559                 buf[trfLen] = '\0';
00560                 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
00561                 CHECK_CONN_ERR(ret);
00562                 continue;
00563             } else {
00564                 PRTCL_ERR();
00565             }
00566         }
00567 
00568         crlfPos = crlfPtr - buf;
00569 
00570         if(crlfPos == 0) { //End of headers
00571             DBG("Headers read");
00572             memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
00573             trfLen -= 2;
00574             break;
00575         }
00576 
00577         buf[crlfPos] = '\0';
00578 
00579         char key[41];
00580         char value[41];
00581 
00582         memset(key,0,41);
00583         memset(value,0,41);
00584 
00585         int n = sscanf(buf, "%40[^:]: %40[^\r\n]", key, value);
00586         if ( n == 2 ) {
00587             DBG("Read header : %s: %s\n", key, value);
00588             if( !strcmp(key, "Content-Length") ) {
00589                 sscanf(value, "%d", &recvContentLength);
00590                 pDataIn->setDataLen(recvContentLength);
00591             } else if( !strcmp(key, "Transfer-Encoding") ) {
00592                 if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") ) {
00593                     recvChunked = true;
00594                     pDataIn->setIsChunked(true);
00595                 }
00596             } else if( !strcmp(key, "Content-Type") ) {
00597                 pDataIn->setDataType(value);
00598             } else if( !strcmp(key, "location") && redirect_url) {
00599                 sscanf(buf, "%40[^:]: %128[^\r\n]", key, redirect_url);
00600                 DBG("Redirect %s: %s", key, redirect_url) ;
00601                 redirect = 1 ;
00602             }
00603             memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
00604             trfLen -= (crlfPos + 2);
00605 
00606         } else {
00607             ERR("Could not parse header");
00608             PRTCL_ERR();
00609         }
00610 
00611     }
00612 
00613     //Receive data
00614     DBG("Receiving data");
00615 
00616     while(true) {
00617         size_t readLen = 0;
00618 
00619         if( recvChunked ) {
00620             //Read chunk header
00621             bool foundCrlf;
00622             do {
00623                 foundCrlf = false;
00624                 crlfPos=0;
00625                 buf[trfLen]=0;
00626                 if(trfLen >= 2) {
00627                     for(; crlfPos < trfLen - 2; crlfPos++) {
00628                         if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' ) {
00629                             foundCrlf = true;
00630                             break;
00631                         }
00632                     }
00633                 }
00634                 if(!foundCrlf) { //Try to read more
00635                     if( trfLen < CHUNK_SIZE ) {
00636                         size_t newTrfLen = 0;
00637                         ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00638                         trfLen += newTrfLen;
00639                         CHECK_CONN_ERR(ret);
00640                         continue;
00641                     } else {
00642                         PRTCL_ERR();
00643                     }
00644                 }
00645             } while(!foundCrlf);
00646             buf[crlfPos] = '\0';
00647             int n = sscanf(buf, "%x", &readLen);
00648             if(n!=1) {
00649                 ERR("Could not read chunk length");
00650                 PRTCL_ERR();
00651             }
00652 
00653             memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
00654             trfLen -= (crlfPos + 2);
00655 
00656             if( readLen == 0 ) {
00657                 //Last chunk
00658                 break;
00659             }
00660         } else {
00661             readLen = recvContentLength;
00662         }
00663 
00664         DBG("Retrieving %d bytes", readLen);
00665 
00666         do {
00667             pDataIn->write(buf, MIN(trfLen, readLen));
00668             if( trfLen > readLen ) {
00669                 memmove(buf, &buf[readLen], trfLen - readLen);
00670                 trfLen -= readLen;
00671                 readLen = 0;
00672             } else {
00673                 readLen -= trfLen;
00674             }
00675 
00676             if(readLen) {
00677                 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
00678                 CHECK_CONN_ERR(ret);
00679             }
00680         } while(readLen);
00681 
00682         if( recvChunked ) {
00683             if(trfLen < 2) {
00684                 size_t newTrfLen = 0;
00685                 //Read missing chars to find end of chunk
00686                 ret = recv(buf + trfLen, 2 - trfLen, CHUNK_SIZE - trfLen - 1, &newTrfLen);
00687                 CHECK_CONN_ERR(ret);
00688                 trfLen += newTrfLen;
00689             }
00690             if( (buf[0] != '\r') || (buf[1] != '\n') ) {
00691                 ERR("Format error");
00692                 PRTCL_ERR();
00693             }
00694             memmove(buf, &buf[2], trfLen - 2);
00695             trfLen -= 2;
00696         } else {
00697             break;
00698         }
00699 
00700     }
00701     
00702     m_sock->close(true);
00703     cyassl_free() ;
00704     DBG("Completed HTTP transaction");
00705     if(redirect)return HTTP_REDIRECT ;
00706     else        return HTTP_OK;
00707 }
00708 
00709 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen)   //0 on success, err code on failure
00710 {
00711     DBG("Trying to read between %d and %d bytes", minLen, maxLen);
00712     size_t readLen = 0;
00713 
00714     if(!m_sock->is_connected()) {
00715         WARN("Connection was closed by server");
00716         return HTTP_CLOSED; //Connection was closed by server
00717     }
00718 
00719     int ret;
00720 
00721     if(port == HTTPS_PORT) {
00722         DBG("Enter CyaSSL_read") ;
00723 
00724         m_sock->set_blocking(false, m_timeout);
00725         readLen = CyaSSL_read(ssl, buf, maxLen);
00726         if (readLen > 0) {
00727             buf[readLen] = 0;
00728             DBG("CyaSSL_read:%s\n", buf);
00729         } else {
00730             ERR("CyaSSL_read, ret = %d", readLen) ;
00731             return HTTP_ERROR ;
00732         }
00733         DBG("Read %d bytes", readLen);
00734         *pReadLen = readLen;
00735         return HTTP_OK;
00736     }
00737 
00738     while(readLen < maxLen) {
00739         if(readLen < minLen) {
00740             DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
00741             m_sock->set_blocking(false, m_timeout);
00742             ret = m_sock->receive_all(buf + readLen, minLen - readLen);
00743         } else {
00744             DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
00745             m_sock->set_blocking(false, 0);
00746             ret = m_sock->receive(buf + readLen, maxLen - readLen);
00747         }
00748 
00749         if( ret > 0) {
00750             readLen += ret;
00751         } else if( ret == 0 ) {
00752             break;
00753         } else {
00754             if(!m_sock->is_connected()) {
00755                 ERR("Connection error (recv returned %d)", ret);
00756                 *pReadLen = readLen;
00757                 return HTTP_CONN;
00758             } else {
00759                 break;
00760             }
00761         }
00762 
00763         if(!m_sock->is_connected()) {
00764             break;
00765         }
00766     }
00767     DBG("Read %d bytes", readLen);
00768     *pReadLen = readLen;
00769     m_sock->set_blocking(false, m_timeout);
00770     return HTTP_OK;
00771 }
00772 
00773 HTTPResult HTTPClient::send(char* buf, size_t len)   //0 on success, err code on failure
00774 {
00775     HTTPResult ret ;
00776     int cp_len ;
00777 
00778     if(len == 0) {
00779         len = strlen(buf);
00780     }
00781 
00782     do {
00783 
00784         if((SEND_BUF_SIZE - (send_buf_p - send_buf)) >= len) {
00785             cp_len = len ;
00786         } else {
00787             cp_len = SEND_BUF_SIZE - (send_buf_p - send_buf) ;
00788         }
00789         DBG("send_buf_p:%x. send_buf+SIZE:%x, len=%d, cp_len=%d", send_buf_p, send_buf+SEND_BUF_SIZE, len, cp_len) ;
00790         memcpy(send_buf_p, buf, cp_len) ;
00791         send_buf_p += cp_len ;
00792         len -= cp_len ;
00793 
00794         if(send_buf_p == send_buf + SEND_BUF_SIZE) {
00795             if(port == HTTPS_PORT){
00796                 ERR("HTTPClient::send buffer overflow");
00797                 return HTTP_ERROR ;
00798             }
00799             ret = flush() ;
00800             if(ret)return(ret) ;
00801         }
00802     } while(len) ;
00803     return HTTP_OK ;
00804 }
00805 
00806 HTTPResult HTTPClient::flush()   //0 on success, err code on failure
00807 {
00808     int len ;
00809     char * buf ;
00810 
00811     buf = send_buf ;
00812     len = send_buf_p - send_buf ;
00813     send_buf_p = send_buf ; // reset send buffer
00814 
00815     DBG("Trying to write %d bytes:%s\n", len, buf);
00816     size_t writtenLen = 0;
00817 
00818     if(!m_sock->is_connected()) {
00819         WARN("Connection was closed by server");
00820         return HTTP_CLOSED; //Connection was closed by server
00821     }
00822 
00823     if(port == HTTPS_PORT) {
00824         DBG("Enter CyaSSL_write") ;
00825         if (CyaSSL_write(ssl, buf, len) != len) {
00826             ERR("SSL_write failed");
00827             return HTTP_ERROR ;
00828         }
00829         DBG("Written %d bytes", writtenLen);
00830         return HTTP_OK;
00831     }
00832     m_sock->set_blocking(false, m_timeout);
00833     int ret = m_sock->send_all(buf, len);
00834     if(ret > 0) {
00835         writtenLen += ret;
00836     } else if( ret == 0 ) {
00837         WARN("Connection was closed by server");
00838         return HTTP_CLOSED; //Connection was closed by server
00839     } else {
00840         ERR("Connection error (send returned %d)", ret);
00841         return HTTP_CONN;
00842     }
00843 
00844     DBG("Written %d bytes", writtenLen);
00845     return HTTP_OK;
00846 }
00847 
00848 HTTPResult HTTPClient::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen)   //Parse URL
00849 {
00850     char* schemePtr = (char*) url;
00851     char* hostPtr = (char*) strstr(url, "://");
00852     if(hostPtr == NULL) {
00853         WARN("Could not find host");
00854         return HTTP_PARSE; //URL is invalid
00855     }
00856 
00857     if( maxSchemeLen < hostPtr - schemePtr + 1 ) { //including NULL-terminating char
00858         WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
00859         return HTTP_PARSE;
00860     }
00861     memcpy(scheme, schemePtr, hostPtr - schemePtr);
00862     scheme[hostPtr - schemePtr] = '\0';
00863 
00864     hostPtr+=3;
00865 
00866     size_t hostLen = 0;
00867 
00868     char* portPtr = strchr(hostPtr, ':');
00869     if( portPtr != NULL ) {
00870         hostLen = portPtr - hostPtr;
00871         portPtr++;
00872         if( sscanf(portPtr, "%hu", port) != 1) {
00873             WARN("Could not find port");
00874             return HTTP_PARSE;
00875         }
00876     } else {
00877         *port=0;
00878     }
00879     char* pathPtr = strchr(hostPtr, '/');
00880     
00881     if( hostLen == 0 ) {
00882         hostLen = pathPtr - hostPtr;
00883     }
00884 
00885     if( maxHostLen < hostLen + 1 ) { //including NULL-terminating char
00886         WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
00887         return HTTP_PARSE;
00888     }
00889 
00890     size_t pathLen;
00891     char* fragmentPtr = strchr(hostPtr, '#');
00892     if(fragmentPtr != NULL) {
00893         pathLen = fragmentPtr - pathPtr;
00894     } else {
00895         pathLen = strlen(pathPtr);
00896     }
00897 
00898     if( maxPathLen < pathLen + 1 ) { //including NULL-terminating char
00899         WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
00900         return HTTP_PARSE;
00901     }
00902     memcpy(host, hostPtr, hostLen);
00903     host[hostLen] = '\0';
00904     memcpy(path, pathPtr, pathLen);
00905     path[pathLen] = '\0';
00906 
00907     return HTTP_OK;
00908 }
00909 
00910 HTTPResult HTTPClient::bAuth(void)
00911 {
00912     HTTPResult ret ;
00913     char b_auth[(int)((AUTHB_SIZE+3)*4/3+1)] ;
00914     char base64buff[AUTHB_SIZE+3] ;
00915     
00916     ret = send("Authorization: Basic ") ;
00917     CHECK_CONN_ERR(ret);
00918     sprintf(base64buff, "%s:%s", m_basicAuthUser, m_basicAuthPassword) ;
00919     DBG("bAuth: %s", base64buff) ;
00920     base64enc(b_auth, base64buff) ;
00921     
00922     int b_auth_len = strlen(b_auth);
00923     if(b_auth_len + 3 >= sizeof(b_auth)) { //\r\n\0 characters make up the +3
00924         ERR("The encoded line is larger than the buffer that holds it");
00925     }
00926     b_auth[b_auth_len + 2] = '\0' ;
00927     b_auth[b_auth_len + 1] = '\n' ;
00928     b_auth[b_auth_len] = '\r' ;
00929     DBG("b_auth:%s", b_auth) ;
00930     ret = send(b_auth) ;
00931     CHECK_CONN_ERR(ret); 
00932     return HTTP_OK ;
00933 }