HTTP and HTTPS library for Mbed OS 5

Dependents:   MQTTGateway2 MQTTGatewayK64 http-example-wnc GuardRoom ... more

For the example program, see: sandbox/http-example.

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

HTTP Request API

NetworkInterface* network = /* obtain a NetworkInterface object */

const char body[] = "{\"hello\":\"world\"}";

HttpRequest* request = new HttpRequest(network, HTTP_POST, "http://httpbin.org/post");
request->set_header("Content-Type", "application/json");
HttpResponse* response = request->send(body, strlen(body));
// if response is NULL, check response->get_error()

printf("status is %d - %s\n", response->get_status_code(), response->get_status_message());
printf("body is:\n%s\n", response->get_body_as_string().c_str());

delete request; // also clears out the response

HTTPS Request API

// pass in the root certificates that you trust, there is no central CA registry in Mbed OS
const char SSL_CA_PEM[] = "-----BEGIN CERTIFICATE-----\n"
    /* rest of the CA root certificates */;

NetworkInterface* network = /* obtain a NetworkInterface object */

const char body[] = "{\"hello\":\"world\"}";

HttpsRequest* request = new HttpsRequest(network, SSL_CA_PEM, HTTP_GET "https://httpbin.org/status/418");
HttpResponse* response = request->send();
// if response is NULL, check response->get_error()

printf("status is %d - %s\n", response->get_status_code(), response->get_status_message());
printf("body is:\n%s\n", response->get_body().c_str());

delete request;

