http

Committer:
reeml3
Date:
Mon Apr 15 18:12:24 2019 +0000
Revision:
1:ce6ccd14af4c
Parent:
0:a49e37a83a7a
http

Who changed what in which revision?

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