http

Committer:
DuaaAbusharkh
Date:
Mon Apr 15 08:30:22 2019 +0000
Revision:
0:a49e37a83a7a
blink

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