Fork of mbed-http

Fork of mbed-http by sandbox

Committer:
Jan Jongboom
Date:
Tue Mar 28 14:44:39 2017 +0200
Revision:
11:96e4dcb9c0c2
Parent:
10:b017c7d2cf23
Allow socket re-use in HTTPS and HTTP request

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 #include <string>
Jan Jongboom 0:910f5949759f 22 #include <vector>
Jan Jongboom 0:910f5949759f 23 #include <map>
Jan Jongboom 0:910f5949759f 24 #include "http_parser.h"
Jan Jongboom 0:910f5949759f 25 #include "http_response.h"
Jan Jongboom 0:910f5949759f 26 #include "http_request_builder.h"
Jan Jongboom 0:910f5949759f 27 #include "http_response_parser.h"
Jan Jongboom 0:910f5949759f 28 #include "http_parsed_url.h"
Jan Jongboom 11:96e4dcb9c0c2 29 #include "tls_socket.h"
Jan Jongboom 0:910f5949759f 30
Jan Jongboom 0:910f5949759f 31 /**
Jan Jongboom 0:910f5949759f 32 * \brief HttpsRequest implements the logic for interacting with HTTPS servers.
Jan Jongboom 0:910f5949759f 33 */
Jan Jongboom 0:910f5949759f 34 class HttpsRequest {
Jan Jongboom 0:910f5949759f 35 public:
Jan Jongboom 0:910f5949759f 36 /**
Jan Jongboom 0:910f5949759f 37 * HttpsRequest Constructor
Jan Jongboom 0:910f5949759f 38 * Initializes the TCP socket, sets up event handlers and flags.
Jan Jongboom 0:910f5949759f 39 *
Jan Jongboom 0:910f5949759f 40 * @param[in] net_iface The network interface
Jan Jongboom 0:910f5949759f 41 * @param[in] ssl_ca_pem String containing the trusted CAs
Jan Jongboom 0:910f5949759f 42 * @param[in] method HTTP method to use
Jan Jongboom 0:910f5949759f 43 * @param[in] url URL to the resource
Jan Jongboom 0:910f5949759f 44 * @param[in] body_callback Callback on which to retrieve chunks of the response body.
Jan Jongboom 0:910f5949759f 45 If not set, the complete body will be allocated on the HttpResponse object,
Jan Jongboom 0:910f5949759f 46 which might use lots of memory.
Jan Jongboom 0:910f5949759f 47 */
Jan Jongboom 0:910f5949759f 48 HttpsRequest(NetworkInterface* net_iface,
Jan Jongboom 0:910f5949759f 49 const char* ssl_ca_pem,
Jan Jongboom 0:910f5949759f 50 http_method method,
Jan Jongboom 0:910f5949759f 51 const char* url,
Jan Jongboom 0:910f5949759f 52 Callback<void(const char *at, size_t length)> body_callback = 0)
Jan Jongboom 0:910f5949759f 53 {
Jan Jongboom 0:910f5949759f 54 _parsed_url = new ParsedUrl(url);
Jan Jongboom 0:910f5949759f 55 _body_callback = body_callback;
Jan Jongboom 0:910f5949759f 56 _request_builder = new HttpRequestBuilder(method, _parsed_url);
Jan Jongboom 0:910f5949759f 57 _response = NULL;
Jan Jongboom 0:910f5949759f 58 _debug = false;
Jan Jongboom 0:910f5949759f 59
Jan Jongboom 11:96e4dcb9c0c2 60 _tlssocket = new TLSSocket(net_iface, _parsed_url->host(), _parsed_url->port(), ssl_ca_pem);
Jan Jongboom 11:96e4dcb9c0c2 61 _we_created_the_socket = true;
Jan Jongboom 11:96e4dcb9c0c2 62 }
Jan Jongboom 0:910f5949759f 63
Jan Jongboom 11:96e4dcb9c0c2 64 /**
Jan Jongboom 11:96e4dcb9c0c2 65 * HttpsRequest Constructor
Jan Jongboom 11:96e4dcb9c0c2 66 * Sets up event handlers and flags.
Jan Jongboom 11:96e4dcb9c0c2 67 *
Jan Jongboom 11:96e4dcb9c0c2 68 * @param[in] socket A connected TLSSocket
Jan Jongboom 11:96e4dcb9c0c2 69 * @param[in] method HTTP method to use
Jan Jongboom 11:96e4dcb9c0c2 70 * @param[in] url URL to the resource
Jan Jongboom 11:96e4dcb9c0c2 71 * @param[in] body_callback Callback on which to retrieve chunks of the response body.
Jan Jongboom 11:96e4dcb9c0c2 72 If not set, the complete body will be allocated on the HttpResponse object,
Jan Jongboom 11:96e4dcb9c0c2 73 which might use lots of memory.
Jan Jongboom 11:96e4dcb9c0c2 74 */
Jan Jongboom 11:96e4dcb9c0c2 75 HttpsRequest(TLSSocket* socket,
Jan Jongboom 11:96e4dcb9c0c2 76 http_method method,
Jan Jongboom 11:96e4dcb9c0c2 77 const char* url,
Jan Jongboom 11:96e4dcb9c0c2 78 Callback<void(const char *at, size_t length)> body_callback = 0)
Jan Jongboom 11:96e4dcb9c0c2 79 {
Jan Jongboom 11:96e4dcb9c0c2 80 _parsed_url = new ParsedUrl(url);
Jan Jongboom 11:96e4dcb9c0c2 81 _body_callback = body_callback;
Jan Jongboom 11:96e4dcb9c0c2 82 _request_builder = new HttpRequestBuilder(method, _parsed_url);
Jan Jongboom 11:96e4dcb9c0c2 83 _response = NULL;
Jan Jongboom 11:96e4dcb9c0c2 84 _debug = false;
Jan Jongboom 11:96e4dcb9c0c2 85
Jan Jongboom 11:96e4dcb9c0c2 86 _tlssocket = socket;
Jan Jongboom 11:96e4dcb9c0c2 87 _we_created_the_socket = false;
Jan Jongboom 0:910f5949759f 88 }
Jan Jongboom 0:910f5949759f 89
Jan Jongboom 0:910f5949759f 90 /**
Jan Jongboom 0:910f5949759f 91 * HttpsRequest Destructor
Jan Jongboom 0:910f5949759f 92 */
Jan Jongboom 0:910f5949759f 93 ~HttpsRequest() {
Jan Jongboom 0:910f5949759f 94 if (_request_builder) {
Jan Jongboom 0:910f5949759f 95 delete _request_builder;
Jan Jongboom 0:910f5949759f 96 }
Jan Jongboom 0:910f5949759f 97
Jan Jongboom 11:96e4dcb9c0c2 98 if (_tlssocket && _we_created_the_socket) {
Jan Jongboom 11:96e4dcb9c0c2 99 delete _tlssocket;
Jan Jongboom 0:910f5949759f 100 }
Jan Jongboom 0:910f5949759f 101
Jan Jongboom 0:910f5949759f 102 if (_parsed_url) {
Jan Jongboom 0:910f5949759f 103 delete _parsed_url;
Jan Jongboom 0:910f5949759f 104 }
Jan Jongboom 0:910f5949759f 105
Jan Jongboom 0:910f5949759f 106 if (_response) {
Jan Jongboom 0:910f5949759f 107 delete _response;
Jan Jongboom 0:910f5949759f 108 }
Jan Jongboom 0:910f5949759f 109 }
Jan Jongboom 0:910f5949759f 110
Jan Jongboom 0:910f5949759f 111 /**
Jan Jongboom 0:910f5949759f 112 * Execute the HTTPS request.
Jan Jongboom 0:910f5949759f 113 *
Jan Jongboom 0:910f5949759f 114 * @param[in] body Pointer to the request body
Jan Jongboom 0:910f5949759f 115 * @param[in] body_size Size of the request body
Jan Jongboom 0:910f5949759f 116 * @return An HttpResponse pointer on success, or NULL on failure.
Jan Jongboom 0:910f5949759f 117 * See get_error() for the error code.
Jan Jongboom 0:910f5949759f 118 */
Jan Jongboom 0:910f5949759f 119 HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
Jan Jongboom 11:96e4dcb9c0c2 120 // not tried to connect before?
Jan Jongboom 11:96e4dcb9c0c2 121 if (_tlssocket->error() != 0) {
Jan Jongboom 11:96e4dcb9c0c2 122 _error = _tlssocket->error();
Jan Jongboom 0:910f5949759f 123 return NULL;
Jan Jongboom 0:910f5949759f 124 }
Jan Jongboom 0:910f5949759f 125
Jan Jongboom 11:96e4dcb9c0c2 126 bool socket_was_open = _tlssocket->connected();
Jan Jongboom 0:910f5949759f 127
Jan Jongboom 11:96e4dcb9c0c2 128 if (!socket_was_open) {
Jan Jongboom 11:96e4dcb9c0c2 129 nsapi_error_t r = _tlssocket->connect();
Jan Jongboom 11:96e4dcb9c0c2 130 if (r != 0) {
Jan Jongboom 11:96e4dcb9c0c2 131 _error = r;
Jan Jongboom 11:96e4dcb9c0c2 132 return NULL;
Jan Jongboom 11:96e4dcb9c0c2 133 }
Jan Jongboom 0:910f5949759f 134 }
Jan Jongboom 0:910f5949759f 135
Jan Jongboom 11:96e4dcb9c0c2 136 int ret;
Jan Jongboom 0:910f5949759f 137
Jan Jongboom 10:b017c7d2cf23 138 size_t request_size = 0;
Jan Jongboom 10:b017c7d2cf23 139 char* request = _request_builder->build(body, body_size, request_size);
Jan Jongboom 0:910f5949759f 140
Jan Jongboom 11:96e4dcb9c0c2 141 ret = mbedtls_ssl_write(_tlssocket->get_ssl_context(), (const unsigned char *) request, request_size);
Jan Jongboom 0:910f5949759f 142
Jan Jongboom 0:910f5949759f 143 free(request);
Jan Jongboom 0:910f5949759f 144
Jan Jongboom 0:910f5949759f 145 if (ret < 0) {
Jan Jongboom 0:910f5949759f 146 if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
Jan Jongboom 0:910f5949759f 147 ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
Jan Jongboom 0:910f5949759f 148 print_mbedtls_error("mbedtls_ssl_write", ret);
Jan Jongboom 11:96e4dcb9c0c2 149 onError(_tlssocket->get_tcp_socket(), -1 );
Jan Jongboom 0:910f5949759f 150 }
Jan Jongboom 0:910f5949759f 151 else {
Jan Jongboom 0:910f5949759f 152 _error = ret;
Jan Jongboom 0:910f5949759f 153 }
Jan Jongboom 0:910f5949759f 154 return NULL;
Jan Jongboom 0:910f5949759f 155 }
Jan Jongboom 0:910f5949759f 156
Jan Jongboom 0:910f5949759f 157 // Create a response object
Jan Jongboom 0:910f5949759f 158 _response = new HttpResponse();
Jan Jongboom 0:910f5949759f 159 // And a response parser
Jan Jongboom 0:910f5949759f 160 HttpResponseParser parser(_response, _body_callback);
Jan Jongboom 0:910f5949759f 161
Jan Jongboom 0:910f5949759f 162 // Set up a receive buffer (on the heap)
Jan Jongboom 0:910f5949759f 163 uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
Jan Jongboom 0:910f5949759f 164
Jan Jongboom 0:910f5949759f 165 /* Read data out of the socket */
Jan Jongboom 11:96e4dcb9c0c2 166 while ((ret = mbedtls_ssl_read(_tlssocket->get_ssl_context(), (unsigned char *) recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
Jan Jongboom 0:910f5949759f 167 // Don't know if this is actually needed, but OK
Jan Jongboom 0:910f5949759f 168 size_t _bpos = static_cast<size_t>(ret);
Jan Jongboom 0:910f5949759f 169 recv_buffer[_bpos] = 0;
Jan Jongboom 0:910f5949759f 170
Jan Jongboom 0:910f5949759f 171 size_t nparsed = parser.execute((const char*)recv_buffer, _bpos);
Jan Jongboom 0:910f5949759f 172 if (nparsed != _bpos) {
Jan Jongboom 0:910f5949759f 173 print_mbedtls_error("parser_error", nparsed);
Jan Jongboom 0:910f5949759f 174 // parser error...
Jan Jongboom 0:910f5949759f 175 _error = -2101;
Jan Jongboom 0:910f5949759f 176 free(recv_buffer);
Jan Jongboom 0:910f5949759f 177 return NULL;
Jan Jongboom 0:910f5949759f 178 }
Jan Jongboom 6:112d72c60e07 179
Jan Jongboom 7:2e3eedb9ca5c 180 if (_response->is_message_complete()) {
Jan Jongboom 0:910f5949759f 181 break;
Jan Jongboom 0:910f5949759f 182 }
Jan Jongboom 0:910f5949759f 183 }
Jan Jongboom 0:910f5949759f 184 if (ret < 0) {
Jan Jongboom 0:910f5949759f 185 if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
Jan Jongboom 0:910f5949759f 186 print_mbedtls_error("mbedtls_ssl_read", ret);
Jan Jongboom 11:96e4dcb9c0c2 187 onError(_tlssocket->get_tcp_socket(), -1 );
Jan Jongboom 0:910f5949759f 188 }
Jan Jongboom 0:910f5949759f 189 else {
Jan Jongboom 0:910f5949759f 190 _error = ret;
Jan Jongboom 0:910f5949759f 191 }
Jan Jongboom 0:910f5949759f 192 free(recv_buffer);
Jan Jongboom 0:910f5949759f 193 return NULL;
Jan Jongboom 0:910f5949759f 194 }
Jan Jongboom 0:910f5949759f 195
Jan Jongboom 0:910f5949759f 196 parser.finish();
Jan Jongboom 0:910f5949759f 197
Jan Jongboom 11:96e4dcb9c0c2 198 if (!socket_was_open) {
Jan Jongboom 11:96e4dcb9c0c2 199 _tlssocket->get_tcp_socket()->close();
Jan Jongboom 11:96e4dcb9c0c2 200 }
Jan Jongboom 11:96e4dcb9c0c2 201
Jan Jongboom 0:910f5949759f 202 free(recv_buffer);
Jan Jongboom 0:910f5949759f 203
Jan Jongboom 0:910f5949759f 204 return _response;
Jan Jongboom 0:910f5949759f 205 }
Jan Jongboom 0:910f5949759f 206
Jan Jongboom 0:910f5949759f 207 /**
Jan Jongboom 11:96e4dcb9c0c2 208 * Closes the underlying TCP socket
Jan Jongboom 0:910f5949759f 209 */
Jan Jongboom 0:910f5949759f 210 void close() {
Jan Jongboom 11:96e4dcb9c0c2 211 _tlssocket->get_tcp_socket()->close();
Jan Jongboom 0:910f5949759f 212 }
Jan Jongboom 0:910f5949759f 213
Jan Jongboom 0:910f5949759f 214 /**
Jan Jongboom 0:910f5949759f 215 * Set a header for the request.
Jan Jongboom 0:910f5949759f 216 *
Jan Jongboom 0:910f5949759f 217 * The 'Host' and 'Content-Length' headers are set automatically.
Jan Jongboom 0:910f5949759f 218 * Setting the same header twice will overwrite the previous entry.
Jan Jongboom 0:910f5949759f 219 *
Jan Jongboom 0:910f5949759f 220 * @param[in] key Header key
Jan Jongboom 0:910f5949759f 221 * @param[in] value Header value
Jan Jongboom 0:910f5949759f 222 */
Jan Jongboom 0:910f5949759f 223 void set_header(string key, string value) {
Jan Jongboom 0:910f5949759f 224 _request_builder->set_header(key, value);
Jan Jongboom 0:910f5949759f 225 }
Jan Jongboom 0:910f5949759f 226
Jan Jongboom 0:910f5949759f 227 /**
Jan Jongboom 0:910f5949759f 228 * Get the error code.
Jan Jongboom 0:910f5949759f 229 *
Jan Jongboom 0:910f5949759f 230 * When send() fails, this error is set.
Jan Jongboom 0:910f5949759f 231 */
Jan Jongboom 0:910f5949759f 232 nsapi_error_t get_error() {
Jan Jongboom 0:910f5949759f 233 return _error;
Jan Jongboom 0:910f5949759f 234 }
Jan Jongboom 0:910f5949759f 235
Jan Jongboom 0:910f5949759f 236 /**
Jan Jongboom 0:910f5949759f 237 * Set the debug flag.
Jan Jongboom 0:910f5949759f 238 *
Jan Jongboom 0:910f5949759f 239 * If this flag is set, debug information from mbed TLS will be logged to stdout.
Jan Jongboom 0:910f5949759f 240 */
Jan Jongboom 0:910f5949759f 241 void set_debug(bool debug) {
Jan Jongboom 0:910f5949759f 242 _debug = debug;
Jan Jongboom 11:96e4dcb9c0c2 243
Jan Jongboom 11:96e4dcb9c0c2 244 _tlssocket->set_debug(debug);
Jan Jongboom 0:910f5949759f 245 }
Jan Jongboom 0:910f5949759f 246
Jan Jongboom 11:96e4dcb9c0c2 247
Jan Jongboom 0:910f5949759f 248 protected:
Jan Jongboom 0:910f5949759f 249 /**
Jan Jongboom 0:910f5949759f 250 * Helper for pretty-printing mbed TLS error codes
Jan Jongboom 0:910f5949759f 251 */
Jan Jongboom 0:910f5949759f 252 static void print_mbedtls_error(const char *name, int err) {
Jan Jongboom 0:910f5949759f 253 char buf[128];
Jan Jongboom 0:910f5949759f 254 mbedtls_strerror(err, buf, sizeof (buf));
Jan Jongboom 0:910f5949759f 255 mbedtls_printf("%s() failed: -0x%04x (%d): %s\r\n", name, -err, err, buf);
Jan Jongboom 0:910f5949759f 256 }
Jan Jongboom 0:910f5949759f 257
Jan Jongboom 0:910f5949759f 258 void onError(TCPSocket *s, int error) {
Jan Jongboom 0:910f5949759f 259 s->close();
Jan Jongboom 0:910f5949759f 260 _error = error;
Jan Jongboom 0:910f5949759f 261 }
Jan Jongboom 0:910f5949759f 262
Jan Jongboom 0:910f5949759f 263 protected:
Jan Jongboom 11:96e4dcb9c0c2 264 TLSSocket* _tlssocket;
Jan Jongboom 11:96e4dcb9c0c2 265 bool _we_created_the_socket;
Jan Jongboom 0:910f5949759f 266
Jan Jongboom 0:910f5949759f 267 Callback<void(const char *at, size_t length)> _body_callback;
Jan Jongboom 0:910f5949759f 268 ParsedUrl* _parsed_url;
Jan Jongboom 0:910f5949759f 269 HttpRequestBuilder* _request_builder;
Jan Jongboom 0:910f5949759f 270 HttpResponse* _response;
Jan Jongboom 0:910f5949759f 271
Jan Jongboom 0:910f5949759f 272 nsapi_error_t _error;
Jan Jongboom 0:910f5949759f 273 bool _debug;
Jan Jongboom 0:910f5949759f 274
Jan Jongboom 0:910f5949759f 275 };
Jan Jongboom 0:910f5949759f 276
Jan Jongboom 0:910f5949759f 277 #endif // _MBED_HTTPS_REQUEST_H_