Note: You can get the root CA for a domain easily from Firefox. Click on the green padlock, click More information > Security > View certificate > Details. Select the top entry in the 'Certificate Hierarchy' and click Export.... This gives you a PEM file. Add the content of the PEM file to your root CA list (here's an image).

Mbed TLS Entropy configuration

If your target does not have a built-in TRNG, or other entropy sources, add the following macros to your mbed_app.json file to disable entropy:

{
    "macros": [
        "MBEDTLS_TEST_NULL_ENTROPY",
        "MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES"
    ]
}

Note that this is not secure, and you should not deploy this device into production with this configuration.

Memory usage

Small requests where the body of the response is cached by the library (like the one found in main-http.cpp), require 4K of RAM. When the request is finished they require 1.5K of RAM, depending on the size of the response. This applies both to HTTP and HTTPS. If you need to handle requests that return a large response body, see 'Dealing with large body'.

HTTPS requires additional memory: on FRDM-K64F about 50K of heap space (at its peak). This means that you cannot use HTTPS on devices with less than 128K of memory, asyou also need to reserve memory for the stack and network interface.

Dealing with large response body

By default the library will store the full request body on the heap. This works well for small responses, but you'll run out of memory when receiving a large response body. To mitigate this you can pass in a callback as the last argument to the request constructor. This callback will be called whenever a chunk of the body is received. You can set the request chunk size in the HTTP_RECEIVE_BUFFER_SIZE macro (see mbed_lib.json for the definition) although it also depends on the buffer size ofthe underlying network connection.

void body_callback(const char* data, uint32_t data_len) {
    // do something with the data
}

HttpRequest* req = new HttpRequest(network, HTTP_GET, "http://pathtolargefile.com", &body_callback);
req->send(NULL, 0);

Dealing with a large request body

If you cannot load the full request into memory, you can pass a callback into the send function. Through this callback you can feed in chunks of the request body. This is very useful if you want to send files from a file system.

const void * get_chunk(uint32_t* out_size) {
    // set the value of out_size (via *out_size = 10) to the size of the buffer
    // return the buffer

    // if you don't have any more data, set *out_size to 0
}

HttpRequest* req = new HttpRequest(network, HTTP_POST, "http://my_api.com/upload");
req->send(callback(&get_chunk));

Socket re-use

By default the library opens a new socket per request. This is wasteful, especially when dealing with TLS requests. You can re-use sockets like this:

HTTP

TCPSocket* socket = new TCPSocket();

nsapi_error_t open_result = socket->open(network);
// check open_result

nsapi_error_t connect_result = socket->connect("httpbin.org", 80);
// check connect_result

// Pass in `socket`, instead of `network` as first argument
HttpRequest* req = new HttpRequest(socket, HTTP_GET, "http://httpbin.org/status/418");

HTTPS

TLSSocket* socket = new TLSSocket();

nsapi_error_t r;
// make sure to check the return values for the calls below (should return NSAPI_ERROR_OK)
r = socket->open(network);
r = socket->set_root_ca_cert(SSL_CA_PEM);
r = socket->connect("httpbin.org", 443);

// Pass in `socket`, instead of `network` as first argument, and omit the `SSL_CA_PEM` argument
HttpsRequest* get_req = new HttpsRequest(socket, HTTP_GET, "https://httpbin.org/status/418");

Request logging

To make debugging easier you can log the raw request body that goes over the line. This also works with chunked encoding.

uint8_t *request_buffer = (uint8_t*)calloc(2048, 1);
req->set_request_log_buffer(request_buffer, 2048);

// after the request is done:
printf("\n----- Request buffer -----\n");
for (size_t ix = 0; ix < req->get_request_log_buffer_length(); ix++) {
    printf("%02x ", request_buffer[ix]);
}
printf("\n");

Integration tests

Integration tests are located in the TESTS folder and are ran through Greentea. Instructions on how to run the tests are in http-example.

Mbed OS 5.10 or lower

If you want to use this library on Mbed OS 5.10 or lower, you need to add the TLSSocket library to your project. This library is included in Mbed OS 5.11 and up.

Tested on

  • K64F with Ethernet.
  • NUCLEO_F411RE with ESP8266.
  • ODIN-W2 with WiFi.
  • K64F with Atmel 6LoWPAN shield.
  • DISCO-L475VG-IOT01A with WiFi.
  • Mbed Simulator.
Committer:
Jan Jongboom
Date:
Tue Mar 28 13:33:14 2017 +0200
Revision:
10:b017c7d2cf23
Parent:
7:2e3eedb9ca5c
Child:
11:96e4dcb9c0c2
Don't rely on strlen for request length, as it prevents sending \0 over the line. Patch via https://developer.mbed.org/users/ihere1/code/mbed-http/rev/ec9b75c349aa

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jan Jongboom 0:910f5949759f 1 /*
Jan Jongboom 0:910f5949759f 2 * PackageLicenseDeclared: Apache-2.0
Jan Jongboom 0:910f5949759f 3 * Copyright (c) 2017 ARM Limited
Jan Jongboom 0:910f5949759f 4 *
Jan Jongboom 0:910f5949759f 5 * Licensed under the Apache License, Version 2.0 (the "License");
Jan Jongboom 0:910f5949759f 6 * you may not use this file except in compliance with the License.
Jan Jongboom 0:910f5949759f 7 * You may obtain a copy of the License at
Jan Jongboom 0:910f5949759f 8 *
Jan Jongboom 0:910f5949759f 9 * http://www.apache.org/licenses/LICENSE-2.0
Jan Jongboom 0:910f5949759f 10 *
Jan Jongboom 0:910f5949759f 11 * Unless required by applicable law or agreed to in writing, software
Jan Jongboom 0:910f5949759f 12 * distributed under the License is distributed on an "AS IS" BASIS,
Jan Jongboom 0:910f5949759f 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Jan Jongboom 0:910f5949759f 14 * See the License for the specific language governing permissions and
Jan Jongboom 0:910f5949759f 15 * limitations under the License.
Jan Jongboom 0:910f5949759f 16 */
Jan Jongboom 0:910f5949759f 17
Jan Jongboom 0:910f5949759f 18 #ifndef _MBED_HTTPS_REQUEST_H_
Jan Jongboom 0:910f5949759f 19 #define _MBED_HTTPS_REQUEST_H_
Jan Jongboom 0:910f5949759f 20
Jan Jongboom 0:910f5949759f 21 /* Change to a number between 1 and 4 to debug the TLS connection */
Jan Jongboom 0:910f5949759f 22 #define DEBUG_LEVEL 0
Jan Jongboom 0:910f5949759f 23
Jan Jongboom 0:910f5949759f 24 #include <string>
Jan Jongboom 0:910f5949759f 25 #include <vector>
Jan Jongboom 0:910f5949759f 26 #include <map>
Jan Jongboom 0:910f5949759f 27 #include "http_parser.h"
Jan Jongboom 0:910f5949759f 28 #include "http_response.h"
Jan Jongboom 0:910f5949759f 29 #include "http_request_builder.h"
Jan Jongboom 0:910f5949759f 30 #include "http_response_parser.h"
Jan Jongboom 0:910f5949759f 31 #include "http_parsed_url.h"
Jan Jongboom 0:910f5949759f 32
Jan Jongboom 0:910f5949759f 33 #include "mbedtls/platform.h"
Jan Jongboom 0:910f5949759f 34 #include "mbedtls/ssl.h"
Jan Jongboom 0:910f5949759f 35 #include "mbedtls/entropy.h"
Jan Jongboom 0:910f5949759f 36 #include "mbedtls/ctr_drbg.h"
Jan Jongboom 0:910f5949759f 37 #include "mbedtls/error.h"
Jan Jongboom 0:910f5949759f 38
Jan Jongboom 0:910f5949759f 39 #if DEBUG_LEVEL > 0
Jan Jongboom 0:910f5949759f 40 #include "mbedtls/debug.h"
Jan Jongboom 0:910f5949759f 41 #endif
Jan Jongboom 0:910f5949759f 42
Jan Jongboom 0:910f5949759f 43 /**
Jan Jongboom 0:910f5949759f 44 * \brief HttpsRequest implements the logic for interacting with HTTPS servers.
Jan Jongboom 0:910f5949759f 45 */
Jan Jongboom 0:910f5949759f 46 class HttpsRequest {
Jan Jongboom 0:910f5949759f 47 public:
Jan Jongboom 0:910f5949759f 48 /**
Jan Jongboom 0:910f5949759f 49 * HttpsRequest Constructor
Jan Jongboom 0:910f5949759f 50 * Initializes the TCP socket, sets up event handlers and flags.
Jan Jongboom 0:910f5949759f 51 *
Jan Jongboom 0:910f5949759f 52 * @param[in] net_iface The network interface
Jan Jongboom 0:910f5949759f 53 * @param[in] ssl_ca_pem String containing the trusted CAs
Jan Jongboom 0:910f5949759f 54 * @param[in] method HTTP method to use
Jan Jongboom 0:910f5949759f 55 * @param[in] url URL to the resource
Jan Jongboom 0:910f5949759f 56 * @param[in] body_callback Callback on which to retrieve chunks of the response body.
Jan Jongboom 0:910f5949759f 57 If not set, the complete body will be allocated on the HttpResponse object,
Jan Jongboom 0:910f5949759f 58 which might use lots of memory.
Jan Jongboom 0:910f5949759f 59 */
Jan Jongboom 0:910f5949759f 60 HttpsRequest(NetworkInterface* net_iface,
Jan Jongboom 0:910f5949759f 61 const char* ssl_ca_pem,
Jan Jongboom 0:910f5949759f 62 http_method method,
Jan Jongboom 0:910f5949759f 63 const char* url,
Jan Jongboom 0:910f5949759f 64 Callback<void(const char *at, size_t length)> body_callback = 0)
Jan Jongboom 0:910f5949759f 65 {
Jan Jongboom 0:910f5949759f 66 _parsed_url = new ParsedUrl(url);
Jan Jongboom 0:910f5949759f 67 _body_callback = body_callback;
Jan Jongboom 0:910f5949759f 68 _tcpsocket = new TCPSocket(net_iface);
Jan Jongboom 0:910f5949759f 69 _request_builder = new HttpRequestBuilder(method, _parsed_url);
Jan Jongboom 0:910f5949759f 70 _response = NULL;
Jan Jongboom 0:910f5949759f 71 _debug = false;
Jan Jongboom 0:910f5949759f 72 _ssl_ca_pem = ssl_ca_pem;
Jan Jongboom 0:910f5949759f 73
Jan Jongboom 0:910f5949759f 74 DRBG_PERS = "mbed TLS helloword client";
Jan Jongboom 0:910f5949759f 75
Jan Jongboom 0:910f5949759f 76 mbedtls_entropy_init(&_entropy);
Jan Jongboom 0:910f5949759f 77 mbedtls_ctr_drbg_init(&_ctr_drbg);
Jan Jongboom 0:910f5949759f 78 mbedtls_x509_crt_init(&_cacert);
Jan Jongboom 0:910f5949759f 79 mbedtls_ssl_init(&_ssl);
Jan Jongboom 0:910f5949759f 80 mbedtls_ssl_config_init(&_ssl_conf);
Jan Jongboom 0:910f5949759f 81 }
Jan Jongboom 0:910f5949759f 82
Jan Jongboom 0:910f5949759f 83 /**
Jan Jongboom 0:910f5949759f 84 * HttpsRequest Destructor
Jan Jongboom 0:910f5949759f 85 */
Jan Jongboom 0:910f5949759f 86 ~HttpsRequest() {
Jan Jongboom 0:910f5949759f 87 mbedtls_entropy_free(&_entropy);
Jan Jongboom 0:910f5949759f 88 mbedtls_ctr_drbg_free(&_ctr_drbg);
Jan Jongboom 0:910f5949759f 89 mbedtls_x509_crt_free(&_cacert);
Jan Jongboom 0:910f5949759f 90 mbedtls_ssl_free(&_ssl);
Jan Jongboom 0:910f5949759f 91 mbedtls_ssl_config_free(&_ssl_conf);
Jan Jongboom 0:910f5949759f 92
Jan Jongboom 0:910f5949759f 93 if (_request_builder) {
Jan Jongboom 0:910f5949759f 94 delete _request_builder;
Jan Jongboom 0:910f5949759f 95 }
Jan Jongboom 0:910f5949759f 96
Jan Jongboom 0:910f5949759f 97 if (_tcpsocket) {
Jan Jongboom 0:910f5949759f 98 delete _tcpsocket;
Jan Jongboom 0:910f5949759f 99 }
Jan Jongboom 0:910f5949759f 100
Jan Jongboom 0:910f5949759f 101 if (_parsed_url) {
Jan Jongboom 0:910f5949759f 102 delete _parsed_url;
Jan Jongboom 0:910f5949759f 103 }
Jan Jongboom 0:910f5949759f 104
Jan Jongboom 0:910f5949759f 105 if (_response) {
Jan Jongboom 0:910f5949759f 106 delete _response;
Jan Jongboom 0:910f5949759f 107 }
Jan Jongboom 0:910f5949759f 108
Jan Jongboom 0:910f5949759f 109 // @todo: free DRBG_PERS ?
Jan Jongboom 0:910f5949759f 110 }
Jan Jongboom 0:910f5949759f 111
Jan Jongboom 0:910f5949759f 112 /**
Jan Jongboom 0:910f5949759f 113 * Execute the HTTPS request.
Jan Jongboom 0:910f5949759f 114 *
Jan Jongboom 0:910f5949759f 115 * @param[in] body Pointer to the request body
Jan Jongboom 0:910f5949759f 116 * @param[in] body_size Size of the request body
Jan Jongboom 0:910f5949759f 117 * @return An HttpResponse pointer on success, or NULL on failure.
Jan Jongboom 0:910f5949759f 118 * See get_error() for the error code.
Jan Jongboom 0:910f5949759f 119 */
Jan Jongboom 0:910f5949759f 120 HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
Jan Jongboom 0:910f5949759f 121 /* Initialize the flags */
Jan Jongboom 0:910f5949759f 122 /*
Jan Jongboom 0:910f5949759f 123 * Initialize TLS-related stuf.
Jan Jongboom 0:910f5949759f 124 */
Jan Jongboom 0:910f5949759f 125 int ret;
Jan Jongboom 0:910f5949759f 126 if ((ret = mbedtls_ctr_drbg_seed(&_ctr_drbg, mbedtls_entropy_func, &_entropy,
Jan Jongboom 0:910f5949759f 127 (const unsigned char *) DRBG_PERS,
Jan Jongboom 0:910f5949759f 128 sizeof (DRBG_PERS))) != 0) {
Jan Jongboom 0:910f5949759f 129 print_mbedtls_error("mbedtls_crt_drbg_init", ret);
Jan Jongboom 0:910f5949759f 130 _error = ret;
Jan Jongboom 0:910f5949759f 131 return NULL;
Jan Jongboom 0:910f5949759f 132 }
Jan Jongboom 0:910f5949759f 133
Jan Jongboom 0:910f5949759f 134 if ((ret = mbedtls_x509_crt_parse(&_cacert, (const unsigned char *)_ssl_ca_pem,
Jan Jongboom 0:910f5949759f 135 strlen(_ssl_ca_pem) + 1)) != 0) {
Jan Jongboom 0:910f5949759f 136 print_mbedtls_error("mbedtls_x509_crt_parse", ret);
Jan Jongboom 0:910f5949759f 137 _error = ret;
Jan Jongboom 0:910f5949759f 138 return NULL;
Jan Jongboom 0:910f5949759f 139 }
Jan Jongboom 0:910f5949759f 140
Jan Jongboom 0:910f5949759f 141 if ((ret = mbedtls_ssl_config_defaults(&_ssl_conf,
Jan Jongboom 0:910f5949759f 142 MBEDTLS_SSL_IS_CLIENT,
Jan Jongboom 0:910f5949759f 143 MBEDTLS_SSL_TRANSPORT_STREAM,
Jan Jongboom 0:910f5949759f 144 MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
Jan Jongboom 0:910f5949759f 145 print_mbedtls_error("mbedtls_ssl_config_defaults", ret);
Jan Jongboom 0:910f5949759f 146 _error = ret;
Jan Jongboom 0:910f5949759f 147 return NULL;
Jan Jongboom 0:910f5949759f 148 }
Jan Jongboom 0:910f5949759f 149
Jan Jongboom 0:910f5949759f 150 mbedtls_ssl_conf_ca_chain(&_ssl_conf, &_cacert, NULL);
Jan Jongboom 0:910f5949759f 151 mbedtls_ssl_conf_rng(&_ssl_conf, mbedtls_ctr_drbg_random, &_ctr_drbg);
Jan Jongboom 0:910f5949759f 152
Jan Jongboom 0:910f5949759f 153 /* It is possible to disable authentication by passing
Jan Jongboom 0:910f5949759f 154 * MBEDTLS_SSL_VERIFY_NONE in the call to mbedtls_ssl_conf_authmode()
Jan Jongboom 0:910f5949759f 155 */
Jan Jongboom 0:910f5949759f 156 mbedtls_ssl_conf_authmode(&_ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
Jan Jongboom 0:910f5949759f 157
Jan Jongboom 0:910f5949759f 158 #if DEBUG_LEVEL > 0
Jan Jongboom 0:910f5949759f 159 mbedtls_ssl_conf_verify(&_ssl_conf, my_verify, NULL);
Jan Jongboom 0:910f5949759f 160 mbedtls_ssl_conf_dbg(&_ssl_conf, my_debug, NULL);
Jan Jongboom 0:910f5949759f 161 mbedtls_debug_set_threshold(DEBUG_LEVEL);
Jan Jongboom 0:910f5949759f 162 #endif
Jan Jongboom 0:910f5949759f 163
Jan Jongboom 0:910f5949759f 164 if ((ret = mbedtls_ssl_setup(&_ssl, &_ssl_conf)) != 0) {
Jan Jongboom 0:910f5949759f 165 print_mbedtls_error("mbedtls_ssl_setup", ret);
Jan Jongboom 0:910f5949759f 166 _error = ret;
Jan Jongboom 0:910f5949759f 167 return NULL;
Jan Jongboom 0:910f5949759f 168 }
Jan Jongboom 0:910f5949759f 169
Jan Jongboom 0:910f5949759f 170 mbedtls_ssl_set_hostname(&_ssl, _parsed_url->host());
Jan Jongboom 0:910f5949759f 171
Jan Jongboom 0:910f5949759f 172 mbedtls_ssl_set_bio(&_ssl, static_cast<void *>(_tcpsocket),
Jan Jongboom 0:910f5949759f 173 ssl_send, ssl_recv, NULL );
Jan Jongboom 0:910f5949759f 174
Jan Jongboom 0:910f5949759f 175 /* Connect to the server */
Jan Jongboom 0:910f5949759f 176 if (_debug) mbedtls_printf("Connecting to %s:%d\r\n", _parsed_url->host(), _parsed_url->port());
Jan Jongboom 0:910f5949759f 177 ret = _tcpsocket->connect(_parsed_url->host(), _parsed_url->port());
Jan Jongboom 0:910f5949759f 178 if (ret != NSAPI_ERROR_OK) {
Jan Jongboom 0:910f5949759f 179 if (_debug) mbedtls_printf("Failed to connect\r\n");
Jan Jongboom 0:910f5949759f 180 onError(_tcpsocket, -1);
Jan Jongboom 0:910f5949759f 181 return NULL;
Jan Jongboom 0:910f5949759f 182 }
Jan Jongboom 0:910f5949759f 183
Jan Jongboom 0:910f5949759f 184 /* Start the handshake, the rest will be done in onReceive() */
Jan Jongboom 0:910f5949759f 185 if (_debug) mbedtls_printf("Starting the TLS handshake...\r\n");
Jan Jongboom 0:910f5949759f 186 ret = mbedtls_ssl_handshake(&_ssl);
Jan Jongboom 0:910f5949759f 187 if (ret < 0) {
Jan Jongboom 0:910f5949759f 188 if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
Jan Jongboom 0:910f5949759f 189 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
Jan Jongboom 0:910f5949759f 190 print_mbedtls_error("mbedtls_ssl_handshake", ret);
Jan Jongboom 0:910f5949759f 191 onError(_tcpsocket, -1);
Jan Jongboom 0:910f5949759f 192 }
Jan Jongboom 0:910f5949759f 193 else {
Jan Jongboom 0:910f5949759f 194 _error = ret;
Jan Jongboom 0:910f5949759f 195 }
Jan Jongboom 0:910f5949759f 196 return NULL;
Jan Jongboom 0:910f5949759f 197 }
Jan Jongboom 0:910f5949759f 198
Jan Jongboom 10:b017c7d2cf23 199 size_t request_size = 0;
Jan Jongboom 10:b017c7d2cf23 200 char* request = _request_builder->build(body, body_size, request_size);
Jan Jongboom 0:910f5949759f 201
Jan Jongboom 0:910f5949759f 202 ret = mbedtls_ssl_write(&_ssl, (const unsigned char *) request, request_size);
Jan Jongboom 0:910f5949759f 203
Jan Jongboom 0:910f5949759f 204 free(request);
Jan Jongboom 0:910f5949759f 205
Jan Jongboom 0:910f5949759f 206 if (ret < 0) {
Jan Jongboom 0:910f5949759f 207 if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
Jan Jongboom 0:910f5949759f 208 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
Jan Jongboom 0:910f5949759f 209 print_mbedtls_error("mbedtls_ssl_write", ret);
Jan Jongboom 0:910f5949759f 210 onError(_tcpsocket, -1 );
Jan Jongboom 0:910f5949759f 211 }
Jan Jongboom 0:910f5949759f 212 else {
Jan Jongboom 0:910f5949759f 213 _error = ret;
Jan Jongboom 0:910f5949759f 214 }
Jan Jongboom 0:910f5949759f 215 return NULL;
Jan Jongboom 0:910f5949759f 216 }
Jan Jongboom 0:910f5949759f 217
Jan Jongboom 0:910f5949759f 218 /* It also means the handshake is done, time to print info */
Jan Jongboom 0:910f5949759f 219 if (_debug) mbedtls_printf("TLS connection to %s:%d established\r\n", _parsed_url->host(), _parsed_url->port());
Jan Jongboom 0:910f5949759f 220
Jan Jongboom 0:910f5949759f 221 const uint32_t buf_size = 1024;
Jan Jongboom 0:910f5949759f 222 char *buf = new char[buf_size];
Jan Jongboom 0:910f5949759f 223 mbedtls_x509_crt_info(buf, buf_size, "\r ",
Jan Jongboom 0:910f5949759f 224 mbedtls_ssl_get_peer_cert(&_ssl));
Jan Jongboom 0:910f5949759f 225 if (_debug) mbedtls_printf("Server certificate:\r\n%s\r", buf);
Jan Jongboom 0:910f5949759f 226
Jan Jongboom 0:910f5949759f 227 uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl);
Jan Jongboom 0:910f5949759f 228 if( flags != 0 )
Jan Jongboom 0:910f5949759f 229 {
Jan Jongboom 0:910f5949759f 230 mbedtls_x509_crt_verify_info(buf, buf_size, "\r ! ", flags);
Jan Jongboom 0:910f5949759f 231 if (_debug) mbedtls_printf("Certificate verification failed:\r\n%s\r\r\n", buf);
Jan Jongboom 0:910f5949759f 232 }
Jan Jongboom 0:910f5949759f 233 else {
Jan Jongboom 0:910f5949759f 234 if (_debug) mbedtls_printf("Certificate verification passed\r\n\r\n");
Jan Jongboom 0:910f5949759f 235 }
Jan Jongboom 0:910f5949759f 236
Jan Jongboom 0:910f5949759f 237 // Create a response object
Jan Jongboom 0:910f5949759f 238 _response = new HttpResponse();
Jan Jongboom 0:910f5949759f 239 // And a response parser
Jan Jongboom 0:910f5949759f 240 HttpResponseParser parser(_response, _body_callback);
Jan Jongboom 0:910f5949759f 241
Jan Jongboom 0:910f5949759f 242 // Set up a receive buffer (on the heap)
Jan Jongboom 0:910f5949759f 243 uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
Jan Jongboom 0:910f5949759f 244
Jan Jongboom 0:910f5949759f 245 /* Read data out of the socket */
Jan Jongboom 0:910f5949759f 246 while ((ret = mbedtls_ssl_read(&_ssl, (unsigned char *) recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
Jan Jongboom 0:910f5949759f 247 // Don't know if this is actually needed, but OK
Jan Jongboom 0:910f5949759f 248 size_t _bpos = static_cast<size_t>(ret);
Jan Jongboom 0:910f5949759f 249 recv_buffer[_bpos] = 0;
Jan Jongboom 0:910f5949759f 250
Jan Jongboom 0:910f5949759f 251 size_t nparsed = parser.execute((const char*)recv_buffer, _bpos);
Jan Jongboom 0:910f5949759f 252 if (nparsed != _bpos) {
Jan Jongboom 0:910f5949759f 253 print_mbedtls_error("parser_error", nparsed);
Jan Jongboom 0:910f5949759f 254 // parser error...
Jan Jongboom 0:910f5949759f 255 _error = -2101;
Jan Jongboom 0:910f5949759f 256 free(recv_buffer);
Jan Jongboom 0:910f5949759f 257 return NULL;
Jan Jongboom 0:910f5949759f 258 }
Jan Jongboom 6:112d72c60e07 259
Jan Jongboom 7:2e3eedb9ca5c 260 if (_response->is_message_complete()) {
Jan Jongboom 0:910f5949759f 261 break;
Jan Jongboom 0:910f5949759f 262 }
Jan Jongboom 0:910f5949759f 263 }
Jan Jongboom 0:910f5949759f 264 if (ret < 0) {
Jan Jongboom 0:910f5949759f 265 if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
Jan Jongboom 0:910f5949759f 266 print_mbedtls_error("mbedtls_ssl_read", ret);
Jan Jongboom 0:910f5949759f 267 onError(_tcpsocket, -1 );
Jan Jongboom 0:910f5949759f 268 }
Jan Jongboom 0:910f5949759f 269 else {
Jan Jongboom 0:910f5949759f 270 _error = ret;
Jan Jongboom 0:910f5949759f 271 }
Jan Jongboom 0:910f5949759f 272 free(recv_buffer);
Jan Jongboom 0:910f5949759f 273 return NULL;
Jan Jongboom 0:910f5949759f 274 }
Jan Jongboom 0:910f5949759f 275
Jan Jongboom 0:910f5949759f 276 parser.finish();
Jan Jongboom 0:910f5949759f 277
Jan Jongboom 0:910f5949759f 278 _tcpsocket->close();
Jan Jongboom 0:910f5949759f 279 free(recv_buffer);
Jan Jongboom 0:910f5949759f 280
Jan Jongboom 0:910f5949759f 281 return _response;
Jan Jongboom 0:910f5949759f 282 }
Jan Jongboom 0:910f5949759f 283
Jan Jongboom 0:910f5949759f 284 /**
Jan Jongboom 0:910f5949759f 285 * Closes the TCP socket
Jan Jongboom 0:910f5949759f 286 */
Jan Jongboom 0:910f5949759f 287 void close() {
Jan Jongboom 0:910f5949759f 288 _tcpsocket->close();
Jan Jongboom 0:910f5949759f 289 }
Jan Jongboom 0:910f5949759f 290
Jan Jongboom 0:910f5949759f 291 /**
Jan Jongboom 0:910f5949759f 292 * Set a header for the request.
Jan Jongboom 0:910f5949759f 293 *
Jan Jongboom 0:910f5949759f 294 * The 'Host' and 'Content-Length' headers are set automatically.
Jan Jongboom 0:910f5949759f 295 * Setting the same header twice will overwrite the previous entry.
Jan Jongboom 0:910f5949759f 296 *
Jan Jongboom 0:910f5949759f 297 * @param[in] key Header key
Jan Jongboom 0:910f5949759f 298 * @param[in] value Header value
Jan Jongboom 0:910f5949759f 299 */
Jan Jongboom 0:910f5949759f 300 void set_header(string key, string value) {
Jan Jongboom 0:910f5949759f 301 _request_builder->set_header(key, value);
Jan Jongboom 0:910f5949759f 302 }
Jan Jongboom 0:910f5949759f 303
Jan Jongboom 0:910f5949759f 304 /**
Jan Jongboom 0:910f5949759f 305 * Get the error code.
Jan Jongboom 0:910f5949759f 306 *
Jan Jongboom 0:910f5949759f 307 * When send() fails, this error is set.
Jan Jongboom 0:910f5949759f 308 */
Jan Jongboom 0:910f5949759f 309 nsapi_error_t get_error() {
Jan Jongboom 0:910f5949759f 310 return _error;
Jan Jongboom 0:910f5949759f 311 }
Jan Jongboom 0:910f5949759f 312
Jan Jongboom 0:910f5949759f 313 /**
Jan Jongboom 0:910f5949759f 314 * Set the debug flag.
Jan Jongboom 0:910f5949759f 315 *
Jan Jongboom 0:910f5949759f 316 * If this flag is set, debug information from mbed TLS will be logged to stdout.
Jan Jongboom 0:910f5949759f 317 */
Jan Jongboom 0:910f5949759f 318 void set_debug(bool debug) {
Jan Jongboom 0:910f5949759f 319 _debug = debug;
Jan Jongboom 0:910f5949759f 320 }
Jan Jongboom 0:910f5949759f 321
Jan Jongboom 0:910f5949759f 322 protected:
Jan Jongboom 0:910f5949759f 323 /**
Jan Jongboom 0:910f5949759f 324 * Helper for pretty-printing mbed TLS error codes
Jan Jongboom 0:910f5949759f 325 */
Jan Jongboom 0:910f5949759f 326 static void print_mbedtls_error(const char *name, int err) {
Jan Jongboom 0:910f5949759f 327 char buf[128];
Jan Jongboom 0:910f5949759f 328 mbedtls_strerror(err, buf, sizeof (buf));
Jan Jongboom 0:910f5949759f 329 mbedtls_printf("%s() failed: -0x%04x (%d): %s\r\n", name, -err, err, buf);
Jan Jongboom 0:910f5949759f 330 }
Jan Jongboom 0:910f5949759f 331
Jan Jongboom 0:910f5949759f 332 #if DEBUG_LEVEL > 0
Jan Jongboom 0:910f5949759f 333 /**
Jan Jongboom 0:910f5949759f 334 * Debug callback for mbed TLS
Jan Jongboom 0:910f5949759f 335 * Just prints on the USB serial port
Jan Jongboom 0:910f5949759f 336 */
Jan Jongboom 0:910f5949759f 337 static void my_debug(void *ctx, int level, const char *file, int line,
Jan Jongboom 0:910f5949759f 338 const char *str)
Jan Jongboom 0:910f5949759f 339 {
Jan Jongboom 0:910f5949759f 340 const char *p, *basename;
Jan Jongboom 0:910f5949759f 341 (void) ctx;
Jan Jongboom 0:910f5949759f 342
Jan Jongboom 0:910f5949759f 343 /* Extract basename from file */
Jan Jongboom 0:910f5949759f 344 for(p = basename = file; *p != '\0'; p++) {
Jan Jongboom 0:910f5949759f 345 if(*p == '/' || *p == '\\') {
Jan Jongboom 0:910f5949759f 346 basename = p + 1;
Jan Jongboom 0:910f5949759f 347 }
Jan Jongboom 0:910f5949759f 348 }
Jan Jongboom 0:910f5949759f 349
Jan Jongboom 0:910f5949759f 350 if (_debug) {
Jan Jongboom 0:910f5949759f 351 mbedtls_printf("%s:%04d: |%d| %s", basename, line, level, str);
Jan Jongboom 0:910f5949759f 352 }
Jan Jongboom 0:910f5949759f 353 }
Jan Jongboom 0:910f5949759f 354
Jan Jongboom 0:910f5949759f 355 /**
Jan Jongboom 0:910f5949759f 356 * Certificate verification callback for mbed TLS
Jan Jongboom 0:910f5949759f 357 * Here we only use it to display information on each cert in the chain
Jan Jongboom 0:910f5949759f 358 */
Jan Jongboom 0:910f5949759f 359 static int my_verify(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
Jan Jongboom 0:910f5949759f 360 {
Jan Jongboom 0:910f5949759f 361 const uint32_t buf_size = 1024;
Jan Jongboom 0:910f5949759f 362 char *buf = new char[buf_size];
Jan Jongboom 0:910f5949759f 363 (void) data;
Jan Jongboom 0:910f5949759f 364
Jan Jongboom 0:910f5949759f 365 if (_debug) mbedtls_printf("\nVerifying certificate at depth %d:\n", depth);
Jan Jongboom 0:910f5949759f 366 mbedtls_x509_crt_info(buf, buf_size - 1, " ", crt);
Jan Jongboom 0:910f5949759f 367 if (_debug) mbedtls_printf("%s", buf);
Jan Jongboom 0:910f5949759f 368
Jan Jongboom 0:910f5949759f 369 if (*flags == 0)
Jan Jongboom 0:910f5949759f 370 if (_debug) mbedtls_printf("No verification issue for this certificate\n");
Jan Jongboom 0:910f5949759f 371 else
Jan Jongboom 0:910f5949759f 372 {
Jan Jongboom 0:910f5949759f 373 mbedtls_x509_crt_verify_info(buf, buf_size, " ! ", *flags);
Jan Jongboom 0:910f5949759f 374 if (_debug) mbedtls_printf("%s\n", buf);
Jan Jongboom 0:910f5949759f 375 }
Jan Jongboom 0:910f5949759f 376
Jan Jongboom 0:910f5949759f 377 delete[] buf;
Jan Jongboom 0:910f5949759f 378 return 0;
Jan Jongboom 0:910f5949759f 379 }
Jan Jongboom 0:910f5949759f 380 #endif
Jan Jongboom 0:910f5949759f 381
Jan Jongboom 0:910f5949759f 382 /**
Jan Jongboom 0:910f5949759f 383 * Receive callback for mbed TLS
Jan Jongboom 0:910f5949759f 384 */
Jan Jongboom 0:910f5949759f 385 static int ssl_recv(void *ctx, unsigned char *buf, size_t len) {
Jan Jongboom 0:910f5949759f 386 int recv = -1;
Jan Jongboom 0:910f5949759f 387 TCPSocket *socket = static_cast<TCPSocket *>(ctx);
Jan Jongboom 0:910f5949759f 388 recv = socket->recv(buf, len);
Jan Jongboom 0:910f5949759f 389
Jan Jongboom 0:910f5949759f 390 if (NSAPI_ERROR_WOULD_BLOCK == recv) {
Jan Jongboom 0:910f5949759f 391 return MBEDTLS_ERR_SSL_WANT_READ;
Jan Jongboom 0:910f5949759f 392 }
Jan Jongboom 0:910f5949759f 393 else if (recv < 0) {
Jan Jongboom 0:910f5949759f 394 return -1;
Jan Jongboom 0:910f5949759f 395 }
Jan Jongboom 0:910f5949759f 396 else {
Jan Jongboom 0:910f5949759f 397 return recv;
Jan Jongboom 0:910f5949759f 398 }
Jan Jongboom 0:910f5949759f 399 }
Jan Jongboom 0:910f5949759f 400
Jan Jongboom 0:910f5949759f 401 /**
Jan Jongboom 0:910f5949759f 402 * Send callback for mbed TLS
Jan Jongboom 0:910f5949759f 403 */
Jan Jongboom 0:910f5949759f 404 static int ssl_send(void *ctx, const unsigned char *buf, size_t len) {
Jan Jongboom 0:910f5949759f 405 int size = -1;
Jan Jongboom 0:910f5949759f 406 TCPSocket *socket = static_cast<TCPSocket *>(ctx);
Jan Jongboom 0:910f5949759f 407 size = socket->send(buf, len);
Jan Jongboom 0:910f5949759f 408
Jan Jongboom 0:910f5949759f 409 if(NSAPI_ERROR_WOULD_BLOCK == size) {
Jan Jongboom 0:910f5949759f 410 return len;
Jan Jongboom 0:910f5949759f 411 }
Jan Jongboom 0:910f5949759f 412 else if (size < 0){
Jan Jongboom 0:910f5949759f 413 return -1;
Jan Jongboom 0:910f5949759f 414 }
Jan Jongboom 0:910f5949759f 415 else {
Jan Jongboom 0:910f5949759f 416 return size;
Jan Jongboom 0:910f5949759f 417 }
Jan Jongboom 0:910f5949759f 418 }
Jan Jongboom 0:910f5949759f 419
Jan Jongboom 0:910f5949759f 420 void onError(TCPSocket *s, int error) {
Jan Jongboom 0:910f5949759f 421 s->close();
Jan Jongboom 0:910f5949759f 422 _error = error;
Jan Jongboom 0:910f5949759f 423 }
Jan Jongboom 0:910f5949759f 424
Jan Jongboom 0:910f5949759f 425 protected:
Jan Jongboom 0:910f5949759f 426 TCPSocket* _tcpsocket;
Jan Jongboom 0:910f5949759f 427
Jan Jongboom 0:910f5949759f 428 Callback<void(const char *at, size_t length)> _body_callback;
Jan Jongboom 0:910f5949759f 429 ParsedUrl* _parsed_url;
Jan Jongboom 0:910f5949759f 430 HttpRequestBuilder* _request_builder;
Jan Jongboom 0:910f5949759f 431 HttpResponse* _response;
Jan Jongboom 0:910f5949759f 432 const char *DRBG_PERS;
Jan Jongboom 0:910f5949759f 433 const char *_ssl_ca_pem;
Jan Jongboom 0:910f5949759f 434
Jan Jongboom 0:910f5949759f 435 nsapi_error_t _error;
Jan Jongboom 0:910f5949759f 436 bool _debug;
Jan Jongboom 0:910f5949759f 437
Jan Jongboom 0:910f5949759f 438 mbedtls_entropy_context _entropy;
Jan Jongboom 0:910f5949759f 439 mbedtls_ctr_drbg_context _ctr_drbg;
Jan Jongboom 0:910f5949759f 440 mbedtls_x509_crt _cacert;
Jan Jongboom 0:910f5949759f 441 mbedtls_ssl_context _ssl;
Jan Jongboom 0:910f5949759f 442 mbedtls_ssl_config _ssl_conf;
Jan Jongboom 0:910f5949759f 443 };
Jan Jongboom 0:910f5949759f 444
Jan Jongboom 0:910f5949759f 445 #endif // _MBED_HTTPS_REQUEST_H_