HTTP and HTTPS library for Mbed OS 5

Dependents:   MQTTGateway2 MQTTGatewayK64 http-example-wnc GuardRoom ... more

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers http_request_base.h Source File

http_request_base.h

00001 /*
00002  * PackageLicenseDeclared: Apache-2.0
00003  * Copyright (c) 2017 ARM Limited
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #ifndef _HTTP_REQUEST_BASE_H_
00019 #define _HTTP_REQUEST_BASE_H_
00020 
00021 #include <map>
00022 #include <string>
00023 #include <vector>
00024 #include "mbed.h"
00025 #include "http_parser.h"
00026 #include "http_parsed_url.h"
00027 #include "http_request_builder.h"
00028 #include "http_request_parser.h"
00029 #include "http_response.h"
00030 #include "NetworkInterface.h"
00031 #include "netsocket/Socket.h"
00032 
00033 /**
00034  * @todo:
00035  *      - Userinfo parameter is not handled
00036  */
00037 
00038 #ifndef HTTP_RECEIVE_BUFFER_SIZE
00039 #define HTTP_RECEIVE_BUFFER_SIZE 8 * 1024
00040 #endif
00041 
00042 class HttpRequest;
00043 class HttpsRequest;
00044 
00045 /**
00046  * \brief HttpRequest implements the logic for interacting with HTTP servers.
00047  */
00048 class HttpRequestBase {
00049     friend class HttpRequest;
00050     friend class HttpsRequest;
00051 
00052 public:
00053     HttpRequestBase(Socket *socket, Callback<void(const char *at, uint32_t length)> bodyCallback)
00054         : _socket(socket), _body_callback(bodyCallback), _request_buffer(NULL), _request_buffer_ix(0)
00055     {}
00056 
00057     /**
00058      * HttpRequest Constructor
00059      */
00060     virtual ~HttpRequestBase() {
00061         // should response be owned by us? Or should user free it?
00062         // maybe implement copy constructor on response...
00063         if (_response) {
00064             delete _response;
00065         }
00066 
00067         if (_parsed_url) {
00068             delete _parsed_url;
00069         }
00070 
00071         if (_request_builder) {
00072             delete _request_builder;
00073         }
00074 
00075         if (_socket && _we_created_socket) {
00076             delete _socket;
00077         }
00078     }
00079 
00080     /**
00081      * Execute the request and receive the response.
00082      * This adds a Content-Length header to the request (when body_size is set), and sends the data to the server.
00083      * @param body Pointer to the body to be sent
00084      * @param body_size Size of the body to be sent
00085      * @return An HttpResponse pointer on success, or NULL on failure.
00086      *         See get_error() for the error code.
00087      */
00088     HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
00089         nsapi_size_or_error_t ret = connect_socket();
00090 
00091         if (ret != NSAPI_ERROR_OK) {
00092             _error = ret;
00093             return NULL;
00094         }
00095 
00096         _request_buffer_ix = 0;
00097 
00098         uint32_t request_size = 0;
00099         char* request = _request_builder->build(body, body_size, request_size);
00100 
00101         ret = send_buffer(request, request_size);
00102 
00103         free(request);
00104 
00105         if (ret < 0) {
00106             _error = ret;
00107             return NULL;
00108         }
00109 
00110         return create_http_response();
00111     }
00112 
00113     /**
00114      * Execute the request and receive the response.
00115      * This sends the request through chunked-encoding.
00116      * @param body_cb Callback which generates the next chunk of the request
00117      * @return An HttpResponse pointer on success, or NULL on failure.
00118      *         See get_error() for the error code.
00119      */
00120     HttpResponse* send(Callback<const void*(uint32_t*)> body_cb) {
00121 
00122         nsapi_error_t ret;
00123 
00124         if ((ret = connect_socket()) != NSAPI_ERROR_OK) {
00125             _error = ret;
00126             return NULL;
00127         }
00128 
00129         _request_buffer_ix = 0;
00130 
00131         set_header("Transfer-Encoding", "chunked");
00132 
00133         uint32_t request_size = 0;
00134         char* request = _request_builder->build(NULL, 0, request_size);
00135 
00136         // first... send this request headers without the body
00137         nsapi_size_or_error_t total_send_count = send_buffer(request, request_size);
00138 
00139         if (total_send_count < 0) {
00140             free(request);
00141             _error = total_send_count;
00142             return NULL;
00143         }
00144 
00145         // ok... now it's time to start sending chunks...
00146         while (1) {
00147             uint32_t size;
00148             const void *buffer = body_cb(&size);
00149 
00150             if (size == 0) break;
00151 
00152             // so... size in HEX, \r\n, data, \r\n again
00153             char size_buff[10]; // if sending length of more than 8 digits, you have another problem on a microcontroller...
00154             int size_buff_size = sprintf(size_buff, "%X\r\n", static_cast<size_t>(size));
00155             if ((total_send_count = send_buffer(size_buff, static_cast<uint32_t>(size_buff_size))) < 0) {
00156                 free(request);
00157                 _error = total_send_count;
00158                 return NULL;
00159             }
00160 
00161             // now send the normal buffer... and then \r\n at the end
00162             total_send_count = send_buffer((char*)buffer, size);
00163             if (total_send_count < 0) {
00164                 free(request);
00165                 _error = total_send_count;
00166                 return NULL;
00167             }
00168 
00169             // and... \r\n
00170             const char* rn = "\r\n";
00171             if ((total_send_count = send_buffer((char*)rn, 2)) < 0) {
00172                 free(request);
00173                 _error = total_send_count;
00174                 return NULL;
00175             }
00176         }
00177 
00178         // finalize...?
00179         const char* fin = "0\r\n\r\n";
00180         if ((total_send_count = send_buffer((char*)fin, strlen(fin))) < 0) {
00181             free(request);
00182             _error = total_send_count;
00183             return NULL;
00184         }
00185 
00186         free(request);
00187 
00188         return create_http_response();
00189     }
00190 
00191     /**
00192      * Set a header for the request.
00193      *
00194      * The 'Host', 'Content-Length', and (optionally) 'Transfer-Encoding: chunked'
00195      * headers are set automatically.
00196      * Setting the same header twice will overwrite the previous entry.
00197      *
00198      * @param key Header key
00199      * @param value Header value
00200      */
00201     void set_header(string key, string value) {
00202         _request_builder->set_header(key, value);
00203     }
00204 
00205     /**
00206      * Get the error code.
00207      *
00208      * When send() fails, this error is set.
00209      */
00210     nsapi_error_t get_error() {
00211         return _error;
00212     }
00213 
00214     /**
00215      * Set the request log buffer, all bytes that are sent for this request are logged here.
00216      * If the buffer would overflow logging is stopped.
00217      *
00218      * @param buffer Pointer to a buffer to store the data in
00219      * @param buffer_size Size of the buffer
00220      */
00221     void set_request_log_buffer(uint8_t *buffer, size_t buffer_size) {
00222         _request_buffer = buffer;
00223         _request_buffer_size = buffer_size;
00224         _request_buffer_ix = 0;
00225     }
00226 
00227     /**
00228      * Get the number of bytes written to the request log buffer, since the last request.
00229      * If no request was sent, or if the request log buffer is NULL, then this returns 0.
00230      */
00231     size_t get_request_log_buffer_length() {
00232         return _request_buffer_ix;
00233     }
00234 
00235 protected:
00236     virtual nsapi_error_t connect_socket(char *host, uint16_t port) = 0;
00237 
00238 private:
00239     nsapi_error_t connect_socket( ) {
00240         if (_response != NULL) {
00241             // already executed this response
00242             return -2100; // @todo, make a lookup table with errors
00243         }
00244 
00245 
00246         if (_we_created_socket) {
00247             nsapi_error_t connection_result = connect_socket(_parsed_url->host(), _parsed_url->port());
00248             if (connection_result != NSAPI_ERROR_OK) {
00249                 return connection_result;
00250             }
00251         }
00252 
00253         return NSAPI_ERROR_OK;
00254     }
00255 
00256     nsapi_size_or_error_t send_buffer(char* buffer, uint32_t buffer_size) {
00257         nsapi_size_or_error_t total_send_count = 0;
00258         while (total_send_count < buffer_size) {
00259 
00260             // get a slice of the buffer
00261             char *buffer_slice = buffer + total_send_count;
00262             uint32_t buffer_slice_size = buffer_size - total_send_count;
00263 
00264             // if request buffer was set, copy it there
00265             if (_request_buffer != NULL && _request_buffer_ix + buffer_slice_size < _request_buffer_size) {
00266                 memcpy(_request_buffer + _request_buffer_ix, buffer_slice, buffer_slice_size);
00267                 _request_buffer_ix += buffer_slice_size;
00268             }
00269 
00270             nsapi_size_or_error_t send_result = _socket->send(buffer_slice, buffer_slice_size);
00271 
00272             if (send_result < 0) {
00273                 total_send_count = send_result;
00274                 break;
00275             }
00276 
00277             if (send_result == 0) {
00278                 break;
00279             }
00280 
00281             total_send_count += send_result;
00282         }
00283 
00284         return total_send_count;
00285     }
00286 
00287     HttpResponse* create_http_response() {
00288         // Create a response object
00289         _response = new HttpResponse();
00290         // And a response parser
00291         HttpParser parser(_response, HTTP_RESPONSE, _body_callback);
00292 
00293         // Set up a receive buffer (on the heap)
00294         uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
00295 
00296         // Socket::recv is called until we don't have any data anymore
00297         nsapi_size_or_error_t recv_ret;
00298         while ((recv_ret = _socket->recv(recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
00299 
00300             // Pass the chunk into the http_parser
00301             uint32_t nparsed = parser.execute((const char*)recv_buffer, recv_ret);
00302             if (nparsed != recv_ret) {
00303                 // printf("Parsing failed... parsed %d bytes, received %d bytes\n", nparsed, recv_ret);
00304                 _error = -2101;
00305                 free(recv_buffer);
00306                 return NULL;
00307             }
00308 
00309             if (_response->is_message_complete()) {
00310                 break;
00311             }
00312         }
00313         // error?
00314         if (recv_ret < 0) {
00315             _error = recv_ret;
00316             free(recv_buffer);
00317             return NULL;
00318         }
00319 
00320         // When done, call parser.finish()
00321         parser.finish();
00322 
00323         // Free the receive buffer
00324         free(recv_buffer);
00325 
00326         if (_we_created_socket) {
00327             // Close the socket
00328             _socket->close();
00329         }
00330 
00331         return _response;
00332     }
00333 
00334 private:
00335     Socket* _socket;
00336     Callback<void(const char *at, uint32_t length)> _body_callback;
00337 
00338     ParsedUrl* _parsed_url;
00339 
00340     HttpRequestBuilder* _request_builder;
00341     HttpResponse* _response;
00342 
00343     bool _we_created_socket;
00344 
00345     nsapi_error_t _error;
00346 
00347     uint8_t *_request_buffer;
00348     size_t _request_buffer_size;
00349     size_t _request_buffer_ix;
00350 };
00351 
00352 #endif // _HTTP_REQUEST_BASE_H_