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:
Mon Aug 12 11:45:31 2019 +0200
Revision:
39:a8d157986ad8
Parent:
31:b3730a2c4f39
Fix parsed url leaking memory if path is empty

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jan Jongboom 31:b3730a2c4f39 1 /*
Jan Jongboom 31:b3730a2c4f39 2 * PackageLicenseDeclared: Apache-2.0
Jan Jongboom 31:b3730a2c4f39 3 * Copyright (c) 2017 ARM Limited
Jan Jongboom 31:b3730a2c4f39 4 *
Jan Jongboom 31:b3730a2c4f39 5 * Licensed under the Apache License, Version 2.0 (the "License");
Jan Jongboom 31:b3730a2c4f39 6 * you may not use this file except in compliance with the License.
Jan Jongboom 31:b3730a2c4f39 7 * You may obtain a copy of the License at
Jan Jongboom 31:b3730a2c4f39 8 *
Jan Jongboom 31:b3730a2c4f39 9 * http://www.apache.org/licenses/LICENSE-2.0
Jan Jongboom 31:b3730a2c4f39 10 *
Jan Jongboom 31:b3730a2c4f39 11 * Unless required by applicable law or agreed to in writing, software
Jan Jongboom 31:b3730a2c4f39 12 * distributed under the License is distributed on an "AS IS" BASIS,
Jan Jongboom 31:b3730a2c4f39 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Jan Jongboom 31:b3730a2c4f39 14 * See the License for the specific language governing permissions and
Jan Jongboom 31:b3730a2c4f39 15 * limitations under the License.
Jan Jongboom 31:b3730a2c4f39 16 */
Jan Jongboom 31:b3730a2c4f39 17
Jan Jongboom 31:b3730a2c4f39 18 #ifndef _HTTP_REQUEST_BASE_H_
Jan Jongboom 31:b3730a2c4f39 19 #define _HTTP_REQUEST_BASE_H_
Jan Jongboom 31:b3730a2c4f39 20
Jan Jongboom 31:b3730a2c4f39 21 #include <map>
Jan Jongboom 31:b3730a2c4f39 22 #include <string>
Jan Jongboom 31:b3730a2c4f39 23 #include <vector>
Jan Jongboom 31:b3730a2c4f39 24 #include "mbed.h"
Jan Jongboom 31:b3730a2c4f39 25 #include "http_parser.h"
Jan Jongboom 31:b3730a2c4f39 26 #include "http_parsed_url.h"
Jan Jongboom 31:b3730a2c4f39 27 #include "http_request_builder.h"
Jan Jongboom 31:b3730a2c4f39 28 #include "http_request_parser.h"
Jan Jongboom 31:b3730a2c4f39 29 #include "http_response.h"
Jan Jongboom 31:b3730a2c4f39 30 #include "NetworkInterface.h"
Jan Jongboom 31:b3730a2c4f39 31 #include "netsocket/Socket.h"
Jan Jongboom 31:b3730a2c4f39 32
Jan Jongboom 31:b3730a2c4f39 33 /**
Jan Jongboom 31:b3730a2c4f39 34 * @todo:
Jan Jongboom 31:b3730a2c4f39 35 * - Userinfo parameter is not handled
Jan Jongboom 31:b3730a2c4f39 36 */
Jan Jongboom 31:b3730a2c4f39 37
Jan Jongboom 31:b3730a2c4f39 38 #ifndef HTTP_RECEIVE_BUFFER_SIZE
Jan Jongboom 31:b3730a2c4f39 39 #define HTTP_RECEIVE_BUFFER_SIZE 8 * 1024
Jan Jongboom 31:b3730a2c4f39 40 #endif
Jan Jongboom 31:b3730a2c4f39 41
Jan Jongboom 31:b3730a2c4f39 42 class HttpRequest;
Jan Jongboom 31:b3730a2c4f39 43 class HttpsRequest;
Jan Jongboom 31:b3730a2c4f39 44
Jan Jongboom 31:b3730a2c4f39 45 /**
Jan Jongboom 31:b3730a2c4f39 46 * \brief HttpRequest implements the logic for interacting with HTTP servers.
Jan Jongboom 31:b3730a2c4f39 47 */
Jan Jongboom 31:b3730a2c4f39 48 class HttpRequestBase {
Jan Jongboom 31:b3730a2c4f39 49 friend class HttpRequest;
Jan Jongboom 31:b3730a2c4f39 50 friend class HttpsRequest;
Jan Jongboom 31:b3730a2c4f39 51
Jan Jongboom 31:b3730a2c4f39 52 public:
Jan Jongboom 31:b3730a2c4f39 53 HttpRequestBase(Socket *socket, Callback<void(const char *at, uint32_t length)> bodyCallback)
Jan Jongboom 31:b3730a2c4f39 54 : _socket(socket), _body_callback(bodyCallback), _request_buffer(NULL), _request_buffer_ix(0)
Jan Jongboom 31:b3730a2c4f39 55 {}
Jan Jongboom 31:b3730a2c4f39 56
Jan Jongboom 31:b3730a2c4f39 57 /**
Jan Jongboom 31:b3730a2c4f39 58 * HttpRequest Constructor
Jan Jongboom 31:b3730a2c4f39 59 */
Jan Jongboom 31:b3730a2c4f39 60 virtual ~HttpRequestBase() {
Jan Jongboom 31:b3730a2c4f39 61 // should response be owned by us? Or should user free it?
Jan Jongboom 31:b3730a2c4f39 62 // maybe implement copy constructor on response...
Jan Jongboom 31:b3730a2c4f39 63 if (_response) {
Jan Jongboom 31:b3730a2c4f39 64 delete _response;
Jan Jongboom 31:b3730a2c4f39 65 }
Jan Jongboom 31:b3730a2c4f39 66
Jan Jongboom 31:b3730a2c4f39 67 if (_parsed_url) {
Jan Jongboom 31:b3730a2c4f39 68 delete _parsed_url;
Jan Jongboom 31:b3730a2c4f39 69 }
Jan Jongboom 31:b3730a2c4f39 70
Jan Jongboom 31:b3730a2c4f39 71 if (_request_builder) {
Jan Jongboom 31:b3730a2c4f39 72 delete _request_builder;
Jan Jongboom 31:b3730a2c4f39 73 }
Jan Jongboom 31:b3730a2c4f39 74
Jan Jongboom 31:b3730a2c4f39 75 if (_socket && _we_created_socket) {
Jan Jongboom 31:b3730a2c4f39 76 delete _socket;
Jan Jongboom 31:b3730a2c4f39 77 }
Jan Jongboom 31:b3730a2c4f39 78 }
Jan Jongboom 31:b3730a2c4f39 79
Jan Jongboom 31:b3730a2c4f39 80 /**
Jan Jongboom 31:b3730a2c4f39 81 * Execute the request and receive the response.
Jan Jongboom 31:b3730a2c4f39 82 * This adds a Content-Length header to the request (when body_size is set), and sends the data to the server.
Jan Jongboom 31:b3730a2c4f39 83 * @param body Pointer to the body to be sent
Jan Jongboom 31:b3730a2c4f39 84 * @param body_size Size of the body to be sent
Jan Jongboom 31:b3730a2c4f39 85 * @return An HttpResponse pointer on success, or NULL on failure.
Jan Jongboom 31:b3730a2c4f39 86 * See get_error() for the error code.
Jan Jongboom 31:b3730a2c4f39 87 */
Jan Jongboom 31:b3730a2c4f39 88 HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
Jan Jongboom 31:b3730a2c4f39 89 nsapi_size_or_error_t ret = connect_socket();
Jan Jongboom 31:b3730a2c4f39 90
Jan Jongboom 31:b3730a2c4f39 91 if (ret != NSAPI_ERROR_OK) {
Jan Jongboom 31:b3730a2c4f39 92 _error = ret;
Jan Jongboom 31:b3730a2c4f39 93 return NULL;
Jan Jongboom 31:b3730a2c4f39 94 }
Jan Jongboom 31:b3730a2c4f39 95
Jan Jongboom 31:b3730a2c4f39 96 _request_buffer_ix = 0;
Jan Jongboom 31:b3730a2c4f39 97
Jan Jongboom 31:b3730a2c4f39 98 uint32_t request_size = 0;
Jan Jongboom 31:b3730a2c4f39 99 char* request = _request_builder->build(body, body_size, request_size);
Jan Jongboom 31:b3730a2c4f39 100
Jan Jongboom 31:b3730a2c4f39 101 ret = send_buffer(request, request_size);
Jan Jongboom 31:b3730a2c4f39 102
Jan Jongboom 31:b3730a2c4f39 103 free(request);
Jan Jongboom 31:b3730a2c4f39 104
Jan Jongboom 31:b3730a2c4f39 105 if (ret < 0) {
Jan Jongboom 31:b3730a2c4f39 106 _error = ret;
Jan Jongboom 31:b3730a2c4f39 107 return NULL;
Jan Jongboom 31:b3730a2c4f39 108 }
Jan Jongboom 31:b3730a2c4f39 109
Jan Jongboom 31:b3730a2c4f39 110 return create_http_response();
Jan Jongboom 31:b3730a2c4f39 111 }
Jan Jongboom 31:b3730a2c4f39 112
Jan Jongboom 31:b3730a2c4f39 113 /**
Jan Jongboom 31:b3730a2c4f39 114 * Execute the request and receive the response.
Jan Jongboom 31:b3730a2c4f39 115 * This sends the request through chunked-encoding.
Jan Jongboom 31:b3730a2c4f39 116 * @param body_cb Callback which generates the next chunk of the request
Jan Jongboom 31:b3730a2c4f39 117 * @return An HttpResponse pointer on success, or NULL on failure.
Jan Jongboom 31:b3730a2c4f39 118 * See get_error() for the error code.
Jan Jongboom 31:b3730a2c4f39 119 */
Jan Jongboom 31:b3730a2c4f39 120 HttpResponse* send(Callback<const void*(uint32_t*)> body_cb) {
Jan Jongboom 31:b3730a2c4f39 121
Jan Jongboom 31:b3730a2c4f39 122 nsapi_error_t ret;
Jan Jongboom 31:b3730a2c4f39 123
Jan Jongboom 31:b3730a2c4f39 124 if ((ret = connect_socket()) != NSAPI_ERROR_OK) {
Jan Jongboom 31:b3730a2c4f39 125 _error = ret;
Jan Jongboom 31:b3730a2c4f39 126 return NULL;
Jan Jongboom 31:b3730a2c4f39 127 }
Jan Jongboom 31:b3730a2c4f39 128
Jan Jongboom 31:b3730a2c4f39 129 _request_buffer_ix = 0;
Jan Jongboom 31:b3730a2c4f39 130
Jan Jongboom 31:b3730a2c4f39 131 set_header("Transfer-Encoding", "chunked");
Jan Jongboom 31:b3730a2c4f39 132
Jan Jongboom 31:b3730a2c4f39 133 uint32_t request_size = 0;
Jan Jongboom 31:b3730a2c4f39 134 char* request = _request_builder->build(NULL, 0, request_size);
Jan Jongboom 31:b3730a2c4f39 135
Jan Jongboom 31:b3730a2c4f39 136 // first... send this request headers without the body
Jan Jongboom 31:b3730a2c4f39 137 nsapi_size_or_error_t total_send_count = send_buffer(request, request_size);
Jan Jongboom 31:b3730a2c4f39 138
Jan Jongboom 31:b3730a2c4f39 139 if (total_send_count < 0) {
Jan Jongboom 31:b3730a2c4f39 140 free(request);
Jan Jongboom 31:b3730a2c4f39 141 _error = total_send_count;
Jan Jongboom 31:b3730a2c4f39 142 return NULL;
Jan Jongboom 31:b3730a2c4f39 143 }
Jan Jongboom 31:b3730a2c4f39 144
Jan Jongboom 31:b3730a2c4f39 145 // ok... now it's time to start sending chunks...
Jan Jongboom 31:b3730a2c4f39 146 while (1) {
Jan Jongboom 31:b3730a2c4f39 147 uint32_t size;
Jan Jongboom 31:b3730a2c4f39 148 const void *buffer = body_cb(&size);
Jan Jongboom 31:b3730a2c4f39 149
Jan Jongboom 31:b3730a2c4f39 150 if (size == 0) break;
Jan Jongboom 31:b3730a2c4f39 151
Jan Jongboom 31:b3730a2c4f39 152 // so... size in HEX, \r\n, data, \r\n again
Jan Jongboom 31:b3730a2c4f39 153 char size_buff[10]; // if sending length of more than 8 digits, you have another problem on a microcontroller...
Jan Jongboom 31:b3730a2c4f39 154 int size_buff_size = sprintf(size_buff, "%X\r\n", static_cast<size_t>(size));
Jan Jongboom 31:b3730a2c4f39 155 if ((total_send_count = send_buffer(size_buff, static_cast<uint32_t>(size_buff_size))) < 0) {
Jan Jongboom 31:b3730a2c4f39 156 free(request);
Jan Jongboom 31:b3730a2c4f39 157 _error = total_send_count;
Jan Jongboom 31:b3730a2c4f39 158 return NULL;
Jan Jongboom 31:b3730a2c4f39 159 }
Jan Jongboom 31:b3730a2c4f39 160
Jan Jongboom 31:b3730a2c4f39 161 // now send the normal buffer... and then \r\n at the end
Jan Jongboom 31:b3730a2c4f39 162 total_send_count = send_buffer((char*)buffer, size);
Jan Jongboom 31:b3730a2c4f39 163 if (total_send_count < 0) {
Jan Jongboom 31:b3730a2c4f39 164 free(request);
Jan Jongboom 31:b3730a2c4f39 165 _error = total_send_count;
Jan Jongboom 31:b3730a2c4f39 166 return NULL;
Jan Jongboom 31:b3730a2c4f39 167 }
Jan Jongboom 31:b3730a2c4f39 168
Jan Jongboom 31:b3730a2c4f39 169 // and... \r\n
Jan Jongboom 31:b3730a2c4f39 170 const char* rn = "\r\n";
Jan Jongboom 31:b3730a2c4f39 171 if ((total_send_count = send_buffer((char*)rn, 2)) < 0) {
Jan Jongboom 31:b3730a2c4f39 172 free(request);
Jan Jongboom 31:b3730a2c4f39 173 _error = total_send_count;
Jan Jongboom 31:b3730a2c4f39 174 return NULL;
Jan Jongboom 31:b3730a2c4f39 175 }
Jan Jongboom 31:b3730a2c4f39 176 }
Jan Jongboom 31:b3730a2c4f39 177
Jan Jongboom 31:b3730a2c4f39 178 // finalize...?
Jan Jongboom 31:b3730a2c4f39 179 const char* fin = "0\r\n\r\n";
Jan Jongboom 31:b3730a2c4f39 180 if ((total_send_count = send_buffer((char*)fin, strlen(fin))) < 0) {
Jan Jongboom 31:b3730a2c4f39 181 free(request);
Jan Jongboom 31:b3730a2c4f39 182 _error = total_send_count;
Jan Jongboom 31:b3730a2c4f39 183 return NULL;
Jan Jongboom 31:b3730a2c4f39 184 }
Jan Jongboom 31:b3730a2c4f39 185
Jan Jongboom 31:b3730a2c4f39 186 free(request);
Jan Jongboom 31:b3730a2c4f39 187
Jan Jongboom 31:b3730a2c4f39 188 return create_http_response();
Jan Jongboom 31:b3730a2c4f39 189 }
Jan Jongboom 31:b3730a2c4f39 190
Jan Jongboom 31:b3730a2c4f39 191 /**
Jan Jongboom 31:b3730a2c4f39 192 * Set a header for the request.
Jan Jongboom 31:b3730a2c4f39 193 *
Jan Jongboom 31:b3730a2c4f39 194 * The 'Host', 'Content-Length', and (optionally) 'Transfer-Encoding: chunked'
Jan Jongboom 31:b3730a2c4f39 195 * headers are set automatically.
Jan Jongboom 31:b3730a2c4f39 196 * Setting the same header twice will overwrite the previous entry.
Jan Jongboom 31:b3730a2c4f39 197 *
Jan Jongboom 31:b3730a2c4f39 198 * @param key Header key
Jan Jongboom 31:b3730a2c4f39 199 * @param value Header value
Jan Jongboom 31:b3730a2c4f39 200 */
Jan Jongboom 31:b3730a2c4f39 201 void set_header(string key, string value) {
Jan Jongboom 31:b3730a2c4f39 202 _request_builder->set_header(key, value);
Jan Jongboom 31:b3730a2c4f39 203 }
Jan Jongboom 31:b3730a2c4f39 204
Jan Jongboom 31:b3730a2c4f39 205 /**
Jan Jongboom 31:b3730a2c4f39 206 * Get the error code.
Jan Jongboom 31:b3730a2c4f39 207 *
Jan Jongboom 31:b3730a2c4f39 208 * When send() fails, this error is set.
Jan Jongboom 31:b3730a2c4f39 209 */
Jan Jongboom 31:b3730a2c4f39 210 nsapi_error_t get_error() {
Jan Jongboom 31:b3730a2c4f39 211 return _error;
Jan Jongboom 31:b3730a2c4f39 212 }
Jan Jongboom 31:b3730a2c4f39 213
Jan Jongboom 31:b3730a2c4f39 214 /**
Jan Jongboom 31:b3730a2c4f39 215 * Set the request log buffer, all bytes that are sent for this request are logged here.
Jan Jongboom 31:b3730a2c4f39 216 * If the buffer would overflow logging is stopped.
Jan Jongboom 31:b3730a2c4f39 217 *
Jan Jongboom 31:b3730a2c4f39 218 * @param buffer Pointer to a buffer to store the data in
Jan Jongboom 31:b3730a2c4f39 219 * @param buffer_size Size of the buffer
Jan Jongboom 31:b3730a2c4f39 220 */
Jan Jongboom 31:b3730a2c4f39 221 void set_request_log_buffer(uint8_t *buffer, size_t buffer_size) {
Jan Jongboom 31:b3730a2c4f39 222 _request_buffer = buffer;
Jan Jongboom 31:b3730a2c4f39 223 _request_buffer_size = buffer_size;
Jan Jongboom 31:b3730a2c4f39 224 _request_buffer_ix = 0;
Jan Jongboom 31:b3730a2c4f39 225 }
Jan Jongboom 31:b3730a2c4f39 226
Jan Jongboom 31:b3730a2c4f39 227 /**
Jan Jongboom 31:b3730a2c4f39 228 * Get the number of bytes written to the request log buffer, since the last request.
Jan Jongboom 31:b3730a2c4f39 229 * If no request was sent, or if the request log buffer is NULL, then this returns 0.
Jan Jongboom 31:b3730a2c4f39 230 */
Jan Jongboom 31:b3730a2c4f39 231 size_t get_request_log_buffer_length() {
Jan Jongboom 31:b3730a2c4f39 232 return _request_buffer_ix;
Jan Jongboom 31:b3730a2c4f39 233 }
Jan Jongboom 31:b3730a2c4f39 234
Jan Jongboom 31:b3730a2c4f39 235 protected:
Jan Jongboom 31:b3730a2c4f39 236 virtual nsapi_error_t connect_socket(char *host, uint16_t port) = 0;
Jan Jongboom 31:b3730a2c4f39 237
Jan Jongboom 31:b3730a2c4f39 238 private:
Jan Jongboom 31:b3730a2c4f39 239 nsapi_error_t connect_socket( ) {
Jan Jongboom 31:b3730a2c4f39 240 if (_response != NULL) {
Jan Jongboom 31:b3730a2c4f39 241 // already executed this response
Jan Jongboom 31:b3730a2c4f39 242 return -2100; // @todo, make a lookup table with errors
Jan Jongboom 31:b3730a2c4f39 243 }
Jan Jongboom 31:b3730a2c4f39 244
Jan Jongboom 31:b3730a2c4f39 245
Jan Jongboom 31:b3730a2c4f39 246 if (_we_created_socket) {
Jan Jongboom 31:b3730a2c4f39 247 nsapi_error_t connection_result = connect_socket(_parsed_url->host(), _parsed_url->port());
Jan Jongboom 31:b3730a2c4f39 248 if (connection_result != NSAPI_ERROR_OK) {
Jan Jongboom 31:b3730a2c4f39 249 return connection_result;
Jan Jongboom 31:b3730a2c4f39 250 }
Jan Jongboom 31:b3730a2c4f39 251 }
Jan Jongboom 31:b3730a2c4f39 252
Jan Jongboom 31:b3730a2c4f39 253 return NSAPI_ERROR_OK;
Jan Jongboom 31:b3730a2c4f39 254 }
Jan Jongboom 31:b3730a2c4f39 255
Jan Jongboom 31:b3730a2c4f39 256 nsapi_size_or_error_t send_buffer(char* buffer, uint32_t buffer_size) {
Jan Jongboom 31:b3730a2c4f39 257 nsapi_size_or_error_t total_send_count = 0;
Jan Jongboom 31:b3730a2c4f39 258 while (total_send_count < buffer_size) {
Jan Jongboom 31:b3730a2c4f39 259
Jan Jongboom 31:b3730a2c4f39 260 // get a slice of the buffer
Jan Jongboom 31:b3730a2c4f39 261 char *buffer_slice = buffer + total_send_count;
Jan Jongboom 31:b3730a2c4f39 262 uint32_t buffer_slice_size = buffer_size - total_send_count;
Jan Jongboom 31:b3730a2c4f39 263
Jan Jongboom 31:b3730a2c4f39 264 // if request buffer was set, copy it there
Jan Jongboom 31:b3730a2c4f39 265 if (_request_buffer != NULL && _request_buffer_ix + buffer_slice_size < _request_buffer_size) {
Jan Jongboom 31:b3730a2c4f39 266 memcpy(_request_buffer + _request_buffer_ix, buffer_slice, buffer_slice_size);
Jan Jongboom 31:b3730a2c4f39 267 _request_buffer_ix += buffer_slice_size;
Jan Jongboom 31:b3730a2c4f39 268 }
Jan Jongboom 31:b3730a2c4f39 269
Jan Jongboom 31:b3730a2c4f39 270 nsapi_size_or_error_t send_result = _socket->send(buffer_slice, buffer_slice_size);
Jan Jongboom 31:b3730a2c4f39 271
Jan Jongboom 31:b3730a2c4f39 272 if (send_result < 0) {
Jan Jongboom 31:b3730a2c4f39 273 total_send_count = send_result;
Jan Jongboom 31:b3730a2c4f39 274 break;
Jan Jongboom 31:b3730a2c4f39 275 }
Jan Jongboom 31:b3730a2c4f39 276
Jan Jongboom 31:b3730a2c4f39 277 if (send_result == 0) {
Jan Jongboom 31:b3730a2c4f39 278 break;
Jan Jongboom 31:b3730a2c4f39 279 }
Jan Jongboom 31:b3730a2c4f39 280
Jan Jongboom 31:b3730a2c4f39 281 total_send_count += send_result;
Jan Jongboom 31:b3730a2c4f39 282 }
Jan Jongboom 31:b3730a2c4f39 283
Jan Jongboom 31:b3730a2c4f39 284 return total_send_count;
Jan Jongboom 31:b3730a2c4f39 285 }
Jan Jongboom 31:b3730a2c4f39 286
Jan Jongboom 31:b3730a2c4f39 287 HttpResponse* create_http_response() {
Jan Jongboom 31:b3730a2c4f39 288 // Create a response object
Jan Jongboom 31:b3730a2c4f39 289 _response = new HttpResponse();
Jan Jongboom 31:b3730a2c4f39 290 // And a response parser
Jan Jongboom 31:b3730a2c4f39 291 HttpParser parser(_response, HTTP_RESPONSE, _body_callback);
Jan Jongboom 31:b3730a2c4f39 292
Jan Jongboom 31:b3730a2c4f39 293 // Set up a receive buffer (on the heap)
Jan Jongboom 31:b3730a2c4f39 294 uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
Jan Jongboom 31:b3730a2c4f39 295
Jan Jongboom 31:b3730a2c4f39 296 // Socket::recv is called until we don't have any data anymore
Jan Jongboom 31:b3730a2c4f39 297 nsapi_size_or_error_t recv_ret;
Jan Jongboom 31:b3730a2c4f39 298 while ((recv_ret = _socket->recv(recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
Jan Jongboom 31:b3730a2c4f39 299
Jan Jongboom 31:b3730a2c4f39 300 // Pass the chunk into the http_parser
Jan Jongboom 31:b3730a2c4f39 301 uint32_t nparsed = parser.execute((const char*)recv_buffer, recv_ret);
Jan Jongboom 31:b3730a2c4f39 302 if (nparsed != recv_ret) {
Jan Jongboom 31:b3730a2c4f39 303 // printf("Parsing failed... parsed %d bytes, received %d bytes\n", nparsed, recv_ret);
Jan Jongboom 31:b3730a2c4f39 304 _error = -2101;
Jan Jongboom 31:b3730a2c4f39 305 free(recv_buffer);
Jan Jongboom 31:b3730a2c4f39 306 return NULL;
Jan Jongboom 31:b3730a2c4f39 307 }
Jan Jongboom 31:b3730a2c4f39 308
Jan Jongboom 31:b3730a2c4f39 309 if (_response->is_message_complete()) {
Jan Jongboom 31:b3730a2c4f39 310 break;
Jan Jongboom 31:b3730a2c4f39 311 }
Jan Jongboom 31:b3730a2c4f39 312 }
Jan Jongboom 31:b3730a2c4f39 313 // error?
Jan Jongboom 31:b3730a2c4f39 314 if (recv_ret < 0) {
Jan Jongboom 31:b3730a2c4f39 315 _error = recv_ret;
Jan Jongboom 31:b3730a2c4f39 316 free(recv_buffer);
Jan Jongboom 31:b3730a2c4f39 317 return NULL;
Jan Jongboom 31:b3730a2c4f39 318 }
Jan Jongboom 31:b3730a2c4f39 319
Jan Jongboom 31:b3730a2c4f39 320 // When done, call parser.finish()
Jan Jongboom 31:b3730a2c4f39 321 parser.finish();
Jan Jongboom 31:b3730a2c4f39 322
Jan Jongboom 31:b3730a2c4f39 323 // Free the receive buffer
Jan Jongboom 31:b3730a2c4f39 324 free(recv_buffer);
Jan Jongboom 31:b3730a2c4f39 325
Jan Jongboom 31:b3730a2c4f39 326 if (_we_created_socket) {
Jan Jongboom 31:b3730a2c4f39 327 // Close the socket
Jan Jongboom 31:b3730a2c4f39 328 _socket->close();
Jan Jongboom 31:b3730a2c4f39 329 }
Jan Jongboom 31:b3730a2c4f39 330
Jan Jongboom 31:b3730a2c4f39 331 return _response;
Jan Jongboom 31:b3730a2c4f39 332 }
Jan Jongboom 31:b3730a2c4f39 333
Jan Jongboom 31:b3730a2c4f39 334 private:
Jan Jongboom 31:b3730a2c4f39 335 Socket* _socket;
Jan Jongboom 31:b3730a2c4f39 336 Callback<void(const char *at, uint32_t length)> _body_callback;
Jan Jongboom 31:b3730a2c4f39 337
Jan Jongboom 31:b3730a2c4f39 338 ParsedUrl* _parsed_url;
Jan Jongboom 31:b3730a2c4f39 339
Jan Jongboom 31:b3730a2c4f39 340 HttpRequestBuilder* _request_builder;
Jan Jongboom 31:b3730a2c4f39 341 HttpResponse* _response;
Jan Jongboom 31:b3730a2c4f39 342
Jan Jongboom 31:b3730a2c4f39 343 bool _we_created_socket;
Jan Jongboom 31:b3730a2c4f39 344
Jan Jongboom 31:b3730a2c4f39 345 nsapi_error_t _error;
Jan Jongboom 31:b3730a2c4f39 346
Jan Jongboom 31:b3730a2c4f39 347 uint8_t *_request_buffer;
Jan Jongboom 31:b3730a2c4f39 348 size_t _request_buffer_size;
Jan Jongboom 31:b3730a2c4f39 349 size_t _request_buffer_ix;
Jan Jongboom 31:b3730a2c4f39 350 };
Jan Jongboom 31:b3730a2c4f39 351
Jan Jongboom 31:b3730a2c4f39 352 #endif // _HTTP_REQUEST_BASE_H_