fix the problem http request with '\0' and fixx query string in url

Fork of mbed-http by sandbox

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers https_request.h Source File

https_request.h

00001 /*
00002  * PackageLicenseDeclared: Apache-2.0
00003  * Copyright (c) 2017 ARM Limited
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #ifndef _MBED_HTTPS_REQUEST_H_
00019 #define _MBED_HTTPS_REQUEST_H_
00020 
00021 /* Change to a number between 1 and 4 to debug the TLS connection */
00022 #define DEBUG_LEVEL 0
00023 
00024 #include <string>
00025 #include <vector>
00026 #include <map>
00027 #include "http_parser.h"
00028 #include "http_response.h"
00029 #include "http_request_builder.h"
00030 #include "http_response_parser.h"
00031 #include "http_parsed_url.h"
00032 
00033 #include "mbedtls/platform.h"
00034 #include "mbedtls/ssl.h"
00035 #include "mbedtls/entropy.h"
00036 #include "mbedtls/ctr_drbg.h"
00037 #include "mbedtls/error.h"
00038 
00039 #if DEBUG_LEVEL > 0
00040 #include "mbedtls/debug.h"
00041 #endif
00042 
00043 /**
00044  * \brief HttpsRequest implements the logic for interacting with HTTPS servers.
00045  */
00046 class HttpsRequest {
00047 public:
00048     /**
00049      * HttpsRequest Constructor
00050      * Initializes the TCP socket, sets up event handlers and flags.
00051      *
00052      * @param[in] net_iface The network interface
00053      * @param[in] ssl_ca_pem String containing the trusted CAs
00054      * @param[in] method HTTP method to use
00055      * @param[in] url URL to the resource
00056      * @param[in] body_callback Callback on which to retrieve chunks of the response body.
00057                                 If not set, the complete body will be allocated on the HttpResponse object,
00058                                 which might use lots of memory.
00059      */
00060     HttpsRequest(NetworkInterface* net_iface,
00061                  const char* ssl_ca_pem,
00062                  http_method method,
00063                  const char* url,
00064                  Callback<void(const char *at, size_t length)> body_callback = 0)
00065     {
00066         _parsed_url = new ParsedUrl(url);
00067         _body_callback = body_callback;
00068         _tcpsocket = new TCPSocket(net_iface);
00069         _request_builder = new HttpRequestBuilder(method, _parsed_url);
00070         _response = NULL;
00071         _debug = false;
00072         _ssl_ca_pem = ssl_ca_pem;
00073 
00074         DRBG_PERS = "mbed TLS helloword client";
00075 
00076         mbedtls_entropy_init(&_entropy);
00077         mbedtls_ctr_drbg_init(&_ctr_drbg);
00078         mbedtls_x509_crt_init(&_cacert);
00079         mbedtls_ssl_init(&_ssl);
00080         mbedtls_ssl_config_init(&_ssl_conf);
00081     }
00082 
00083     /**
00084      * HttpsRequest Destructor
00085      */
00086     ~HttpsRequest() {
00087         mbedtls_entropy_free(&_entropy);
00088         mbedtls_ctr_drbg_free(&_ctr_drbg);
00089         mbedtls_x509_crt_free(&_cacert);
00090         mbedtls_ssl_free(&_ssl);
00091         mbedtls_ssl_config_free(&_ssl_conf);
00092 
00093         if (_request_builder) {
00094             delete _request_builder;
00095         }
00096 
00097         if (_tcpsocket) {
00098             delete _tcpsocket;
00099         }
00100 
00101         if (_parsed_url) {
00102             delete _parsed_url;
00103         }
00104 
00105         if (_response) {
00106             delete _response;
00107         }
00108 
00109         // @todo: free DRBG_PERS ?
00110     }
00111 
00112     /**
00113      * Execute the HTTPS request.
00114      *
00115      * @param[in] body Pointer to the request body
00116      * @param[in] body_size Size of the request body
00117      * @return An HttpResponse pointer on success, or NULL on failure.
00118      *         See get_error() for the error code.
00119      */
00120     HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
00121         /* Initialize the flags */
00122         /*
00123          * Initialize TLS-related stuf.
00124          */
00125         int ret;
00126         if ((ret = mbedtls_ctr_drbg_seed(&_ctr_drbg, mbedtls_entropy_func, &_entropy,
00127                           (const unsigned char *) DRBG_PERS,
00128                           sizeof (DRBG_PERS))) != 0) {
00129             print_mbedtls_error("mbedtls_crt_drbg_init", ret);
00130             _error = ret;
00131             return NULL;
00132         }
00133 
00134         if ((ret = mbedtls_x509_crt_parse(&_cacert, (const unsigned char *)_ssl_ca_pem,
00135                            strlen(_ssl_ca_pem) + 1)) != 0) {
00136             print_mbedtls_error("mbedtls_x509_crt_parse", ret);
00137             _error = ret;
00138             return NULL;
00139         }
00140 
00141         if ((ret = mbedtls_ssl_config_defaults(&_ssl_conf,
00142                         MBEDTLS_SSL_IS_CLIENT,
00143                         MBEDTLS_SSL_TRANSPORT_STREAM,
00144                         MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
00145             print_mbedtls_error("mbedtls_ssl_config_defaults", ret);
00146             _error = ret;
00147             return NULL;
00148         }
00149 
00150         mbedtls_ssl_conf_ca_chain(&_ssl_conf, &_cacert, NULL);
00151         mbedtls_ssl_conf_rng(&_ssl_conf, mbedtls_ctr_drbg_random, &_ctr_drbg);
00152 
00153         /* It is possible to disable authentication by passing
00154          * MBEDTLS_SSL_VERIFY_NONE in the call to mbedtls_ssl_conf_authmode()
00155          */
00156         mbedtls_ssl_conf_authmode(&_ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
00157 
00158 #if DEBUG_LEVEL > 0
00159         mbedtls_ssl_conf_verify(&_ssl_conf, my_verify, NULL);
00160         mbedtls_ssl_conf_dbg(&_ssl_conf, my_debug, NULL);
00161         mbedtls_debug_set_threshold(DEBUG_LEVEL);
00162 #endif
00163 
00164         if ((ret = mbedtls_ssl_setup(&_ssl, &_ssl_conf)) != 0) {
00165             print_mbedtls_error("mbedtls_ssl_setup", ret);
00166             _error = ret;
00167             return NULL;
00168         }
00169 
00170         mbedtls_ssl_set_hostname(&_ssl, _parsed_url->host());
00171 
00172         mbedtls_ssl_set_bio(&_ssl, static_cast<void *>(_tcpsocket),
00173                                    ssl_send, ssl_recv, NULL );
00174 
00175         /* Connect to the server */
00176         if (_debug) mbedtls_printf("Connecting to %s:%d\r\n", _parsed_url->host(), _parsed_url->port());
00177         ret = _tcpsocket->connect(_parsed_url->host(), _parsed_url->port());
00178         if (ret != NSAPI_ERROR_OK) {
00179             if (_debug) mbedtls_printf("Failed to connect\r\n");
00180             onError(_tcpsocket, -1);
00181             return NULL;
00182         }
00183 
00184        /* Start the handshake, the rest will be done in onReceive() */
00185         if (_debug) mbedtls_printf("Starting the TLS handshake...\r\n");
00186         ret = mbedtls_ssl_handshake(&_ssl);
00187         if (ret < 0) {
00188             if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
00189                 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
00190                 print_mbedtls_error("mbedtls_ssl_handshake", ret);
00191                 onError(_tcpsocket, -1);
00192             }
00193             else {
00194                 _error = ret;
00195             }
00196             return NULL;
00197         }
00198 
00199         size_t request_size = 0;
00200         char* request = _request_builder->build(body, body_size, request_size);
00201 
00202         ret = mbedtls_ssl_write(&_ssl, (const unsigned char *) request, request_size);
00203 
00204         free(request);
00205 
00206         if (ret < 0) {
00207             if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
00208                 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
00209                 print_mbedtls_error("mbedtls_ssl_write", ret);
00210                 onError(_tcpsocket, -1 );
00211             }
00212             else {
00213                 _error = ret;
00214             }
00215             return NULL;
00216         }
00217 
00218         /* It also means the handshake is done, time to print info */
00219         if (_debug) mbedtls_printf("TLS connection to %s:%d established\r\n", _parsed_url->host(), _parsed_url->port());
00220 
00221         const uint32_t buf_size = 1024;
00222         char *buf = new char[buf_size];
00223         mbedtls_x509_crt_info(buf, buf_size, "\r    ",
00224                         mbedtls_ssl_get_peer_cert(&_ssl));
00225         if (_debug) mbedtls_printf("Server certificate:\r\n%s\r", buf);
00226 
00227         uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl);
00228         if( flags != 0 )
00229         {
00230             mbedtls_x509_crt_verify_info(buf, buf_size, "\r  ! ", flags);
00231             if (_debug) mbedtls_printf("Certificate verification failed:\r\n%s\r\r\n", buf);
00232         }
00233         else {
00234             if (_debug) mbedtls_printf("Certificate verification passed\r\n\r\n");
00235         }
00236 
00237         // Create a response object
00238         _response = new HttpResponse();
00239         // And a response parser
00240         HttpResponseParser parser(_response, _body_callback);
00241 
00242         // Set up a receive buffer (on the heap)
00243         uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
00244 
00245         /* Read data out of the socket */
00246         while ((ret = mbedtls_ssl_read(&_ssl, (unsigned char *) recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
00247             // Don't know if this is actually needed, but OK
00248             size_t _bpos = static_cast<size_t>(ret);
00249             recv_buffer[_bpos] = 0;
00250 
00251             size_t nparsed = parser.execute((const char*)recv_buffer, _bpos);
00252             if (nparsed != _bpos) {
00253                 print_mbedtls_error("parser_error", nparsed);
00254                 // parser error...
00255                 _error = -2101;
00256                 free(recv_buffer);
00257                 return NULL;
00258             }
00259 
00260             if (_response->is_message_complete()) {
00261                 break;
00262             }
00263         }
00264         if (ret < 0) {
00265             if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
00266                 print_mbedtls_error("mbedtls_ssl_read", ret);
00267                 onError(_tcpsocket, -1 );
00268             }
00269             else {
00270                 _error = ret;
00271             }
00272             free(recv_buffer);
00273             return NULL;
00274         }
00275 
00276         parser.finish();
00277 
00278         _tcpsocket->close();
00279         free(recv_buffer);
00280 
00281         return _response;
00282     }
00283 
00284     /**
00285      * Closes the TCP socket
00286      */
00287     void close() {
00288         _tcpsocket->close();
00289     }
00290 
00291     /**
00292      * Set a header for the request.
00293      *
00294      * The 'Host' and 'Content-Length' headers are set automatically.
00295      * Setting the same header twice will overwrite the previous entry.
00296      *
00297      * @param[in] key Header key
00298      * @param[in] value Header value
00299      */
00300     void set_header(string key, string value) {
00301         _request_builder->set_header(key, value);
00302     }
00303 
00304     /**
00305      * Get the error code.
00306      *
00307      * When send() fails, this error is set.
00308      */
00309     nsapi_error_t get_error() {
00310         return _error;
00311     }
00312 
00313     /**
00314      * Set the debug flag.
00315      *
00316      * If this flag is set, debug information from mbed TLS will be logged to stdout.
00317      */
00318     void set_debug(bool debug) {
00319         _debug = debug;
00320     }
00321 
00322 protected:
00323     /**
00324      * Helper for pretty-printing mbed TLS error codes
00325      */
00326     static void print_mbedtls_error(const char *name, int err) {
00327         char buf[128];
00328         mbedtls_strerror(err, buf, sizeof (buf));
00329         mbedtls_printf("%s() failed: -0x%04x (%d): %s\r\n", name, -err, err, buf);
00330     }
00331 
00332 #if DEBUG_LEVEL > 0
00333     /**
00334      * Debug callback for mbed TLS
00335      * Just prints on the USB serial port
00336      */
00337     static void my_debug(void *ctx, int level, const char *file, int line,
00338                          const char *str)
00339     {
00340         const char *p, *basename;
00341         (void) ctx;
00342 
00343         /* Extract basename from file */
00344         for(p = basename = file; *p != '\0'; p++) {
00345             if(*p == '/' || *p == '\\') {
00346                 basename = p + 1;
00347             }
00348         }
00349 
00350         if (_debug) {
00351             mbedtls_printf("%s:%04d: |%d| %s", basename, line, level, str);
00352         }
00353     }
00354 
00355     /**
00356      * Certificate verification callback for mbed TLS
00357      * Here we only use it to display information on each cert in the chain
00358      */
00359     static int my_verify(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
00360     {
00361         const uint32_t buf_size = 1024;
00362         char *buf = new char[buf_size];
00363         (void) data;
00364 
00365         if (_debug) mbedtls_printf("\nVerifying certificate at depth %d:\n", depth);
00366         mbedtls_x509_crt_info(buf, buf_size - 1, "  ", crt);
00367         if (_debug) mbedtls_printf("%s", buf);
00368 
00369         if (*flags == 0)
00370             if (_debug) mbedtls_printf("No verification issue for this certificate\n");
00371         else
00372         {
00373             mbedtls_x509_crt_verify_info(buf, buf_size, "  ! ", *flags);
00374             if (_debug) mbedtls_printf("%s\n", buf);
00375         }
00376 
00377         delete[] buf;
00378         return 0;
00379     }
00380 #endif
00381 
00382     /**
00383      * Receive callback for mbed TLS
00384      */
00385     static int ssl_recv(void *ctx, unsigned char *buf, size_t len) {
00386         int recv = -1;
00387         TCPSocket *socket = static_cast<TCPSocket *>(ctx);
00388         recv = socket->recv(buf, len);
00389 
00390         if (NSAPI_ERROR_WOULD_BLOCK == recv) {
00391             return MBEDTLS_ERR_SSL_WANT_READ;
00392         }
00393         else if (recv < 0) {
00394             return -1;
00395         }
00396         else {
00397             return recv;
00398         }
00399    }
00400 
00401     /**
00402      * Send callback for mbed TLS
00403      */
00404     static int ssl_send(void *ctx, const unsigned char *buf, size_t len) {
00405        int size = -1;
00406         TCPSocket *socket = static_cast<TCPSocket *>(ctx);
00407         size = socket->send(buf, len);
00408 
00409         if(NSAPI_ERROR_WOULD_BLOCK == size) {
00410             return len;
00411         }
00412         else if (size < 0){
00413             return -1;
00414         }
00415         else {
00416             return size;
00417         }
00418     }
00419 
00420     void onError(TCPSocket *s, int error) {
00421         s->close();
00422         _error = error;
00423     }
00424 
00425 protected:
00426     TCPSocket* _tcpsocket;
00427 
00428     Callback<void(const char *at, size_t length)> _body_callback;
00429     ParsedUrl* _parsed_url;
00430     HttpRequestBuilder* _request_builder;
00431     HttpResponse* _response;
00432     const char *DRBG_PERS;
00433     const char *_ssl_ca_pem;
00434 
00435     nsapi_error_t _error;
00436     bool _debug;
00437 
00438     mbedtls_entropy_context _entropy;
00439     mbedtls_ctr_drbg_context _ctr_drbg;
00440     mbedtls_x509_crt _cacert;
00441     mbedtls_ssl_context _ssl;
00442     mbedtls_ssl_config _ssl_conf;
00443 };
00444 
00445 #endif // _MBED_HTTPS_REQUEST_H_