]> git.gir.st - tmk_keyboard.git/blob - tmk_core/tool/mbed/mbed-sdk/libraries/tests/net/cellular/http/common/HTTPClient/HTTPClient.cpp
Merge commit '1fe4406f374291ab2e86e95a97341fd9c475fcb8'
[tmk_keyboard.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 }
Imprint / Impressum