1 /* 2 Copyright Copyright (C) 2013 Andrey Uzunov 3 4 This program is free software: you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation, either version 3 of the License, or 7 (at your option) any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License 15 along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 /** 19 * @file mhd2spdy_http.c 20 * @brief HTTP part of the proxy. libmicrohttpd is used for the server side. 21 * @author Andrey Uzunov 22 */ 23 24 #include "mhd2spdy_structures.h" 25 #include "mhd2spdy_http.h" 26 #include "mhd2spdy_spdy.h" 27 28 29 void * 30 http_cb_log(void * cls, 31 const char * uri) 32 { 33 (void)cls; 34 35 struct HTTP_URI * http_uri; 36 37 PRINT_INFO2("log uri '%s'\n", uri); 38 39 //TODO not freed once in a while 40 if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI )))) 41 return NULL; 42 http_uri->uri = strdup(uri); 43 return http_uri; 44 } 45 46 47 static int 48 http_cb_iterate(void *cls, 49 enum MHD_ValueKind kind, 50 const char *name, 51 const char *value) 52 { 53 (void)kind; 54 55 static char * const forbidden[] = {"Transfer-Encoding", 56 "Proxy-Connection", 57 "Keep-Alive", 58 "Connection"}; 59 static int forbidden_size = 4; 60 int i; 61 struct SPDY_Headers *spdy_headers = (struct SPDY_Headers *)cls; 62 63 if(0 == strcasecmp(name, "Host")) 64 spdy_headers->nv[9] = (char *)value; 65 else 66 { 67 for(i=0; i<forbidden_size; ++i) 68 if(0 == strcasecmp(forbidden[i], name)) 69 return MHD_YES; 70 spdy_headers->nv[spdy_headers->cnt++] = (char *)name; 71 spdy_headers->nv[spdy_headers->cnt++] = (char *)value; 72 } 73 74 return MHD_YES; 75 } 76 77 78 static ssize_t 79 http_cb_response (void *cls, 80 uint64_t pos, 81 char *buffer, 82 size_t max) 83 { 84 (void)pos; 85 86 int ret; 87 struct Proxy *proxy = (struct Proxy *)cls; 88 void *newbody; 89 const union MHD_ConnectionInfo *info; 90 int val = 1; 91 92 PRINT_INFO2("http_cb_response for %s", proxy->url); 93 94 if(proxy->spdy_error) 95 return MHD_CONTENT_READER_END_WITH_ERROR; 96 97 if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active)) 98 { 99 PRINT_INFO("sent end of stream"); 100 return MHD_CONTENT_READER_END_OF_STREAM; 101 } 102 103 if(!proxy->http_body_size)//nothing to write now 104 { 105 //flush data 106 info = MHD_get_connection_info (proxy->http_connection, 107 MHD_CONNECTION_INFO_CONNECTION_FD); 108 ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); 109 if(ret == -1) { 110 DIE("setsockopt"); 111 } 112 113 PRINT_INFO("FLUSH data"); 114 return 0; 115 } 116 117 if(max >= proxy->http_body_size) 118 { 119 ret = proxy->http_body_size; 120 newbody = NULL; 121 } 122 else 123 { 124 ret = max; 125 if(NULL == (newbody = au_malloc(proxy->http_body_size - max))) 126 { 127 PRINT_INFO("no memory"); 128 return MHD_CONTENT_READER_END_WITH_ERROR; 129 } 130 memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max); 131 } 132 memcpy(buffer, proxy->http_body, ret); 133 free(proxy->http_body); 134 proxy->http_body = newbody; 135 proxy->http_body_size -= ret; 136 137 if(proxy->length >= 0) 138 { 139 proxy->length -= ret; 140 } 141 142 PRINT_INFO2("response_callback, size: %i",ret); 143 144 return ret; 145 } 146 147 148 static void 149 http_cb_response_done(void *cls) 150 { 151 (void)cls; 152 //TODO remove 153 } 154 155 int 156 http_cb_request (void *cls, 157 struct MHD_Connection *connection, 158 const char *url, 159 const char *method, 160 const char *version, 161 const char *upload_data, 162 size_t *upload_data_size, 163 void **ptr) 164 { 165 (void)cls; 166 (void)url; 167 (void)upload_data; 168 (void)upload_data_size; 169 170 int ret; 171 struct Proxy *proxy; 172 struct SPDY_Headers spdy_headers; 173 bool with_body = false; 174 struct HTTP_URI *http_uri; 175 const char *header_value; 176 177 if (NULL == ptr || NULL == *ptr) 178 return MHD_NO; 179 180 http_uri = (struct HTTP_URI *)*ptr; 181 182 if(NULL == http_uri->proxy) 183 { 184 //first call for this request 185 if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST)) 186 { 187 free(http_uri->uri); 188 free(http_uri); 189 PRINT_INFO2("unexpected method %s", method); 190 return MHD_NO; 191 } 192 193 if(NULL == (proxy = au_malloc(sizeof(struct Proxy)))) 194 { 195 free(http_uri->uri); 196 free(http_uri); 197 PRINT_INFO("No memory"); 198 return MHD_NO; 199 } 200 201 ++glob_opt.responses_pending; 202 proxy->id = rand(); 203 proxy->http_active = true; 204 proxy->http_connection = connection; 205 http_uri->proxy = proxy; 206 return MHD_YES; 207 } 208 209 proxy = http_uri->proxy; 210 211 if(proxy->spdy_error || proxy->http_error) 212 return MHD_NO; // handled at different place TODO? leaks? 213 214 if(proxy->spdy_active) 215 { 216 if(0 == strcmp (method, MHD_HTTP_METHOD_POST)) 217 { 218 PRINT_INFO("POST processing"); 219 220 int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id); 221 PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id); 222 proxy->spdy_connection->want_io |= WANT_WRITE; 223 224 if(0 == *upload_data_size) 225 { 226 PRINT_INFO("POST http EOF"); 227 proxy->receiving_done = true; 228 return MHD_YES; 229 } 230 231 if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size)) 232 { 233 //TODO handle it better? 234 PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); 235 return MHD_NO; 236 } 237 238 *upload_data_size = 0; 239 240 return MHD_YES; 241 } 242 243 //already handled 244 PRINT_INFO("unnecessary call to http_cb_request"); 245 return MHD_YES; 246 } 247 248 //second call for this request 249 250 PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version); 251 252 proxy->url = http_uri->uri; 253 254 header_value = MHD_lookup_connection_value(connection, 255 MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); 256 257 with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST) 258 && (NULL == header_value || 0 != strcmp ("0", header_value)); 259 260 PRINT_INFO2("body will be sent %i", with_body); 261 262 ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri); 263 if(ret != 0) 264 DIE("parse_uri failed"); 265 proxy->http_uri = http_uri; 266 267 spdy_headers.num = MHD_get_connection_values (connection, 268 MHD_HEADER_KIND, 269 NULL, 270 NULL); 271 if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *)))) 272 DIE("no memory"); 273 spdy_headers.nv[0] = ":method"; spdy_headers.nv[1] = method; 274 spdy_headers.nv[2] = ":path"; spdy_headers.nv[3] = proxy->uri->path_and_more; 275 spdy_headers.nv[4] = ":version"; spdy_headers.nv[5] = (char *)version; 276 spdy_headers.nv[6] = ":scheme"; spdy_headers.nv[7] = proxy->uri->scheme; 277 spdy_headers.nv[8] = ":host"; spdy_headers.nv[9] = NULL; 278 //nv[14] = NULL; 279 spdy_headers.cnt = 10; 280 MHD_get_connection_values (connection, 281 MHD_HEADER_KIND, 282 &http_cb_iterate, 283 &spdy_headers); 284 285 spdy_headers.nv[spdy_headers.cnt] = NULL; 286 if(NULL == spdy_headers.nv[9]) 287 spdy_headers.nv[9] = proxy->uri->host_and_port; 288 289 if(0 != spdy_request(spdy_headers.nv, proxy, with_body)) 290 { 291 free(spdy_headers.nv); 292 //free_proxy(proxy); 293 294 return MHD_NO; 295 } 296 free(spdy_headers.nv); 297 298 proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 299 4096, 300 &http_cb_response, 301 proxy, 302 &http_cb_response_done); 303 304 if (NULL == proxy->http_response) 305 DIE("no response"); 306 307 if(MHD_NO == MHD_add_response_header (proxy->http_response, 308 "Proxy-Connection", "keep-alive")) 309 PRINT_INFO("SPDY_name_value_add failed: "); 310 if(MHD_NO == MHD_add_response_header (proxy->http_response, 311 "Connection", "Keep-Alive")) 312 PRINT_INFO("SPDY_name_value_add failed: "); 313 if(MHD_NO == MHD_add_response_header (proxy->http_response, 314 "Keep-Alive", "timeout=5, max=100")) 315 PRINT_INFO("SPDY_name_value_add failed: "); 316 317 proxy->spdy_active = true; 318 319 return MHD_YES; 320 } 321 322 323 void 324 http_create_response(struct Proxy* proxy, 325 char **nv) 326 { 327 size_t i; 328 329 if(!proxy->http_active) 330 return; 331 332 for(i = 0; nv[i]; i += 2) { 333 if(0 == strcmp(":status", nv[i])) 334 { 335 char tmp[4]; 336 memcpy(&tmp,nv[i+1],3); 337 tmp[3]=0; 338 proxy->status = atoi(tmp); 339 continue; 340 } 341 else if(0 == strcmp(":version", nv[i])) 342 { 343 proxy->version = nv[i+1]; 344 continue; 345 } 346 else if(0 == strcmp("content-length", nv[i])) 347 { 348 continue; 349 } 350 351 char *header = *(nv+i); 352 if(MHD_NO == MHD_add_response_header (proxy->http_response, 353 header, nv[i+1])) 354 { 355 PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]); 356 } 357 PRINT_INFO2("adding '%s: %s'",header, nv[i+1]); 358 } 359 360 if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){ 361 PRINT_INFO("No queue"); 362 //TODO 363 //abort(); 364 proxy->http_error = true; 365 } 366 367 MHD_destroy_response (proxy->http_response); 368 proxy->http_response = NULL; 369 } 370 371 void 372 http_cb_request_completed (void *cls, 373 struct MHD_Connection *connection, 374 void **con_cls, 375 enum MHD_RequestTerminationCode toe) 376 { 377 (void)cls; 378 (void)connection; 379 struct HTTP_URI *http_uri; 380 struct Proxy *proxy; 381 382 http_uri = (struct HTTP_URI *)*con_cls; 383 if(NULL == http_uri) 384 return; 385 proxy = (struct Proxy *)http_uri->proxy; 386 assert(NULL != proxy); 387 388 PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id); 389 390 if(NULL != proxy->http_response) 391 { 392 MHD_destroy_response (proxy->http_response); 393 proxy->http_response = NULL; 394 } 395 396 if(proxy->spdy_active) 397 { 398 proxy->http_active = false; 399 if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe) 400 { 401 proxy->http_error = true; 402 if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/) 403 { 404 //send RST_STREAM_STATUS_CANCEL 405 PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id ); 406 spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5); 407 } 408 /*else 409 { 410 DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy); 411 free_proxy(proxy); 412 }*/ 413 } 414 } 415 else 416 { 417 PRINT_INFO2("proxy free http id %i ", proxy->id); 418 free_proxy(proxy); 419 } 420 421 --glob_opt.responses_pending; 422 } 423