Fork of mbed-http

Fork of mbed-http by sandbox

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers http_request.h Source File

http_request.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_
00019 #define _HTTP_REQUEST_
00020 
00021 #include <string>
00022 #include <vector>
00023 #include <map>
00024 #include "http_parser.h"
00025 #include "http_response.h"
00026 #include "http_request_builder.h"
00027 #include "http_response_parser.h"
00028 #include "http_parsed_url.h"
00029 
00030 /**
00031  * @todo:
00032  *      - Userinfo parameter is not handled
00033  */
00034 
00035 
00036 /**
00037  * \brief HttpRequest implements the logic for interacting with HTTPS servers.
00038  */
00039 class HttpRequest {
00040 public:
00041     /**
00042      * HttpRequest Constructor
00043      *
00044      * @param[in] aNetwork The network interface
00045      * @param[in] aMethod HTTP method to use
00046      * @param[in] url URL to the resource
00047      * @param[in] aBodyCallback Callback on which to retrieve chunks of the response body.
00048                                 If not set, the complete body will be allocated on the HttpResponse object,
00049                                 which might use lots of memory.
00050     */
00051     HttpRequest(NetworkInterface* aNetwork, http_method aMethod, const char* url, Callback<void(const char *at, size_t length)> aBodyCallback = 0)
00052         : network(aNetwork), method(aMethod), body_callback(aBodyCallback)
00053     {
00054         error = 0;
00055         response = NULL;
00056 
00057         parsed_url = new ParsedUrl(url);
00058         request_builder = new HttpRequestBuilder(method, parsed_url);
00059 
00060         socket = new TCPSocket();
00061         we_created_socket = true;
00062     }
00063 
00064     /**
00065      * HttpRequest Constructor
00066      *
00067      * @param[in] aSocket An open TCPSocket
00068      * @param[in] aMethod HTTP method to use
00069      * @param[in] url URL to the resource
00070      * @param[in] aBodyCallback Callback on which to retrieve chunks of the response body.
00071                                 If not set, the complete body will be allocated on the HttpResponse object,
00072                                 which might use lots of memory.
00073     */
00074     HttpRequest(TCPSocket* aSocket, http_method aMethod, const char* url, Callback<void(const char *at, size_t length)> aBodyCallback = 0)
00075         : socket(aSocket), method(aMethod), body_callback(aBodyCallback)
00076     {
00077         error = 0;
00078         response = NULL;
00079         network = NULL;
00080 
00081         parsed_url = new ParsedUrl(url);
00082         request_builder = new HttpRequestBuilder(method, parsed_url);
00083 
00084         we_created_socket = false;
00085     }
00086 
00087     /**
00088      * HttpRequest Constructor
00089      */
00090     ~HttpRequest() {
00091         // should response be owned by us? Or should user free it?
00092         // maybe implement copy constructor on response...
00093         if (response) {
00094             delete response;
00095         }
00096 
00097         if (parsed_url) {
00098             delete parsed_url;
00099         }
00100 
00101         if (request_builder) {
00102             delete request_builder;
00103         }
00104 
00105         if (socket && we_created_socket) {
00106             delete socket;
00107         }
00108     }
00109 
00110     /**
00111      * Execute the request and receive the response.
00112      */
00113     HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
00114         if (response != NULL) {
00115             // already executed this response
00116             error = -2100; // @todo, make a lookup table with errors
00117             return NULL;
00118         }
00119 
00120         error = 0;
00121 
00122         if (we_created_socket) {
00123             nsapi_error_t open_result = socket->open(network);
00124             if (open_result != 0) {
00125                 error = open_result;
00126                 return NULL;
00127             }
00128 
00129             nsapi_error_t connection_result = socket->connect(parsed_url->host(), parsed_url->port());
00130             if (connection_result != 0) {
00131                 error = connection_result;
00132                 return NULL;
00133             }
00134         }
00135 
00136         size_t request_size = 0;
00137         char* request = request_builder->build(body, body_size, request_size);
00138 
00139         nsapi_size_or_error_t send_result = socket->send(request, request_size);
00140 
00141         free(request);
00142 
00143         if (send_result != request_size) {
00144             error = send_result;
00145             return NULL;
00146         }
00147 
00148         // Create a response object
00149         response = new HttpResponse();
00150         // response->set_chunked();
00151         // And a response parser
00152         HttpResponseParser parser(response, body_callback);
00153 
00154         // Set up a receive buffer (on the heap)
00155         uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
00156 
00157         // TCPSocket::recv is called until we don't have any data anymore
00158         nsapi_size_or_error_t recv_ret;
00159         while ((recv_ret = socket->recv(recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
00160 
00161             // Pass the chunk into the http_parser
00162             size_t nparsed = parser.execute((const char*)recv_buffer, recv_ret);
00163             if (nparsed != recv_ret) {
00164                 // printf("Parsing failed... parsed %d bytes, received %d bytes\n", nparsed, recv_ret);
00165                 error = -2101;
00166                 free(recv_buffer);
00167                 return NULL;
00168             }
00169 
00170             // if we don't get a content-length field in the first chunk, assume it's chunked.
00171             if (!response->get_have_content_length()) {
00172                 response->set_chunked();
00173             }
00174 
00175             if (response->is_message_complete()) {
00176                 break;
00177             }
00178         }
00179         // error?
00180         if (recv_ret < 0) {
00181             error = recv_ret;
00182             free(recv_buffer);
00183             return NULL;
00184         }
00185 
00186         // When done, call parser.finish()
00187         parser.finish();
00188 
00189         // Free the receive buffer
00190         free(recv_buffer);
00191 
00192         if (we_created_socket) {
00193             // Close the socket
00194             socket->close();
00195         }
00196 
00197         return response;
00198     }
00199 
00200     /**
00201      * Set a header for the request.
00202      *
00203      * The 'Host' and 'Content-Length' headers are set automatically.
00204      * Setting the same header twice will overwrite the previous entry.
00205      *
00206      * @param[in] key Header key
00207      * @param[in] value Header value
00208      */
00209     void set_header(string key, string value) {
00210         request_builder->set_header(key, value);
00211     }
00212 
00213     /**
00214      * Get the error code.
00215      *
00216      * When send() fails, this error is set.
00217      */
00218     nsapi_error_t get_error() {
00219         return error;
00220     }
00221 
00222 private:
00223     NetworkInterface* network;
00224     TCPSocket* socket;
00225     http_method method;
00226     Callback<void(const char *at, size_t length)> body_callback;
00227 
00228     ParsedUrl* parsed_url;
00229 
00230     HttpRequestBuilder* request_builder;
00231     HttpResponse* response;
00232 
00233     bool we_created_socket;
00234 
00235     nsapi_error_t error;
00236 };
00237 
00238 #endif // _HTTP_REQUEST_