aboutsummaryrefslogtreecommitdiff
path: root/src/spdy2http
diff options
context:
space:
mode:
Diffstat (limited to 'src/spdy2http')
-rw-r--r--src/spdy2http/Makefile.am35
-rw-r--r--src/spdy2http/proxy.c625
2 files changed, 660 insertions, 0 deletions
diff --git a/src/spdy2http/Makefile.am b/src/spdy2http/Makefile.am
new file mode 100644
index 00000000..a8f2536f
--- /dev/null
+++ b/src/spdy2http/Makefile.am
@@ -0,0 +1,35 @@
1SUBDIRS = .
2
3AM_CFLAGS = -DDATADIR=\"$(top_srcdir)/src/datadir/\"
4
5if USE_COVERAGE
6 AM_CFLAGS += -fprofile-arcs -ftest-coverage
7endif
8
9if USE_PRIVATE_PLIBC_H
10 PLIBC_INCLUDE = -I$(top_srcdir)/src/include/plibc
11endif
12
13AM_CPPFLAGS = \
14 $(PLIBC_INCLUDE) \
15 -I$(top_srcdir) \
16 -I$(top_srcdir)/src/include \
17 -I$(top_srcdir)/src/applicationlayer \
18$(LIBCURL_CPPFLAGS)
19
20if !HAVE_W32
21PERF_GET_CONCURRENT=perf_get_concurrent
22endif
23
24bin_PROGRAMS = \
25 microspdy2http
26
27microspdy2http_SOURCES = \
28 proxy.c
29microspdy2http_LDADD = \
30 $(top_builddir)/src/microspdy/libmicrospdy.la \
31 -lssl \
32 -lcrypto \
33 -lz \
34 -ldl \
35 -lcurl
diff --git a/src/spdy2http/proxy.c b/src/spdy2http/proxy.c
new file mode 100644
index 00000000..f1513680
--- /dev/null
+++ b/src/spdy2http/proxy.c
@@ -0,0 +1,625 @@
1/*
2 This file is part of libmicrospdy
3 Copyright (C) 2013 Andrey Uzunov
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file proxy.c
21 * @brief Translates incoming SPDY requests to http server on localhost.
22 * Uses libcurl.
23 * @author Andrey Uzunov
24 */
25
26#include "platform.h"
27#include <unistd.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <stdbool.h>
31#include <string.h>
32#include <stdio.h>
33#include <ctype.h>
34#include <errno.h>
35#include "microspdy.h"
36#include <curl/curl.h>
37#include <assert.h>
38
39
40#define PRINT_INFO(msg) do{\
41 printf("%i:%s\n", __LINE__, msg);\
42 fflush(stdout);\
43 }\
44 while(0)
45
46
47#define PRINT_INFO2(fmt, ...) do{\
48 printf("%i\n", __LINE__);\
49 printf(fmt,##__VA_ARGS__);\
50 printf("\n");\
51 fflush(stdout);\
52 }\
53 while(0)
54
55
56#define CURL_SETOPT(handle, opt, val) do{\
57 int ret; \
58 if(CURLE_OK != (ret = curl_easy_setopt(handle, opt, val))) \
59 { \
60 PRINT_INFO2("curl_easy_setopt failed (%i = %i)", opt, ret); \
61 abort(); \
62 } \
63 }\
64 while(0)
65
66
67int run = 1;
68char* http_host;
69CURLM *multi_handle;
70int still_running = 0; /* keep number of running handles */
71int http10=0;
72
73struct Proxy
74{
75 char *path;
76 struct SPDY_Request *request;
77 struct SPDY_Response *response;
78 CURL *curl_handle;
79 struct curl_slist *curl_headers;
80 struct SPDY_NameValue *headers;
81 char *version;
82 char *status_msg;
83 void *http_body;
84 size_t http_body_size;
85 ssize_t length;
86 int status;
87};
88
89
90ssize_t
91response_callback (void *cls,
92 void *buffer,
93 size_t max,
94 bool *more)
95{
96 int ret;
97 struct Proxy *proxy = (struct Proxy *)cls;
98 void *newbody;
99
100 //printf("response_callback\n");
101
102 assert(0 != proxy->length);
103
104 *more = true;
105 if(!proxy->http_body_size)//nothing to write now
106 return 0;
107
108 if(max >= proxy->http_body_size)
109 {
110 ret = proxy->http_body_size;
111 newbody = NULL;
112 }
113 else
114 {
115 ret = max;
116 if(NULL == (newbody = malloc(proxy->http_body_size - max)))
117 {
118 PRINT_INFO("no memory");
119 return -1;
120 }
121 memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max);
122 }
123 memcpy(buffer, proxy->http_body, ret);
124 free(proxy->http_body);
125 proxy->http_body = newbody;
126 proxy->http_body_size -= ret;
127
128 if(proxy->length >= 0)
129 {
130 proxy->length -= ret;
131 //printf("pr len %i", proxy->length);
132 if(proxy->length <= 0)
133 {
134 *more = false;
135 //last frame
136 proxy->length = 0;
137 }
138 }
139
140 return ret;
141}
142
143
144void
145response_done_callback(void *cls,
146 struct SPDY_Response *response,
147 struct SPDY_Request *request,
148 enum SPDY_RESPONSE_RESULT status,
149 bool streamopened)
150{
151 (void)streamopened;
152 struct Proxy *proxy = (struct Proxy *)cls;
153 int ret;
154
155 //printf("response_done_callback\n");
156
157 //printf("answer for %s was sent\n", (char *)cls);
158
159 if(SPDY_RESPONSE_RESULT_SUCCESS != status)
160 {
161 printf("answer was NOT sent, %i\n",status);
162 }
163 if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle)))
164 {
165 PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret);
166 }
167 curl_slist_free_all(proxy->curl_headers);
168 curl_easy_cleanup(proxy->curl_handle);
169
170 SPDY_destroy_request(request);
171 SPDY_destroy_response(response);
172 if(!strcmp("/close",proxy->path)) run = 0;
173 free(proxy->path);
174 free(proxy);
175}
176
177
178static size_t
179curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp)
180{
181 size_t realsize = size * nmemb;
182 struct Proxy *proxy = (struct Proxy *)userp;
183 char *line = (char *)ptr;
184 char *name;
185 char *value;
186 char *status;
187 const char *const*length;
188 int i;
189 int pos;
190
191 //printf("curl_header_cb\n");
192
193 if('\r' == line[0])
194 {
195 //all headers were already handled; prepare spdy frames
196 if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status,
197 proxy->status_msg,
198 proxy->version,
199 proxy->headers,
200 &response_callback,
201 proxy,
202 0)))
203 {
204 PRINT_INFO("no response");
205 abort();
206 }
207 if(NULL != (length = SPDY_name_value_lookup(proxy->headers,
208 SPDY_HTTP_HEADER_CONTENT_LENGTH,
209 &i)))
210 proxy->length = atoi(length[0]);
211 else
212 proxy->length = -1;
213 SPDY_name_value_destroy(proxy->headers);
214 free(proxy->status_msg);
215 free(proxy->version);
216
217 if(SPDY_YES != SPDY_queue_response(proxy->request,
218 proxy->response,
219 true,
220 false,
221 &response_done_callback,
222 proxy))
223 {
224 PRINT_INFO("no queue");
225 abort();
226 }
227 //printf("spdy headers queued %i\n");
228
229 return realsize;
230 }
231
232 pos = 0;
233 if(NULL == proxy->version)
234 {
235 //first line from headers
236 //version
237 for(i=pos; i<realsize && ' '!=line[i]; ++i);
238 if(i == realsize)
239 {
240 PRINT_INFO("error on parsing headers");
241 abort();
242 }
243 if(NULL == (proxy->version = strndup(line, i - pos)))
244 {
245 PRINT_INFO("no memory");
246 abort();
247 }
248 pos = i+1;
249
250 //status (number)
251 for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i]; ++i);
252 if(NULL == (status = strndup(&(line[pos]), i - pos)))
253 {
254 PRINT_INFO("no memory");
255 abort();
256 }
257 proxy->status = atoi(status);
258 free(status);
259 if(i<realsize && '\r'!=line[i])
260 {
261 //status (message)
262 pos = i+1;
263 for(i=pos; i<realsize && '\r'!=line[i]; ++i);
264 if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos)))
265 {
266 PRINT_INFO("no memory");
267 abort();
268 }
269 }
270 return realsize;
271 }
272
273 //other lines
274 //header name
275 for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i]; ++i)
276 line[i] = tolower(line[i]); //spdy requires lower case
277 if(NULL == (name = strndup(line, i - pos)))
278 {
279 PRINT_INFO("no memory");
280 abort();
281 }
282 if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name)
283 || 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name))
284 {
285 //forbidden in spdy, ignore
286 free(name);
287 return realsize;
288 }
289 if(i == realsize || '\r'==line[i])
290 {
291 //no value. is it possible?
292 if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, ""))
293 {
294 PRINT_INFO("SPDY_name_value_add failed");
295 abort();
296 }
297 return realsize;
298 }
299
300 //header value
301 pos = i+1;
302 while(pos<realsize && isspace(line[pos])) ++pos; //remove leading space
303 for(i=pos; i<realsize && '\r'!=line[i]; ++i);
304 if(NULL == (value = strndup(&(line[pos]), i - pos)))
305 {
306 PRINT_INFO("no memory");
307 abort();
308 }
309 if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, value))
310 {
311 PRINT_INFO("SPDY_name_value_add failed");
312 abort();
313 }
314 free(name);
315 free(value);
316
317 return realsize;
318}
319
320
321static size_t
322curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
323{
324 size_t realsize = size * nmemb;
325 struct Proxy *proxy = (struct Proxy *)userp;
326
327 //printf("curl_write_cb %i\n", realsize);
328
329 if(NULL == proxy->http_body)
330 proxy->http_body = malloc(realsize);
331 else
332 proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + realsize);
333 if(NULL == proxy->http_body)
334 {
335 PRINT_INFO("not enough memory (realloc returned NULL)");
336 return 0;
337 }
338
339 memcpy(proxy->http_body + proxy->http_body_size, contents, realsize);
340 proxy->http_body_size += realsize;
341
342 return realsize;
343}
344
345
346int
347iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
348{
349 struct Proxy *proxy = (struct Proxy *)cls;
350 struct curl_slist **curl_headers = (&(proxy->curl_headers));
351 char *line;
352 int line_len = strlen(name) + 3; //+ ": \0"
353 int i;
354
355 for(i=0; i<num_values; ++i)
356 {
357 if(i) line_len += 2; //", "
358 line_len += strlen(value[i]);
359 }
360
361 if(NULL == (line = malloc(line_len)))
362 {
363 //no recovory
364 PRINT_INFO("no memory");
365 abort();
366 }
367 line[0] = 0;
368
369 strcat(line, name);
370 strcat(line, ": ");
371 //all spdy header names are lower case;
372 //for simplicity here we just capitalize the first letter
373 line[0] = toupper(line[0]);
374
375 for(i=0; i<num_values; ++i)
376 {
377 if(i) strcat(line, ", ");
378 strcat(line, value[i]);
379 }
380 if(NULL == (*curl_headers = curl_slist_append(*curl_headers, line)))
381 {
382 PRINT_INFO("curl_slist_append failed");
383 abort();
384 }
385 free(line);
386
387 return SPDY_YES;
388}
389
390
391void
392standard_request_handler(void *cls,
393 struct SPDY_Request * request,
394 uint8_t priority,
395 const char *method,
396 const char *path,
397 const char *version,
398 const char *host,
399 const char *scheme,
400 struct SPDY_NameValue * headers)
401{
402 (void)cls;
403 (void)priority;
404 (void)host;
405 (void)scheme;
406
407 char *url;
408 struct Proxy *proxy;
409 int ret;
410
411 //printf("received request for '%s %s %s'\n", method, path, version);
412 if(NULL == (proxy = malloc(sizeof(struct Proxy))))
413 {
414 PRINT_INFO("No memory");
415 abort();
416 }
417 memset(proxy, 0, sizeof(struct Proxy));
418 proxy->request = request;
419 if(NULL == (proxy->headers = SPDY_name_value_create()))
420 {
421 PRINT_INFO("No memory");
422 abort();
423 }
424
425 if(-1 == asprintf(&url,"%s%s%s","http://", http_host, path))
426 {
427 PRINT_INFO("No memory");
428 abort();
429 }
430
431 if(NULL == (proxy->path = strdup(path)))
432 {
433 PRINT_INFO("No memory");
434 abort();
435 }
436
437 SPDY_name_value_iterate(headers, &iterate_cb, proxy);
438
439 if(NULL == (proxy->curl_handle = curl_easy_init()))
440 {
441 PRINT_INFO("curl_easy_init failed");
442 abort();
443 }
444
445 //CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1);
446 CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, url);
447 free(url);
448 if(http10)
449 CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
450 CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
451 CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy);
452 CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
453 CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy);
454 CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers);
455 CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
456 CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
457
458 if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle)))
459 {
460 PRINT_INFO2("curl_multi_add_handle failed (%i)", ret);
461 abort();
462 }
463
464 if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
465 && CURLM_CALL_MULTI_PERFORM != ret)
466 {
467 PRINT_INFO2("curl_multi_perform failed (%i)", ret);
468 abort();
469 }
470}
471
472int
473main (int argc, char *const *argv)
474{
475 unsigned long long timeoutlong=0;
476 long curl_timeo = -1;
477 struct timeval timeout;
478 int ret;
479 fd_set rs;
480 fd_set ws;
481 fd_set es;
482 fd_set curl_rs;
483 fd_set curl_ws;
484 fd_set curl_es;
485 int maxfd = -1;
486 struct SPDY_Daemon *daemon;
487
488 //signal(SIGPIPE, SIG_IGN);
489
490 if(argc != 6)
491 {
492 printf("Usage: %s cert-file key-file host port http/1.0(yes/no)\n", argv[0]);
493 return 1;
494 }
495
496 SPDY_init();
497
498 daemon = SPDY_start_daemon(atoi(argv[4]),
499 argv[1],
500 argv[2],
501 NULL,
502 NULL,
503 &standard_request_handler,
504 NULL,
505 NULL,
506 SPDY_DAEMON_OPTION_SESSION_TIMEOUT,
507 1800,
508 SPDY_DAEMON_OPTION_END);
509
510 if(NULL==daemon){
511 printf("no daemon\n");
512 return 1;
513 }
514
515 multi_handle = curl_multi_init();
516
517 if(NULL==multi_handle){
518 PRINT_INFO("no multi_handle");
519 abort();
520 }
521
522 if(!strcmp("yes", argv[5]))
523 http10 = 1;
524
525 http_host = argv[3];
526 timeout.tv_usec = 0;
527
528 do
529 {
530 //printf("still %i\n", still_running);
531 FD_ZERO(&rs);
532 FD_ZERO(&ws);
533 FD_ZERO(&es);
534 FD_ZERO(&curl_rs);
535 FD_ZERO(&curl_ws);
536 FD_ZERO(&curl_es);
537
538 if(still_running > 0)
539 timeout.tv_sec = 0; //return immediately
540 else
541 {
542 ret = SPDY_get_timeout(daemon, &timeoutlong);
543 if(SPDY_NO == ret)
544 timeout.tv_sec = 1;
545 else
546 timeout.tv_sec = timeoutlong;
547 }
548 timeout.tv_usec = 0;
549
550 maxfd = SPDY_get_fdset (daemon,
551 &rs,
552 &ws,
553 &es);
554 assert(-1 != maxfd);
555
556 ret = select(maxfd+1, &rs, &ws, &es, &timeout);
557
558 switch(ret) {
559 case -1:
560 PRINT_INFO2("select error: %i", errno);
561 break;
562 case 0:
563 break;
564 default:
565 SPDY_run(daemon);
566 break;
567 }
568
569 timeout.tv_sec = 0;
570 if(still_running > 0)
571 {
572 if(CURLM_OK != (ret = curl_multi_timeout(multi_handle, &curl_timeo)))
573 {
574 PRINT_INFO2("curl_multi_timeout failed (%i)", ret);
575 abort();
576 }
577 if(curl_timeo >= 0 && curl_timeo < 500)
578 timeout.tv_usec = curl_timeo * 1000;
579 else
580 timeout.tv_usec = 500000;
581 }
582 else continue;
583 //else timeout.tv_usec = 500000;
584
585 if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &curl_rs, &curl_ws, &curl_es, &maxfd)))
586 {
587 PRINT_INFO2("curl_multi_fdset failed (%i)", ret);
588 abort();
589 }
590 if(-1 == maxfd)
591 {
592 PRINT_INFO("maxfd is -1");
593 //continue;
594 ret = 0;
595 }
596 else
597 ret = select(maxfd+1, &curl_rs, &curl_ws, &curl_es, &timeout);
598
599 switch(ret) {
600 case -1:
601 PRINT_INFO2("select error: %i", errno);
602 break;
603 case 0: /* timeout */
604 //break or not
605 default: /* action */
606 if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
607 && CURLM_CALL_MULTI_PERFORM != ret)
608 {
609 PRINT_INFO2("curl_multi_perform failed (%i)", ret);
610 abort();
611 }
612 break;
613 }
614 }
615 while(run);
616
617 curl_multi_cleanup(multi_handle);
618
619 SPDY_stop_daemon(daemon);
620
621 SPDY_deinit();
622
623 return 0;
624}
625