Fork of mbed-http

Fork of mbed-http by sandbox

Committer:
Matthew Else
Date:
Wed Mar 29 17:26:14 2017 +0100
Revision:
14:5f9acddaa0a4
Parent:
13:efe5c8b16dab
Tidy up chunked detection

Who changed what in which revision?

UserRevisionLine numberNew 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 _HTTP_REQUEST_
Jan Jongboom 0:910f5949759f 19 #define _HTTP_REQUEST_
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 0:910f5949759f 29
Jan Jongboom 0:910f5949759f 30 /**
Jan Jongboom 0:910f5949759f 31 * @todo:
Jan Jongboom 0:910f5949759f 32 * - Userinfo parameter is not handled
Jan Jongboom 0:910f5949759f 33 */
Jan Jongboom 0:910f5949759f 34
Jan Jongboom 0:910f5949759f 35
Jan Jongboom 0:910f5949759f 36 /**
Jan Jongboom 0:910f5949759f 37 * \brief HttpRequest implements the logic for interacting with HTTPS servers.
Jan Jongboom 0:910f5949759f 38 */
Jan Jongboom 0:910f5949759f 39 class HttpRequest {
Jan Jongboom 0:910f5949759f 40 public:
Jan Jongboom 0:910f5949759f 41 /**
Jan Jongboom 0:910f5949759f 42 * HttpRequest Constructor
Jan Jongboom 0:910f5949759f 43 *
Jan Jongboom 0:910f5949759f 44 * @param[in] aNetwork The network interface
Jan Jongboom 0:910f5949759f 45 * @param[in] aMethod HTTP method to use
Jan Jongboom 0:910f5949759f 46 * @param[in] url URL to the resource
Jan Jongboom 0:910f5949759f 47 * @param[in] aBodyCallback Callback on which to retrieve chunks of the response body.
Jan Jongboom 0:910f5949759f 48 If not set, the complete body will be allocated on the HttpResponse object,
Jan Jongboom 0:910f5949759f 49 which might use lots of memory.
Jan Jongboom 0:910f5949759f 50 */
Jan Jongboom 0:910f5949759f 51 HttpRequest(NetworkInterface* aNetwork, http_method aMethod, const char* url, Callback<void(const char *at, size_t length)> aBodyCallback = 0)
Jan Jongboom 0:910f5949759f 52 : network(aNetwork), method(aMethod), body_callback(aBodyCallback)
Jan Jongboom 0:910f5949759f 53 {
Jan Jongboom 0:910f5949759f 54 error = 0;
Jan Jongboom 0:910f5949759f 55 response = NULL;
Jan Jongboom 0:910f5949759f 56
Jan Jongboom 0:910f5949759f 57 parsed_url = new ParsedUrl(url);
Jan Jongboom 0:910f5949759f 58 request_builder = new HttpRequestBuilder(method, parsed_url);
Jan Jongboom 11:96e4dcb9c0c2 59
Jan Jongboom 11:96e4dcb9c0c2 60 socket = new TCPSocket();
Jan Jongboom 11:96e4dcb9c0c2 61 we_created_socket = true;
Jan Jongboom 11:96e4dcb9c0c2 62 }
Jan Jongboom 11:96e4dcb9c0c2 63
Jan Jongboom 11:96e4dcb9c0c2 64 /**
Jan Jongboom 11:96e4dcb9c0c2 65 * HttpRequest Constructor
Jan Jongboom 11:96e4dcb9c0c2 66 *
Jan Jongboom 11:96e4dcb9c0c2 67 * @param[in] aSocket An open TCPSocket
Jan Jongboom 11:96e4dcb9c0c2 68 * @param[in] aMethod HTTP method to use
Jan Jongboom 11:96e4dcb9c0c2 69 * @param[in] url URL to the resource
Jan Jongboom 11:96e4dcb9c0c2 70 * @param[in] aBodyCallback Callback on which to retrieve chunks of the response body.
Jan Jongboom 11:96e4dcb9c0c2 71 If not set, the complete body will be allocated on the HttpResponse object,
Jan Jongboom 11:96e4dcb9c0c2 72 which might use lots of memory.
Jan Jongboom 11:96e4dcb9c0c2 73 */
Jan Jongboom 11:96e4dcb9c0c2 74 HttpRequest(TCPSocket* aSocket, http_method aMethod, const char* url, Callback<void(const char *at, size_t length)> aBodyCallback = 0)
Jan Jongboom 11:96e4dcb9c0c2 75 : socket(aSocket), method(aMethod), body_callback(aBodyCallback)
Jan Jongboom 11:96e4dcb9c0c2 76 {
Jan Jongboom 11:96e4dcb9c0c2 77 error = 0;
Jan Jongboom 11:96e4dcb9c0c2 78 response = NULL;
Jan Jongboom 11:96e4dcb9c0c2 79 network = NULL;
Jan Jongboom 11:96e4dcb9c0c2 80
Jan Jongboom 11:96e4dcb9c0c2 81 parsed_url = new ParsedUrl(url);
Jan Jongboom 11:96e4dcb9c0c2 82 request_builder = new HttpRequestBuilder(method, parsed_url);
Jan Jongboom 11:96e4dcb9c0c2 83
Jan Jongboom 11:96e4dcb9c0c2 84 we_created_socket = false;
Jan Jongboom 0:910f5949759f 85 }
Jan Jongboom 0:910f5949759f 86
Jan Jongboom 0:910f5949759f 87 /**
Jan Jongboom 0:910f5949759f 88 * HttpRequest Constructor
Jan Jongboom 0:910f5949759f 89 */
Jan Jongboom 0:910f5949759f 90 ~HttpRequest() {
Jan Jongboom 0:910f5949759f 91 // should response be owned by us? Or should user free it?
Jan Jongboom 0:910f5949759f 92 // maybe implement copy constructor on response...
Jan Jongboom 0:910f5949759f 93 if (response) {
Jan Jongboom 0:910f5949759f 94 delete response;
Jan Jongboom 0:910f5949759f 95 }
Jan Jongboom 0:910f5949759f 96
Jan Jongboom 0:910f5949759f 97 if (parsed_url) {
Jan Jongboom 0:910f5949759f 98 delete parsed_url;
Jan Jongboom 0:910f5949759f 99 }
Jan Jongboom 0:910f5949759f 100
Jan Jongboom 0:910f5949759f 101 if (request_builder) {
Jan Jongboom 0:910f5949759f 102 delete request_builder;
Jan Jongboom 0:910f5949759f 103 }
Jan Jongboom 11:96e4dcb9c0c2 104
Jan Jongboom 11:96e4dcb9c0c2 105 if (socket && we_created_socket) {
Jan Jongboom 11:96e4dcb9c0c2 106 delete socket;
Jan Jongboom 11:96e4dcb9c0c2 107 }
Jan Jongboom 0:910f5949759f 108 }
Jan Jongboom 0:910f5949759f 109
Jan Jongboom 0:910f5949759f 110 /**
Jan Jongboom 0:910f5949759f 111 * Execute the request and receive the response.
Jan Jongboom 0:910f5949759f 112 */
Jan Jongboom 0:910f5949759f 113 HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
Jan Jongboom 0:910f5949759f 114 if (response != NULL) {
Jan Jongboom 0:910f5949759f 115 // already executed this response
Jan Jongboom 0:910f5949759f 116 error = -2100; // @todo, make a lookup table with errors
Jan Jongboom 0:910f5949759f 117 return NULL;
Jan Jongboom 0:910f5949759f 118 }
Jan Jongboom 0:910f5949759f 119
Jan Jongboom 0:910f5949759f 120 error = 0;
Jan Jongboom 0:910f5949759f 121
Jan Jongboom 11:96e4dcb9c0c2 122 if (we_created_socket) {
Jan Jongboom 11:96e4dcb9c0c2 123 nsapi_error_t open_result = socket->open(network);
Jan Jongboom 11:96e4dcb9c0c2 124 if (open_result != 0) {
Jan Jongboom 11:96e4dcb9c0c2 125 error = open_result;
Jan Jongboom 11:96e4dcb9c0c2 126 return NULL;
Jan Jongboom 11:96e4dcb9c0c2 127 }
Jan Jongboom 0:910f5949759f 128
Jan Jongboom 11:96e4dcb9c0c2 129 nsapi_error_t connection_result = socket->connect(parsed_url->host(), parsed_url->port());
Jan Jongboom 11:96e4dcb9c0c2 130 if (connection_result != 0) {
Jan Jongboom 11:96e4dcb9c0c2 131 error = connection_result;
Jan Jongboom 11:96e4dcb9c0c2 132 return NULL;
Jan Jongboom 11:96e4dcb9c0c2 133 }
Jan Jongboom 0:910f5949759f 134 }
Jan Jongboom 0:910f5949759f 135
Jan Jongboom 10:b017c7d2cf23 136 size_t request_size = 0;
Jan Jongboom 10:b017c7d2cf23 137 char* request = request_builder->build(body, body_size, request_size);
Jan Jongboom 0:910f5949759f 138
Jan Jongboom 11:96e4dcb9c0c2 139 nsapi_size_or_error_t send_result = socket->send(request, request_size);
Jan Jongboom 0:910f5949759f 140
Jan Jongboom 0:910f5949759f 141 free(request);
Jan Jongboom 0:910f5949759f 142
Jan Jongboom 0:910f5949759f 143 if (send_result != request_size) {
Jan Jongboom 0:910f5949759f 144 error = send_result;
Jan Jongboom 0:910f5949759f 145 return NULL;
Jan Jongboom 0:910f5949759f 146 }
Jan Jongboom 0:910f5949759f 147
Jan Jongboom 0:910f5949759f 148 // Create a response object
Jan Jongboom 0:910f5949759f 149 response = new HttpResponse();
Matthew Else 13:efe5c8b16dab 150 // response->set_chunked();
Jan Jongboom 0:910f5949759f 151 // And a response parser
Jan Jongboom 0:910f5949759f 152 HttpResponseParser parser(response, body_callback);
Jan Jongboom 0:910f5949759f 153
Jan Jongboom 0:910f5949759f 154 // Set up a receive buffer (on the heap)
Jan Jongboom 0:910f5949759f 155 uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
Jan Jongboom 0:910f5949759f 156
Jan Jongboom 0:910f5949759f 157 // TCPSocket::recv is called until we don't have any data anymore
Jan Jongboom 0:910f5949759f 158 nsapi_size_or_error_t recv_ret;
Jan Jongboom 11:96e4dcb9c0c2 159 while ((recv_ret = socket->recv(recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
Jan Jongboom 3:8a6b003e3874 160
Jan Jongboom 0:910f5949759f 161 // Pass the chunk into the http_parser
Jan Jongboom 0:910f5949759f 162 size_t nparsed = parser.execute((const char*)recv_buffer, recv_ret);
Jan Jongboom 0:910f5949759f 163 if (nparsed != recv_ret) {
Jan Jongboom 0:910f5949759f 164 // printf("Parsing failed... parsed %d bytes, received %d bytes\n", nparsed, recv_ret);
Jan Jongboom 0:910f5949759f 165 error = -2101;
Jan Jongboom 0:910f5949759f 166 free(recv_buffer);
Jan Jongboom 0:910f5949759f 167 return NULL;
Jan Jongboom 0:910f5949759f 168 }
Jan Jongboom 3:8a6b003e3874 169
Matthew Else 14:5f9acddaa0a4 170 // if we don't get a content-length field in the first chunk, assume it's chunked.
Matthew Else 14:5f9acddaa0a4 171 if (!response->get_have_content_length()) {
Matthew Else 13:efe5c8b16dab 172 response->set_chunked();
Matthew Else 13:efe5c8b16dab 173 }
Matthew Else 13:efe5c8b16dab 174
Jan Jongboom 7:2e3eedb9ca5c 175 if (response->is_message_complete()) {
Jan Jongboom 0:910f5949759f 176 break;
Jan Jongboom 0:910f5949759f 177 }
Jan Jongboom 0:910f5949759f 178 }
Jan Jongboom 0:910f5949759f 179 // error?
Jan Jongboom 0:910f5949759f 180 if (recv_ret < 0) {
Jan Jongboom 0:910f5949759f 181 error = recv_ret;
Jan Jongboom 0:910f5949759f 182 free(recv_buffer);
Jan Jongboom 0:910f5949759f 183 return NULL;
Jan Jongboom 0:910f5949759f 184 }
Jan Jongboom 0:910f5949759f 185
Jan Jongboom 0:910f5949759f 186 // When done, call parser.finish()
Jan Jongboom 0:910f5949759f 187 parser.finish();
Jan Jongboom 0:910f5949759f 188
Jan Jongboom 0:910f5949759f 189 // Free the receive buffer
Jan Jongboom 0:910f5949759f 190 free(recv_buffer);
Jan Jongboom 0:910f5949759f 191
Jan Jongboom 11:96e4dcb9c0c2 192 if (we_created_socket) {
Jan Jongboom 11:96e4dcb9c0c2 193 // Close the socket
Jan Jongboom 11:96e4dcb9c0c2 194 socket->close();
Jan Jongboom 11:96e4dcb9c0c2 195 }
Jan Jongboom 0:910f5949759f 196
Jan Jongboom 0:910f5949759f 197 return response;
Jan Jongboom 0:910f5949759f 198 }
Jan Jongboom 0:910f5949759f 199
Jan Jongboom 0:910f5949759f 200 /**
Jan Jongboom 0:910f5949759f 201 * Set a header for the request.
Jan Jongboom 0:910f5949759f 202 *
Jan Jongboom 0:910f5949759f 203 * The 'Host' and 'Content-Length' headers are set automatically.
Jan Jongboom 0:910f5949759f 204 * Setting the same header twice will overwrite the previous entry.
Jan Jongboom 0:910f5949759f 205 *
Jan Jongboom 0:910f5949759f 206 * @param[in] key Header key
Jan Jongboom 0:910f5949759f 207 * @param[in] value Header value
Jan Jongboom 0:910f5949759f 208 */
Jan Jongboom 0:910f5949759f 209 void set_header(string key, string value) {
Jan Jongboom 0:910f5949759f 210 request_builder->set_header(key, value);
Jan Jongboom 0:910f5949759f 211 }
Jan Jongboom 0:910f5949759f 212
Jan Jongboom 0:910f5949759f 213 /**
Jan Jongboom 0:910f5949759f 214 * Get the error code.
Jan Jongboom 0:910f5949759f 215 *
Jan Jongboom 0:910f5949759f 216 * When send() fails, this error is set.
Jan Jongboom 0:910f5949759f 217 */
Jan Jongboom 0:910f5949759f 218 nsapi_error_t get_error() {
Jan Jongboom 0:910f5949759f 219 return error;
Jan Jongboom 0:910f5949759f 220 }
Jan Jongboom 0:910f5949759f 221
Jan Jongboom 0:910f5949759f 222 private:
Jan Jongboom 0:910f5949759f 223 NetworkInterface* network;
Jan Jongboom 11:96e4dcb9c0c2 224 TCPSocket* socket;
Jan Jongboom 0:910f5949759f 225 http_method method;
Jan Jongboom 0:910f5949759f 226 Callback<void(const char *at, size_t length)> body_callback;
Jan Jongboom 0:910f5949759f 227
Jan Jongboom 0:910f5949759f 228 ParsedUrl* parsed_url;
Jan Jongboom 0:910f5949759f 229
Jan Jongboom 0:910f5949759f 230 HttpRequestBuilder* request_builder;
Jan Jongboom 0:910f5949759f 231 HttpResponse* response;
Jan Jongboom 0:910f5949759f 232
Jan Jongboom 11:96e4dcb9c0c2 233 bool we_created_socket;
Jan Jongboom 11:96e4dcb9c0c2 234
Jan Jongboom 0:910f5949759f 235 nsapi_error_t error;
Jan Jongboom 0:910f5949759f 236 };
Jan Jongboom 0:910f5949759f 237
Jan Jongboom 0:910f5949759f 238 #endif // _HTTP_REQUEST_