Library for Firebase, PUT, PATCH, POST, GET, DELETE operations supported, (others are available, todo). Based on Mbed's https-example. Tested on STM32F767 using ETHERNET and ESP8266 WIFI interfaces and STM32F446 using ESP8266 WIFI interface.

Dependents:   Firebase-Example

Committer:
star297
Date:
Thu Jan 23 22:18:11 2020 +0000
Revision:
0:768ae9838086
Initial version.

Who changed what in which revision?

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