mbed-http

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