This library is used to make HTTP and HTTPS calls from mbed OS 5 applications.

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 #include <string>
00022 #include <vector>
00023 #include <map>
00024 #include "http_parser.h"
00025 #include "http_response.h"
00026 #include "http_request_builder.h"
00027 #include "http_request_parser.h"
00028 #include "http_parsed_url.h"
00029 #include "tls_socket.h"
00030 
00031 /**
00032  * \brief HttpsRequest implements the logic for interacting with HTTPS servers.
00033  */
00034 class HttpsRequest {
00035 public:
00036     /**
00037      * HttpsRequest Constructor
00038      * Initializes the TCP socket, sets up event handlers and flags.
00039      *
00040      * @param[in] net_iface The network interface
00041      * @param[in] ssl_ca_pem String containing the trusted CAs
00042      * @param[in] method HTTP method to use
00043      * @param[in] url URL to the resource
00044      * @param[in] body_callback Callback on which to retrieve chunks of the response body.
00045                                 If not set, the complete body will be allocated on the HttpResponse object,
00046                                 which might use lots of memory.
00047      */
00048     HttpsRequest(NetworkInterface* net_iface,
00049                  const char* ssl_ca_pem,
00050                  http_method method,
00051                  const char* url,
00052                  Callback<void(const char *at, size_t length)> body_callback = 0)
00053     {
00054         _parsed_url = new ParsedUrl(url);
00055         _body_callback = body_callback;
00056         _request_builder = new HttpRequestBuilder(method, _parsed_url);
00057         _response = NULL;
00058         _debug = false;
00059 
00060         _tlssocket = new TLSSocket(net_iface, _parsed_url->host(), _parsed_url->port(), ssl_ca_pem);
00061         _we_created_the_socket = true;
00062     }
00063 
00064     /**
00065      * HttpsRequest Constructor
00066      * Sets up event handlers and flags.
00067      *
00068      * @param[in] socket A connected TLSSocket
00069      * @param[in] method HTTP method to use
00070      * @param[in] url URL to the resource
00071      * @param[in] body_callback Callback on which to retrieve chunks of the response body.
00072                                 If not set, the complete body will be allocated on the HttpResponse object,
00073                                 which might use lots of memory.
00074      */
00075     HttpsRequest(TLSSocket* socket,
00076                  http_method method,
00077                  const char* url,
00078                  Callback<void(const char *at, size_t length)> body_callback = 0)
00079     {
00080         _parsed_url = new ParsedUrl(url);
00081         _body_callback = body_callback;
00082         _request_builder = new HttpRequestBuilder(method, _parsed_url);
00083         _response = NULL;
00084         _debug = false;
00085 
00086         _tlssocket = socket;
00087         _we_created_the_socket = false;
00088     }
00089 
00090     /**
00091      * HttpsRequest Destructor
00092      */
00093     ~HttpsRequest() {
00094         if (_request_builder) {
00095             delete _request_builder;
00096         }
00097 
00098         if (_tlssocket && _we_created_the_socket) {
00099             delete _tlssocket;
00100         }
00101 
00102         if (_parsed_url) {
00103             delete _parsed_url;
00104         }
00105 
00106         if (_response) {
00107             delete _response;
00108         }
00109     }
00110 
00111     /**
00112      * Execute the HTTPS request.
00113      *
00114      * @param[in] body Pointer to the request body
00115      * @param[in] body_size Size of the request body
00116      * @return An HttpResponse pointer on success, or NULL on failure.
00117      *         See get_error() for the error code.
00118      */
00119     HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
00120         // not tried to connect before?
00121         if (_tlssocket->error() != 0) {
00122             _error = _tlssocket->error();
00123             return NULL;
00124         }
00125 
00126         bool socket_was_open = _tlssocket->connected();
00127 
00128         if (!socket_was_open) {
00129             nsapi_error_t r = _tlssocket->connect();
00130             if (r != 0) {
00131                 _error = r;
00132                 return NULL;
00133             }
00134         }
00135 
00136         int ret;
00137 
00138         size_t request_size = 0;
00139         char* request = _request_builder->build(body, body_size, request_size);
00140 
00141         ret = mbedtls_ssl_write(_tlssocket->get_ssl_context(), (const unsigned char *) request, request_size);
00142 
00143         free(request);
00144 
00145         if (ret < 0) {
00146             if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
00147                 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
00148                 print_mbedtls_error("mbedtls_ssl_write", ret);
00149                 onError(_tlssocket->get_tcp_socket(), -1 );
00150             }
00151             else {
00152                 _error = ret;
00153             }
00154             return NULL;
00155         }
00156 
00157         // Create a response object
00158         _response = new HttpResponse();
00159         // And a response parser
00160         HttpParser parser(_response, HTTP_RESPONSE, _body_callback);
00161 
00162         // Set up a receive buffer (on the heap)
00163         uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
00164 
00165         /* Read data out of the socket */
00166         while ((ret = mbedtls_ssl_read(_tlssocket->get_ssl_context(), (unsigned char *) recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
00167             // Don't know if this is actually needed, but OK
00168             size_t _bpos = static_cast<size_t>(ret);
00169             recv_buffer[_bpos] = 0;
00170 
00171             size_t nparsed = parser.execute((const char*)recv_buffer, _bpos);
00172             if (nparsed != _bpos) {
00173                 print_mbedtls_error("parser_error", nparsed);
00174                 // parser error...
00175                 _error = -2101;
00176                 free(recv_buffer);
00177                 return NULL;
00178             }
00179 
00180             if (_response->is_message_complete()) {
00181                 break;
00182             }
00183         }
00184         if (ret < 0) {
00185             if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
00186                 print_mbedtls_error("mbedtls_ssl_read", ret);
00187                 onError(_tlssocket->get_tcp_socket(), -1 );
00188             }
00189             else {
00190                 _error = ret;
00191             }
00192             free(recv_buffer);
00193             return NULL;
00194         }
00195 
00196         parser.finish();
00197 
00198         if (!socket_was_open) {
00199             _tlssocket->get_tcp_socket()->close();
00200         }
00201 
00202         free(recv_buffer);
00203 
00204         return _response;
00205     }
00206 
00207     /**
00208      * Closes the underlying TCP socket
00209      */
00210     void close() {
00211         _tlssocket->get_tcp_socket()->close();
00212     }
00213 
00214     /**
00215      * Set a header for the request.
00216      *
00217      * The 'Host' and 'Content-Length' headers are set automatically.
00218      * Setting the same header twice will overwrite the previous entry.
00219      *
00220      * @param[in] key Header key
00221      * @param[in] value Header value
00222      */
00223     void set_header(string key, string value) {
00224         _request_builder->set_header(key, value);
00225     }
00226 
00227     /**
00228      * Get the error code.
00229      *
00230      * When send() fails, this error is set.
00231      */
00232     nsapi_error_t get_error() {
00233         return _error;
00234     }
00235 
00236     /**
00237      * Set the debug flag.
00238      *
00239      * If this flag is set, debug information from mbed TLS will be logged to stdout.
00240      */
00241     void set_debug(bool debug) {
00242         _debug = debug;
00243 
00244         _tlssocket->set_debug(debug);
00245     }
00246 
00247 
00248 protected:
00249     /**
00250      * Helper for pretty-printing mbed TLS error codes
00251      */
00252     static void print_mbedtls_error(const char *name, int err) {
00253         char buf[128];
00254         mbedtls_strerror(err, buf, sizeof (buf));
00255         mbedtls_printf("%s() failed: -0x%04x (%d): %s\r\n", name, -err, err, buf);
00256     }
00257 
00258     void onError(TCPSocket *s, int error) {
00259         s->close();
00260         _error = error;
00261     }
00262 
00263 protected:
00264     TLSSocket* _tlssocket;
00265     bool _we_created_the_socket;
00266 
00267     Callback<void(const char *at, size_t length)> _body_callback;
00268     ParsedUrl* _parsed_url;
00269     HttpRequestBuilder* _request_builder;
00270     HttpResponse* _response;
00271 
00272     nsapi_error_t _error;
00273     bool _debug;
00274 
00275 };
00276 
00277 #endif // _MBED_HTTPS_REQUEST_H_