Fork of mbed-http
Fork of mbed-http by
source/https_request.h@11:96e4dcb9c0c2, 2017-03-28 (annotated)
- 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?
User | Revision | Line number | New 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_ |