]> git.donarmstrong.com Git - qmk_firmware.git/blob - tmk_core/tool/mbed/mbed-sdk/libraries/tests/net/cellular/http/common/HTTPClient/HTTPClient.cpp
Merge commit '1fe4406f374291ab2e86e95a97341fd9c475fcb8'
[qmk_firmware.git] / tmk_core / tool / mbed / mbed-sdk / libraries / tests / net / cellular / http / common / HTTPClient / HTTPClient.cpp
1 /* HTTPClient.cpp */
2 /* Copyright (C) 2012 mbed.org, MIT License
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5  * and associated documentation files (the "Software"), to deal in the Software without restriction,
6  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
7  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in all copies or
11  * substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18  */
19
20 //Debug is disabled by default
21 #if 0
22 //Enable debug
23 #include <cstdio>
24 #define DBG(x, ...) std::printf("[HTTPClient : DBG]"x"\r\n", ##__VA_ARGS__);
25 #define WARN(x, ...) std::printf("[HTTPClient : WARN]"x"\r\n", ##__VA_ARGS__);
26 #define ERR(x, ...) std::printf("[HTTPClient : ERR]"x"\r\n", ##__VA_ARGS__);
27
28 #else
29 //Disable debug
30 #define DBG(x, ...)
31 #define WARN(x, ...)
32 #define ERR(x, ...)
33
34 #endif
35
36 #define HTTP_PORT 80
37
38 #define OK 0
39
40 #define MIN(x,y) (((x)<(y))?(x):(y))
41 #define MAX(x,y) (((x)>(y))?(x):(y))
42
43 #define CHUNK_SIZE 256
44
45 #include <cstring>
46
47 #include "HTTPClient.h"
48
49 HTTPClient::HTTPClient() :
50 m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
51 {
52
53 }
54
55 HTTPClient::~HTTPClient()
56 {
57
58 }
59
60 #if 0
61 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
62 {
63   m_basicAuthUser = user;
64   m_basicAuthPassword = password;
65 }
66 #endif
67
68 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
69 {
70   return connect(url, HTTP_GET, NULL, pDataIn, timeout);
71 }
72
73 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
74 {
75   HTTPText str(result, maxResultLen);
76   return get(url, &str, timeout);
77 }
78
79 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
80 {
81   return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
82 }
83
84 HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
85 {
86   return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
87 }
88
89 HTTPResult HTTPClient::del(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
90 {
91   return connect(url, HTTP_DELETE, NULL, pDataIn, timeout);
92 }
93
94
95 int HTTPClient::getHTTPResponseCode()
96 {
97   return m_httpResponseCode;
98 }
99
100 #define CHECK_CONN_ERR(ret) \
101   do{ \
102     if(ret) { \
103       m_sock.close(); \
104       ERR("Connection error (%d)", ret); \
105       return HTTP_CONN; \
106     } \
107   } while(0)
108
109 #define PRTCL_ERR() \
110   do{ \
111     m_sock.close(); \
112     ERR("Protocol error"); \
113     return HTTP_PRTCL; \
114   } while(0)
115
116 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
117 {
118   m_httpResponseCode = 0; //Invalidate code
119   m_timeout = timeout;
120
121   pDataIn->writeReset();
122   if( pDataOut )
123   {
124     pDataOut->readReset();
125   }
126
127   char scheme[8];
128   uint16_t port;
129   char host[32];
130   char path[64];
131   //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
132   HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
133   if(res != HTTP_OK)
134   {
135     ERR("parseURL returned %d", res);
136     return res;
137   }
138
139   if(port == 0) //TODO do handle HTTPS->443
140   {
141     port = 80;
142   }
143
144   DBG("Scheme: %s", scheme);
145   DBG("Host: %s", host);
146   DBG("Port: %d", port);
147   DBG("Path: %s", path);
148
149   //Connect
150   DBG("Connecting socket to server");
151   int ret = m_sock.connect(host, port);
152   if (ret < 0)
153   {
154     m_sock.close();
155     ERR("Could not connect");
156     return HTTP_CONN;
157   }
158
159   //Send request
160   DBG("Sending request");
161   char buf[CHUNK_SIZE];
162   const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
163   snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s\r\n", meth, path, host); //Write request
164   ret = send(buf);
165   if(ret)
166   {
167     m_sock.close();
168     ERR("Could not write request");
169     return HTTP_CONN;
170   }
171
172   //Send all headers
173
174   //Send default headers
175   DBG("Sending headers");
176   if( pDataOut != NULL )
177   {
178     if( pDataOut->getIsChunked() )
179     {
180       ret = send("Transfer-Encoding: chunked\r\n");
181       CHECK_CONN_ERR(ret);
182     }
183     else
184     {
185       snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
186       ret = send(buf);
187       CHECK_CONN_ERR(ret);
188     }
189     char type[48];
190     if( pDataOut->getDataType(type, 48) == HTTP_OK )
191     {
192       snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
193       ret = send(buf);
194       CHECK_CONN_ERR(ret);
195     }
196   }
197
198   //Close headers
199   DBG("Headers sent");
200   ret = send("\r\n");
201   CHECK_CONN_ERR(ret);
202
203   size_t trfLen;
204
205   //Send data (if available)
206   if( pDataOut != NULL )
207   {
208     DBG("Sending data");
209     while(true)
210     {
211       size_t writtenLen = 0;
212       pDataOut->read(buf, CHUNK_SIZE, &trfLen);
213       if( pDataOut->getIsChunked() )
214       {
215         //Write chunk header
216         char chunkHeader[16];
217         snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
218         ret = send(chunkHeader);
219         CHECK_CONN_ERR(ret);
220       }
221       else if( trfLen == 0 )
222       {
223         break;
224       }
225       if( trfLen != 0 )
226       {
227         ret = send(buf, trfLen);
228         CHECK_CONN_ERR(ret);
229       }
230
231       if( pDataOut->getIsChunked()  )
232       {
233         ret = send("\r\n"); //Chunk-terminating CRLF
234         CHECK_CONN_ERR(ret);
235       }
236       else
237       {
238         writtenLen += trfLen;
239         if( writtenLen >= pDataOut->getDataLen() )
240         {
241           break;
242         }
243       }
244
245       if( trfLen == 0 )
246       {
247         break;
248       }
249     }
250
251   }
252
253   //Receive response
254   DBG("Receiving response");
255   ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
256   CHECK_CONN_ERR(ret);
257
258   buf[trfLen] = '\0';
259
260   char* crlfPtr = strstr(buf, "\r\n");
261   if(crlfPtr == NULL)
262   {
263     PRTCL_ERR();
264   }
265
266   int crlfPos = crlfPtr - buf;
267   buf[crlfPos] = '\0';
268
269   //Parse HTTP response
270   if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 )
271   {
272     //Cannot match string, error
273     ERR("Not a correct HTTP answer : %s\n", buf);
274     PRTCL_ERR();
275   }
276
277   if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 300) )
278   {
279     //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
280     WARN("Response code %d", m_httpResponseCode);
281     PRTCL_ERR();
282   }
283
284   DBG("Reading headers");
285
286   memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
287   trfLen -= (crlfPos + 2);
288
289   size_t recvContentLength = 0;
290   bool recvChunked = false;
291   //Now get headers
292   while( true )
293   {
294     crlfPtr = strstr(buf, "\r\n");
295     if(crlfPtr == NULL)
296     {
297       if( trfLen < CHUNK_SIZE - 1 )
298       {
299         size_t newTrfLen;
300         ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
301         trfLen += newTrfLen;
302         buf[trfLen] = '\0';
303         DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
304         CHECK_CONN_ERR(ret);
305         continue;
306       }
307       else
308       {
309         PRTCL_ERR();
310       }
311     }
312
313     crlfPos = crlfPtr - buf;
314
315     if(crlfPos == 0) //End of headers
316     {
317       DBG("Headers read");
318       memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
319       trfLen -= 2;
320       break;
321     }
322
323     buf[crlfPos] = '\0';
324
325     char key[32];
326     char value[32];
327
328     key[31] = '\0';
329     value[31] = '\0';
330
331     int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
332     if ( n == 2 )
333     {
334       DBG("Read header : %s: %s\n", key, value);
335       if( !strcmp(key, "Content-Length") )
336       {
337         sscanf(value, "%d", &recvContentLength);
338         pDataIn->setDataLen(recvContentLength);
339       }
340       else if( !strcmp(key, "Transfer-Encoding") )
341       {
342         if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") )
343         {
344           recvChunked = true;
345           pDataIn->setIsChunked(true);
346         }
347       }
348       else if( !strcmp(key, "Content-Type") )
349       {
350         pDataIn->setDataType(value);
351       }
352
353       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
354       trfLen -= (crlfPos + 2);
355
356     }
357     else
358     {
359       ERR("Could not parse header");
360       PRTCL_ERR();
361     }
362
363   }
364
365   //Receive data
366   DBG("Receiving data");
367   while(true)
368   {
369     size_t readLen = 0;
370
371     if( recvChunked )
372     {
373       //Read chunk header
374       bool foundCrlf;
375       do
376       {
377         foundCrlf = false;
378         crlfPos=0;
379         buf[trfLen]=0;
380         if(trfLen >= 2)
381         {
382           for(; crlfPos < trfLen - 2; crlfPos++)
383           {
384             if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' )
385             {
386               foundCrlf = true;
387               break;
388             }
389           }
390         }
391         if(!foundCrlf) //Try to read more
392         {
393           if( trfLen < CHUNK_SIZE )
394           {
395             size_t newTrfLen;
396             ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
397             trfLen += newTrfLen;
398             CHECK_CONN_ERR(ret);
399             continue;
400           }
401           else
402           {
403             PRTCL_ERR();
404           }
405         }
406       } while(!foundCrlf);
407       buf[crlfPos] = '\0';
408       int n = sscanf(buf, "%x", &readLen);
409       if(n!=1)
410       {
411         ERR("Could not read chunk length");
412         PRTCL_ERR();
413       }
414
415       memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
416       trfLen -= (crlfPos + 2);
417
418       if( readLen == 0 )
419       {
420         //Last chunk
421         break;
422       }
423     }
424     else
425     {
426       readLen = recvContentLength;
427     }
428
429     DBG("Retrieving %d bytes", readLen);
430
431     do
432     {
433       pDataIn->write(buf, MIN(trfLen, readLen));
434       if( trfLen > readLen )
435       {
436         memmove(buf, &buf[readLen], trfLen - readLen);
437         trfLen -= readLen;
438         readLen = 0;
439       }
440       else
441       {
442         readLen -= trfLen;
443       }
444
445       if(readLen)
446       {
447         ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
448         CHECK_CONN_ERR(ret);
449       }
450     } while(readLen);
451
452     if( recvChunked )
453     {
454       if(trfLen < 2)
455       {
456         size_t newTrfLen;
457         //Read missing chars to find end of chunk
458         ret = recv(buf + trfLen, 2 - trfLen, CHUNK_SIZE - trfLen - 1, &newTrfLen);
459         CHECK_CONN_ERR(ret);
460         trfLen += newTrfLen;
461       }
462       if( (buf[0] != '\r') || (buf[1] != '\n') )
463       {
464         ERR("Format error");
465         PRTCL_ERR();
466       }
467       memmove(buf, &buf[2], trfLen - 2);
468       trfLen -= 2;
469     }
470     else
471     {
472       break;
473     }
474
475   }
476
477   m_sock.close();
478   DBG("Completed HTTP transaction");
479
480   return HTTP_OK;
481 }
482
483 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
484 {
485   DBG("Trying to read between %d and %d bytes", minLen, maxLen);
486   size_t readLen = 0;
487
488   if(!m_sock.is_connected())
489   {
490     WARN("Connection was closed by server");
491     return HTTP_CLOSED; //Connection was closed by server
492   }
493
494   int ret;
495   while(readLen < maxLen)
496   {
497     if(readLen < minLen)
498     {
499       DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
500       m_sock.set_blocking(false, m_timeout);
501       ret = m_sock.receive_all(buf + readLen, minLen - readLen);
502     }
503     else
504     {
505       DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
506       m_sock.set_blocking(false, 0);
507       ret = m_sock.receive(buf + readLen, maxLen - readLen);
508     }
509
510     if( ret > 0)
511     {
512       readLen += ret;
513     }
514     else if( ret == 0 )
515     {
516       break;
517     }
518     else
519     {
520       if(!m_sock.is_connected())
521       {
522         ERR("Connection error (recv returned %d)", ret);
523         *pReadLen = readLen;
524         return HTTP_CONN;
525       }
526       else
527       {
528         break;
529       }
530     }
531
532     if(!m_sock.is_connected())
533     {
534       break;
535     }
536   }
537   DBG("Read %d bytes", readLen);
538   *pReadLen = readLen;
539   return HTTP_OK;
540 }
541
542 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
543 {
544   if(len == 0)
545   {
546     len = strlen(buf);
547   }
548   DBG("Trying to write %d bytes", len);
549   size_t writtenLen = 0;
550
551   if(!m_sock.is_connected())
552   {
553     WARN("Connection was closed by server");
554     return HTTP_CLOSED; //Connection was closed by server
555   }
556
557   m_sock.set_blocking(false, m_timeout);
558   int ret = m_sock.send_all(buf, len);
559   if(ret > 0)
560   {
561     writtenLen += ret;
562   }
563   else if( ret == 0 )
564   {
565     WARN("Connection was closed by server");
566     return HTTP_CLOSED; //Connection was closed by server
567   }
568   else
569   {
570     ERR("Connection error (send returned %d)", ret);
571     return HTTP_CONN;
572   }
573
574   DBG("Written %d bytes", writtenLen);
575   return HTTP_OK;
576 }
577
578 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
579 {
580   char* schemePtr = (char*) url;
581   char* hostPtr = (char*) strstr(url, "://");
582   if(hostPtr == NULL)
583   {
584     WARN("Could not find host");
585     return HTTP_PARSE; //URL is invalid
586   }
587
588   if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
589   {
590     WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
591     return HTTP_PARSE;
592   }
593   memcpy(scheme, schemePtr, hostPtr - schemePtr);
594   scheme[hostPtr - schemePtr] = '\0';
595
596   hostPtr+=3;
597
598   size_t hostLen = 0;
599
600   char* portPtr = strchr(hostPtr, ':');
601   if( portPtr != NULL )
602   {
603     hostLen = portPtr - hostPtr;
604     portPtr++;
605     if( sscanf(portPtr, "%hu", port) != 1)
606     {
607       WARN("Could not find port");
608       return HTTP_PARSE;
609     }
610   }
611   else
612   {
613     *port=0;
614   }
615   char* pathPtr = strchr(hostPtr, '/');
616   if( hostLen == 0 )
617   {
618     hostLen = pathPtr - hostPtr;
619   }
620
621   if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
622   {
623     WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
624     return HTTP_PARSE;
625   }
626   memcpy(host, hostPtr, hostLen);
627   host[hostLen] = '\0';
628
629   size_t pathLen;
630   char* fragmentPtr = strchr(hostPtr, '#');
631   if(fragmentPtr != NULL)
632   {
633     pathLen = fragmentPtr - pathPtr;
634   }
635   else
636   {
637     pathLen = strlen(pathPtr);
638   }
639
640   if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
641   {
642     WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
643     return HTTP_PARSE;
644   }
645   memcpy(path, pathPtr, pathLen);
646   path[pathLen] = '\0';
647
648   return HTTP_OK;
649 }