GNU libmicrohttpd  0.9.29
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
proxy.c
Go to the documentation of this file.
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 
38 #include "platform.h"
39 #include <unistd.h>
40 #include <stdlib.h>
41 #include <stdint.h>
42 #include <stdbool.h>
43 #include <string.h>
44 #include <stdio.h>
45 #include <ctype.h>
46 #include <errno.h>
47 #include "microspdy.h"
48 #include <curl/curl.h>
49 #include <assert.h>
50 #include <getopt.h>
51 #include <regex.h>
52 
53 #define ERROR_RESPONSE "502 Bad Gateway"
54 
55 
56 struct global_options
57 {
58  char *http_backend;
59  char *cert;
60  char *cert_key;
61  char *listen_host;
62  unsigned int timeout;
63  uint16_t listen_port;
64  bool verbose;
65  bool curl_verbose;
66  bool transparent;
67  bool http10;
68  bool notls;
69  bool nodelay;
70  bool ipv4;
71  bool ipv6;
72 } glob_opt;
73 
74 
75 struct URI
76 {
77  char * full_uri;
78  char * scheme;
79  char * host_and_port;
80  //char * host_and_port_for_connecting;
81  char * host;
82  char * path;
83  char * path_and_more;
84  char * query;
85  char * fragment;
86  uint16_t port;
87 };
88 
89 
90 #define PRINT_INFO(msg) do{\
91  fprintf(stdout, "%i:%s\n", __LINE__, msg);\
92  fflush(stdout);\
93  }\
94  while(0)
95 
96 
97 #define PRINT_INFO2(fmt, ...) do{\
98  fprintf(stdout, "%i\n", __LINE__);\
99  fprintf(stdout, fmt,##__VA_ARGS__);\
100  fprintf(stdout, "\n");\
101  fflush(stdout);\
102  }\
103  while(0)
104 
105 
106 #define PRINT_VERBOSE(msg) do{\
107  if(glob_opt.verbose){\
108  fprintf(stdout, "%i:%s\n", __LINE__, msg);\
109  fflush(stdout);\
110  }\
111  }\
112  while(0)
113 
114 
115 #define PRINT_VERBOSE2(fmt, ...) do{\
116  if(glob_opt.verbose){\
117  fprintf(stdout, "%i\n", __LINE__);\
118  fprintf(stdout, fmt,##__VA_ARGS__);\
119  fprintf(stdout, "\n");\
120  fflush(stdout);\
121  }\
122  }\
123  while(0)
124 
125 
126 #define CURL_SETOPT(handle, opt, val) do{\
127  int ret; \
128  if(CURLE_OK != (ret = curl_easy_setopt(handle, opt, val))) \
129  { \
130  PRINT_INFO2("curl_easy_setopt failed (%i = %i)", opt, ret); \
131  abort(); \
132  } \
133  }\
134  while(0)
135 
136 
137 #define DIE(msg) do{\
138  printf("FATAL ERROR (line %i): %s\n", __LINE__, msg);\
139  fflush(stdout);\
140  exit(EXIT_FAILURE);\
141  }\
142  while(0)
143 
144 
145 static int loop = 1;
146 
147 static CURLM *multi_handle;
148 
149 static int still_running = 0; /* keep number of running handles */
150 
151 static regex_t uri_preg;
152 
153 static bool call_spdy_run;
154 static bool call_curl_run;
155 
157 
158 
159 struct Proxy
160 {
161  char *url;
162  struct SPDY_Request *request;
163  struct SPDY_Response *response;
164  CURL *curl_handle;
165  struct curl_slist *curl_headers;
166  struct SPDY_NameValue *headers;
167  char *version;
168  char *status_msg;
169  void *http_body;
170  void *received_body;
171  bool *session_alive;
172  size_t http_body_size;
173  size_t received_body_size;
174  //ssize_t length;
175  int status;
176  //bool done;
177  bool receiving_done;
178  bool is_curl_read_paused;
179  bool is_with_body_data;
180  //bool error;
181  bool curl_done;
182  bool curl_error;
183  bool spdy_done;
184  bool spdy_error;
185 };
186 
187 
188 static void
189 free_uri(struct URI * uri)
190 {
191  if(NULL != uri)
192  {
193  free(uri->full_uri);
194  free(uri->scheme);
195  free(uri->host_and_port);
196  //free(uri->host_and_port_for_connecting);
197  free(uri->host);
198  free(uri->path);
199  free(uri->path_and_more);
200  free(uri->query);
201  free(uri->fragment);
202  uri->port = 0;
203  free(uri);
204  }
205 }
206 
207 
208 static int
209 init_parse_uri(regex_t * preg)
210 {
211  // RFC 2396
212  // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
213  /*
214  scheme = $2
215  authority = $4
216  path = $5
217  query = $7
218  fragment = $9
219  */
220 
221  return regcomp(preg, "^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?", REG_EXTENDED);
222 }
223 
224 
225 static void
226 deinit_parse_uri(regex_t * preg)
227 {
228  regfree(preg);
229 }
230 
231 
232 static int
233 parse_uri(regex_t * preg, const char * full_uri, struct URI ** uri)
234 {
235  //TODO memeory checks
236  int ret;
237  char *colon;
238  long long port;
239  size_t nmatch = 10;
240  regmatch_t pmatch[10];
241 
242  if (0 != (ret = regexec(preg, full_uri, nmatch, pmatch, 0)))
243  return ret;
244 
245  *uri = malloc(sizeof(struct URI));
246  if(NULL == *uri)
247  return -200;
248 
249  (*uri)->full_uri = strdup(full_uri);
250 
251  asprintf(&((*uri)->scheme),
252  "%.*s",
253  (int) (pmatch[2].rm_eo - pmatch[2].rm_so),
254  &full_uri[pmatch[2].rm_so]);
255  asprintf(&((*uri)->host_and_port), "%.*s",
256  (int) (pmatch[4].rm_eo - pmatch[4].rm_so),
257  &full_uri[pmatch[4].rm_so]);
258  asprintf(&((*uri)->path),
259  "%.*s",
260  (int) (pmatch[5].rm_eo - pmatch[5].rm_so),
261  &full_uri[pmatch[5].rm_so]);
262  asprintf(&((*uri)->path_and_more),
263  "%.*s",
264  (int) (pmatch[9].rm_eo - pmatch[5].rm_so),
265  &full_uri[pmatch[5].rm_so]);
266  asprintf(&((*uri)->query),
267  "%.*s",
268  (int) (pmatch[7].rm_eo - pmatch[7].rm_so),
269  &full_uri[pmatch[7].rm_so]);
270  asprintf(&((*uri)->fragment),
271  "%.*s",
272  (int) (pmatch[9].rm_eo - pmatch[9].rm_so),
273  &full_uri[pmatch[9].rm_so]);
274 
275  colon = strrchr((*uri)->host_and_port, ':');
276  if(NULL == colon)
277  {
278  (*uri)->host = strdup((*uri)->host_and_port);
279  /*if(0 == strcasecmp("http", uri->scheme))
280  {
281  uri->port = 80;
282  asprintf(&(uri->host_and_port_for_connecting), "%s:80", uri->host_and_port);
283  }
284  else if(0 == strcasecmp("https", uri->scheme))
285  {
286  uri->port = 443;
287  asprintf(&(uri->host_and_port_for_connecting), "%s:443", uri->host_and_port);
288  }
289  else
290  {
291  PRINT_INFO("no standard scheme!");
292  */(*uri)->port = 0;
293  /*uri->host_and_port_for_connecting = strdup(uri->host_and_port);
294  }*/
295  return 0;
296  }
297 
298  port = atoi(colon + 1);
299  if(port<1 || port >= 256 * 256)
300  {
301  free_uri(*uri);
302  return -100;
303  }
304  (*uri)->port = port;
305  asprintf(&((*uri)->host), "%.*s", (int)(colon - (*uri)->host_and_port), (*uri)->host_and_port);
306 
307  return 0;
308 }
309 
310 
311 static bool
312 store_in_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
313 {
314  if(0 == src_size)
315  return true;
316 
317  if(NULL == *dst)
318  *dst = malloc(src_size);
319  else
320  *dst = realloc(*dst, src_size + *dst_size);
321  if(NULL == *dst)
322  return false;
323 
324  memcpy(*dst + *dst_size, src, src_size);
325  *dst_size += src_size;
326 
327  return true;
328 }
329 
330 
331 static ssize_t
332 get_from_buffer(void **src, size_t *src_size, void *dst, size_t max_size)
333 {
334  size_t ret;
335  void *newbody;
336 
337  if(max_size >= *src_size)
338  {
339  ret = *src_size;
340  newbody = NULL;
341  }
342  else
343  {
344  ret = max_size;
345  if(NULL == (newbody = malloc(*src_size - max_size)))
346  return -1;
347  memcpy(newbody, *src + ret, *src_size - ret);
348  }
349  memcpy(dst, *src, ret);
350  free(*src);
351  *src = newbody;
352  *src_size -= ret;
353 
354  return ret;
355 }
356 
357 
358 static void
359 catch_signal(int signal)
360 {
361  (void)signal;
362 
363  loop = 0;
364 }
365 
366 static void
367 new_session_cb (void * cls,
368  struct SPDY_Session * session)
369 {
370  (void)cls;
371 
372  bool *session_alive;
373 
374  PRINT_VERBOSE("new session");
375  //TODO clean this memory
376  if(NULL == (session_alive = malloc(sizeof(bool))))
377  {
378  DIE("no memory");
379  }
380  *session_alive = true;
381  SPDY_set_cls_to_session(session,
382  session_alive);
383 }
384 
385 static void
386 session_closed_cb (void * cls,
387  struct SPDY_Session * session,
388  int by_client)
389 {
390  (void)cls;
391 
392  bool *session_alive;
393 
394  PRINT_VERBOSE2("session closed; by client: %i", by_client);
395 
396  session_alive = SPDY_get_cls_from_session(session);
397  assert(NULL != session_alive);
398 
399  *session_alive = false;
400 }
401 
402 
403 static int
404 spdy_post_data_cb (void * cls,
405  struct SPDY_Request *request,
406  const void * buf,
407  size_t size,
408  bool more)
409 {
410  (void)cls;
411  int ret;
412  struct Proxy *proxy = (struct Proxy *)SPDY_get_cls_from_request(request);
413 
414  if(!store_in_buffer(buf, size, &proxy->received_body, &proxy->received_body_size))
415  {
416  PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
417  return 0;
418  }
419 
420  proxy->receiving_done = !more;
421 
422  PRINT_VERBOSE2("POST bytes from SPDY: %zu", size);
423 
424  call_curl_run = true;
425 
426  if(proxy->is_curl_read_paused)
427  {
428  if(CURLE_OK != (ret = curl_easy_pause(proxy->curl_handle, CURLPAUSE_CONT)))
429  {
430  PRINT_INFO2("curl_easy_pause returned %i", ret);
431  abort();
432  }
433  PRINT_VERBOSE("curl_read_cb pause resumed");
434  }
435 
436  return SPDY_YES;
437 }
438 
439 
440 ssize_t
441 response_callback (void *cls,
442  void *buffer,
443  size_t max,
444  bool *more)
445 {
446  ssize_t ret;
447  struct Proxy *proxy = (struct Proxy *)cls;
448 
449  *more = true;
450 
451  assert(!proxy->spdy_error);
452 
453  if(proxy->curl_error)
454  {
455  PRINT_VERBOSE("tell spdy about the error");
456  return -1;
457  }
458 
459  if(!proxy->http_body_size)//nothing to write now
460  {
461  PRINT_VERBOSE("nothing to write now");
462  if(proxy->curl_done || proxy->curl_error) *more = false;
463  return 0;
464  }
465 
466  ret = get_from_buffer(&(proxy->http_body), &(proxy->http_body_size), buffer, max);
467  if(ret < 0)
468  {
469  PRINT_INFO("no memory");
470  //TODO error?
471  return -1;
472  }
473 
474  if((proxy->curl_done || proxy->curl_error) && 0 == proxy->http_body_size) *more = false;
475 
476  PRINT_VERBOSE2("given bytes to microspdy: %zd", ret);
477 
478  return ret;
479 }
480 
481 
482 static void
483 cleanup(struct Proxy *proxy)
484 {
485  int ret;
486 
487  //fprintf(stderr, "free proxy for %s\n", proxy->url);
488 
489  if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle)))
490  {
491  PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret);
492  DIE("bug in cleanup");
493  }
494  debug_num_curls--;
495  //TODO bug on ku6.com or amazon.cn
496  // after curl_multi_remove_handle returned CURLM_BAD_EASY_HANDLE
497  curl_slist_free_all(proxy->curl_headers);
498  curl_easy_cleanup(proxy->curl_handle);
499 
500  free(proxy->url);
501  free(proxy);
502 }
503 
504 
505 static void
507  struct SPDY_Response *response,
508  struct SPDY_Request *request,
509  enum SPDY_RESPONSE_RESULT status,
510  bool streamopened)
511 {
512  (void)streamopened;
513  struct Proxy *proxy = (struct Proxy *)cls;
514 
515  if(SPDY_RESPONSE_RESULT_SUCCESS != status)
516  {
517  free(proxy->http_body);
518  proxy->http_body = NULL;
519  proxy->spdy_error = true;
520  }
521  cleanup(proxy);
522  SPDY_destroy_request(request);
523  SPDY_destroy_response(response);
524 }
525 
526 
527 static size_t
528 curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp)
529 {
530  size_t realsize = size * nmemb;
531  struct Proxy *proxy = (struct Proxy *)userp;
532  char *line = (char *)ptr;
533  char *name;
534  char *value;
535  char *status;
536  unsigned int i;
537  unsigned int pos;
538  int ret;
539  int num_values;
540  const char * const * values;
541  bool abort_it;
542 
543  //printf("curl_header_cb %s\n", line);
544  if(!*(proxy->session_alive))
545  {
546  PRINT_VERBOSE("headers received, but session is dead");
547  proxy->spdy_error = true;
548  proxy->curl_error = true;
549  return 0;
550  }
551 
552  //trailer
553  if(NULL != proxy->response) return 0;
554 
555  if('\r' == line[0] || '\n' == line[0])
556  {
557  //all headers were already handled; prepare spdy frames
558  if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status,
559  proxy->status_msg,
560  proxy->version,
561  proxy->headers,
563  proxy,
564  0)))
565  //256)))
566  DIE("no response");
567 
568  SPDY_name_value_destroy(proxy->headers);
569  proxy->headers = NULL;
570  free(proxy->status_msg);
571  proxy->status_msg = NULL;
572  free(proxy->version);
573  proxy->version = NULL;
574 
575  if(SPDY_YES != SPDY_queue_response(proxy->request,
576  proxy->response,
577  true,
578  false,
580  proxy))
581  {
582  //DIE("no queue");
583  //TODO right?
584  proxy->spdy_error = true;
585  proxy->curl_error = true;
586  PRINT_VERBOSE2("no queue in curl_header_cb for %s", proxy->url);
587  SPDY_destroy_response(proxy->response);
588  proxy->response = NULL;
589  return 0;
590  }
591 
592  call_spdy_run = true;
593 
594  return realsize;
595  }
596 
597  pos = 0;
598  if(NULL == proxy->version)
599  {
600  //first line from headers
601  //version
602  for(i=pos; i<realsize && ' '!=line[i]; ++i);
603  if(i == realsize)
604  DIE("error on parsing headers");
605  if(NULL == (proxy->version = strndup(line, i - pos)))
606  DIE("No memory");
607  pos = i+1;
608 
609  //status (number)
610  for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i);
611  if(NULL == (status = strndup(&(line[pos]), i - pos)))
612  DIE("No memory");
613  proxy->status = atoi(status);
614  free(status);
615  if(i<realsize && '\r'!=line[i] && '\n'!=line[i])
616  {
617  //status (message)
618  pos = i+1;
619  for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
620  if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos)))
621  DIE("No memory");
622  }
623  PRINT_VERBOSE2("Header line received '%s' '%i' '%s' ", proxy->version, proxy->status, proxy->status_msg);
624  return realsize;
625  }
626 
627  //other lines
628  //header name
629  for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i] && '\n'!=line[i]; ++i)
630  line[i] = tolower(line[i]); //spdy requires lower case
631  if(NULL == (name = strndup(line, i - pos)))
632  DIE("No memory");
633  if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name)
634  || 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name)
635  || 0 == strcmp(SPDY_HTTP_HEADER_TRANSFER_ENCODING, name)
636  )
637  {
638  //forbidden in spdy, ignore
639  free(name);
640  return realsize;
641  }
642  if(i == realsize || '\r'==line[i] || '\n'==line[i])
643  {
644  //no value. is it possible?
645  if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, ""))
646  DIE("SPDY_name_value_add failed");
647  return realsize;
648  }
649 
650  //header value
651  pos = i+1;
652  while(pos<realsize && isspace(line[pos])) ++pos; //remove leading space
653  for(i=pos; i<realsize && '\r'!=line[i] && '\n'!=line[i]; ++i);
654  if(NULL == (value = strndup(&(line[pos]), i - pos)))
655  DIE("No memory");
656  PRINT_VERBOSE2("Adding header: '%s': '%s'", name, value);
657  if(SPDY_YES != (ret = SPDY_name_value_add(proxy->headers, name, value)))
658  {
659  abort_it=true;
660  if(NULL != (values = SPDY_name_value_lookup(proxy->headers, name, &num_values)))
661  for(i=0; i<(unsigned int)num_values; ++i)
662  if(0 == strcasecmp(value, values[i]))
663  {
664  abort_it=false;
665  PRINT_VERBOSE2("header appears more than once with same value '%s: %s'", name, value);
666  break;
667  }
668 
669  if(abort_it)
670  {
671  PRINT_INFO2("SPDY_name_value_add failed (%i) for '%s'", ret, name);
672  abort();
673  }
674  }
675  free(name);
676  free(value);
677 
678  return realsize;
679 }
680 
681 
682 static size_t
683 curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
684 {
685  size_t realsize = size * nmemb;
686  struct Proxy *proxy = (struct Proxy *)userp;
687 
688  //printf("curl_write_cb %i\n", realsize);
689  if(!*(proxy->session_alive))
690  {
691  PRINT_VERBOSE("data received, but session is dead");
692  proxy->spdy_error = true;
693  proxy->curl_error = true;
694  return 0;
695  }
696 
697  if(!store_in_buffer(contents, realsize, &proxy->http_body, &proxy->http_body_size))
698  {
699  PRINT_INFO("not enough memory (malloc/realloc returned NULL)");
700  proxy->curl_error = true;
701  return 0;
702  }
703  /*
704  if(NULL == proxy->http_body)
705  proxy->http_body = malloc(realsize);
706  else
707  proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + realsize);
708  if(NULL == proxy->http_body)
709  {
710  PRINT_INFO("not enough memory (realloc returned NULL)");
711  return 0;
712  }
713 
714  memcpy(proxy->http_body + proxy->http_body_size, contents, realsize);
715  proxy->http_body_size += realsize;
716  */
717 
718  PRINT_VERBOSE2("received bytes from curl: %zu", realsize);
719 
720  call_spdy_run = true;
721 
722  return realsize;
723 }
724 
725 
726 static size_t
727 curl_read_cb(void *ptr, size_t size, size_t nmemb, void *userp)
728 {
729  ssize_t ret;
730  size_t max = size * nmemb;
731  struct Proxy *proxy = (struct Proxy *)userp;
732  //void *newbody;
733 
734 
735  if((proxy->receiving_done && !proxy->received_body_size) || !proxy->is_with_body_data || max < 1)
736  {
737  PRINT_VERBOSE("curl_read_cb last call");
738  return 0;
739  }
740 
741  if(!*(proxy->session_alive))
742  {
743  PRINT_VERBOSE("POST is still being sent, but session is dead");
744  return CURL_READFUNC_ABORT;
745  }
746 
747  if(!proxy->received_body_size)//nothing to write now
748  {
749  PRINT_VERBOSE("curl_read_cb called paused");
750  proxy->is_curl_read_paused = true;
751  return CURL_READFUNC_PAUSE;//TODO curl pause should be used
752  }
753 
754  ret = get_from_buffer(&(proxy->received_body), &(proxy->received_body_size), ptr, max);
755  if(ret < 0)
756  {
757  PRINT_INFO("no memory");
758  return CURL_READFUNC_ABORT;
759  }
760 
761  /*
762  if(max >= proxy->received_body_size)
763  {
764  ret = proxy->received_body_size;
765  newbody = NULL;
766  }
767  else
768  {
769  ret = max;
770  if(NULL == (newbody = malloc(proxy->received_body_size - max)))
771  {
772  PRINT_INFO("no memory");
773  return CURL_READFUNC_ABORT;
774  }
775  memcpy(newbody, proxy->received_body + max, proxy->received_body_size - max);
776  }
777  memcpy(ptr, proxy->received_body, ret);
778  free(proxy->received_body);
779  proxy->received_body = newbody;
780  proxy->received_body_size -= ret;
781  * */
782 
783  PRINT_VERBOSE2("given POST bytes to curl: %zd", ret);
784 
785  return ret;
786 }
787 
788 
789 static int
790 iterate_cb (void *cls, const char *name, const char * const * value, int num_values)
791 {
792  struct Proxy *proxy = (struct Proxy *)cls;
793  struct curl_slist **curl_headers = (&(proxy->curl_headers));
794  char *line;
795  int line_len = strlen(name) + 3; //+ ": \0"
796  int i;
797 
798  for(i=0; i<num_values; ++i)
799  {
800  if(i) line_len += 2; //", "
801  line_len += strlen(value[i]);
802  }
803 
804  if(NULL == (line = malloc(line_len)))//no recovery
805  DIE("No memory");
806  line[0] = 0;
807 
808  strcat(line, name);
809  strcat(line, ": ");
810  //all spdy header names are lower case;
811  //for simplicity here we just capitalize the first letter
812  line[0] = toupper(line[0]);
813 
814  for(i=0; i<num_values; ++i)
815  {
816  if(i) strcat(line, ", ");
817  PRINT_VERBOSE2("Adding request header: '%s' len %ld", value[i], strlen(value[i]));
818  strcat(line, value[i]);
819  }
820  PRINT_VERBOSE2("Adding request header: '%s'", line);
821  if(NULL == (*curl_headers = curl_slist_append(*curl_headers, line)))
822  DIE("curl_slist_append failed");
823  free(line);
824 
825  return SPDY_YES;
826 }
827 
828 
829 static void
831  struct SPDY_Request * request,
832  uint8_t priority,
833  const char *method,
834  const char *path,
835  const char *version,
836  const char *host,
837  const char *scheme,
838  struct SPDY_NameValue * headers,
839  bool more)
840 {
841  (void)cls;
842  (void)priority;
843  (void)host;
844  (void)scheme;
845 
846  struct Proxy *proxy;
847  int ret;
848  struct URI *uri;
849  struct SPDY_Session *session;
850 
851  proxy = SPDY_get_cls_from_request(request);
852  if(NULL != proxy)
853  {
854  //ignore trailers or more headers
855  return;
856  }
857 
858  PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version);
859 
860  //TODO not freed once in a while
861  if(NULL == (proxy = malloc(sizeof(struct Proxy))))
862  DIE("No memory");
863  memset(proxy, 0, sizeof(struct Proxy));
864 
865  //fprintf(stderr, "new proxy for %s\n", path);
866 
867  session = SPDY_get_session_for_request(request);
868  assert(NULL != session);
869  proxy->session_alive = SPDY_get_cls_from_session(session);
870  assert(NULL != proxy->session_alive);
871 
872  SPDY_set_cls_to_request(request, proxy);
873 
874  proxy->request = request;
875  proxy->is_with_body_data = more;
876  if(NULL == (proxy->headers = SPDY_name_value_create()))
877  DIE("No memory");
878 
879  if(glob_opt.transparent)
880  {
881  if(NULL != glob_opt.http_backend) //use always same host
882  ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path);
883  else //use host header
884  ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path);
885  if(-1 == ret)
886  DIE("No memory");
887 
888  ret = parse_uri(&uri_preg, proxy->url, &uri);
889  if(ret != 0)
890  DIE("parsing built uri failed");
891  }
892  else
893  {
894  ret = parse_uri(&uri_preg, path, &uri);
895  PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host);
896  if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host))
897  DIE("parsing received uri failed");
898 
899  if(NULL != glob_opt.http_backend) //use backend host
900  {
901  ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more);
902  if(-1 == ret)
903  DIE("No memory");
904  }
905  else //use request path
906  if(NULL == (proxy->url = strdup(path)))
907  DIE("No memory");
908  }
909 
910  free_uri(uri);
911 
912  PRINT_VERBOSE2("curl will request '%s'", proxy->url);
913 
914  SPDY_name_value_iterate(headers, &iterate_cb, proxy);
915 
916  if(NULL == (proxy->curl_handle = curl_easy_init()))
917  {
918  PRINT_INFO("curl_easy_init failed");
919  abort();
920  }
921 
922  if(glob_opt.curl_verbose)
923  CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1);
924 
925  if(0 == strcmp(SPDY_HTTP_METHOD_POST,method))
926  {
927  if(NULL == (proxy->curl_headers = curl_slist_append(proxy->curl_headers, "Expect:")))
928  DIE("curl_slist_append failed");
929  CURL_SETOPT(proxy->curl_handle, CURLOPT_POST, 1);
930  CURL_SETOPT(proxy->curl_handle, CURLOPT_READFUNCTION, curl_read_cb);
931  CURL_SETOPT(proxy->curl_handle, CURLOPT_READDATA, proxy);
932  }
933 
934  if(glob_opt.timeout)
935  CURL_SETOPT(proxy->curl_handle, CURLOPT_TIMEOUT, glob_opt.timeout);
936  CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url);
937  if(glob_opt.http10)
938  CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
939  CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb);
940  CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy);
941  CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb);
942  CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy);
943  CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy);
944  CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers);
945  CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);//TODO
946  CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
947  if(glob_opt.ipv4 && !glob_opt.ipv6)
948  CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
949  else if(glob_opt.ipv6 && !glob_opt.ipv4)
950  CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
951 
952  if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle)))
953  {
954  PRINT_INFO2("curl_multi_add_handle failed (%i)", ret);
955  abort();
956  }
957  debug_num_curls++;
958 
959  //~5ms additional latency for calling this
960  if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
961  && CURLM_CALL_MULTI_PERFORM != ret)
962  {
963  PRINT_INFO2("curl_multi_perform failed (%i)", ret);
964  abort();
965  }
966 
967  call_curl_run = true;
968 }
969 
970 
971 static int
972 run ()
973 {
974  unsigned long long timeoutlong = 0;
975  unsigned long long timeout_spdy = 0;
976  long timeout_curl = -1;
977  struct timeval timeout;
978  int ret;
979  int ret_curl;
980  int ret_spdy;
981  fd_set rs;
982  fd_set ws;
983  fd_set es;
984  int maxfd = -1;
985  int maxfd_curl = -1;
986  struct SPDY_Daemon *daemon;
987  CURLMsg *msg;
988  int msgs_left;
989  struct Proxy *proxy;
990  struct sockaddr_in *addr;
991  struct addrinfo hints;
992  char service[NI_MAXSERV];
993  struct addrinfo *gai;
996  //struct SPDY_Response *error_response;
997  char *curl_private;
998 
999  signal(SIGPIPE, SIG_IGN);
1000 
1001  if (signal(SIGINT, catch_signal) == SIG_ERR)
1002  PRINT_VERBOSE("signal failed");
1003 
1004  srand(time(NULL));
1005  if(init_parse_uri(&uri_preg))
1006  DIE("Regexp compilation failed");
1007 
1008  SPDY_init();
1009 
1010  if(glob_opt.nodelay)
1011  flags |= SPDY_DAEMON_FLAG_NO_DELAY;
1012 
1013  if(NULL == glob_opt.listen_host)
1014  {
1015  daemon = SPDY_start_daemon(glob_opt.listen_port,
1016  glob_opt.cert,
1017  glob_opt.cert_key,
1018  &new_session_cb,
1022  NULL,
1024  1800,
1026  io,
1028  flags,
1030  }
1031  else
1032  {
1033  snprintf (service, sizeof(service), "%u", glob_opt.listen_port);
1034  memset (&hints, 0, sizeof(struct addrinfo));
1035  hints.ai_family = AF_INET;
1036  hints.ai_socktype = SOCK_STREAM;
1037 
1038  ret = getaddrinfo(glob_opt.listen_host, service, &hints, &gai);
1039  if(ret != 0)
1040  DIE("problem with specified host");
1041 
1042  addr = (struct sockaddr_in *) gai->ai_addr;
1043 
1044  daemon = SPDY_start_daemon(0,
1045  glob_opt.cert,
1046  glob_opt.cert_key,
1047  &new_session_cb,
1051  NULL,
1053  1800,
1055  io,
1057  flags,
1059  addr,
1060  //SPDY_DAEMON_OPTION_MAX_NUM_FRAMES,
1061  //1,
1063  }
1064 
1065  if(NULL==daemon){
1066  printf("no daemon\n");
1067  return 1;
1068  }
1069 
1070  multi_handle = curl_multi_init();
1071  if(NULL==multi_handle)
1072  DIE("no multi_handle");
1073 
1074  timeout.tv_usec = 0;
1075 
1076  do
1077  {
1078  FD_ZERO(&rs);
1079  FD_ZERO(&ws);
1080  FD_ZERO(&es);
1081 
1082  PRINT_VERBOSE2("num curls %i", debug_num_curls);
1083 
1084  ret_spdy = SPDY_get_timeout(daemon, &timeout_spdy);
1085  if(SPDY_NO == ret_spdy || timeout_spdy > 5000)
1086  timeoutlong = 5000;
1087  else
1088  timeoutlong = timeout_spdy;
1089  PRINT_VERBOSE2("SPDY timeout %lld; %i", timeout_spdy, ret_spdy);
1090 
1091  if(CURLM_OK != (ret_curl = curl_multi_timeout(multi_handle, &timeout_curl)))
1092  {
1093  PRINT_VERBOSE2("curl_multi_timeout failed (%i)", ret_curl);
1094  //curl_timeo = timeoutlong;
1095  }
1096  else if(timeout_curl >= 0 && timeoutlong > (unsigned long)timeout_curl)
1097  timeoutlong = (unsigned long)timeout_curl;
1098 
1099  PRINT_VERBOSE2("curl timeout %ld", timeout_curl);
1100 
1101  timeout.tv_sec = timeoutlong / 1000;
1102  timeout.tv_usec = (timeoutlong % 1000) * 1000;
1103 
1104  maxfd = SPDY_get_fdset (daemon,
1105  &rs,
1106  &ws,
1107  &es);
1108  assert(-1 != maxfd);
1109 
1110  if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &rs,
1111  &ws,
1112  &es, &maxfd_curl)))
1113  {
1114  PRINT_INFO2("curl_multi_fdset failed (%i)", ret);
1115  abort();
1116  }
1117 
1118  if(maxfd_curl > maxfd)
1119  maxfd = maxfd_curl;
1120 
1121  PRINT_VERBOSE2("timeout before %lld %lld", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec);
1122  ret = select(maxfd+1, &rs, &ws, &es, &timeout);
1123  PRINT_VERBOSE2("timeout after %lld %lld; ret is %i", (unsigned long long)timeout.tv_sec, (unsigned long long)timeout.tv_usec, ret);
1124 
1125  /*switch(ret) {
1126  case -1:
1127  PRINT_INFO2("select error: %i", errno);
1128  break;
1129  case 0:
1130  break;
1131  default:*/
1132 
1133  //the second part should not happen with current implementation
1134  if(ret > 0 || (SPDY_YES == ret_spdy && 0 == timeout_spdy))
1135  {
1136  PRINT_VERBOSE("run spdy");
1137  SPDY_run(daemon);
1138  call_spdy_run = false;
1139  }
1140 
1141  //if(ret > 0 || (CURLM_OK == ret_curl && 0 == timeout_curl) || call_curl_run)
1142  {
1143  PRINT_VERBOSE("run curl");
1144  if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running))
1145  && CURLM_CALL_MULTI_PERFORM != ret)
1146  {
1147  PRINT_INFO2("curl_multi_perform failed (%i)", ret);
1148  abort();
1149  }
1150  call_curl_run = false;
1151  }
1152  /*break;
1153  }*/
1154 
1155  while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
1156  if (msg->msg == CURLMSG_DONE) {
1157  PRINT_VERBOSE("A curl handler is done");
1158  if(CURLE_OK != (ret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curl_private)))
1159  {
1160  PRINT_INFO2("err %i",ret);
1161  abort();
1162  }
1163  assert(NULL != curl_private);
1164  proxy = (struct Proxy *)curl_private;
1165  if(CURLE_OK == msg->data.result)
1166  {
1167  proxy->curl_done = true;
1168  call_spdy_run = true;
1169  //TODO what happens with proxy when the client resets a stream
1170  //and response_done is not yet set for the last frame? is it
1171  //possible?
1172  }
1173  else
1174  {
1175  PRINT_VERBOSE2("bad curl result (%i) for '%s'", msg->data.result, proxy->url);
1176  if(proxy->spdy_done || proxy->spdy_error || (NULL == proxy->response && !*(proxy->session_alive)))
1177  {
1178  PRINT_VERBOSE("cleaning");
1179  SPDY_name_value_destroy(proxy->headers);
1180  SPDY_destroy_request(proxy->request);
1181  SPDY_destroy_response(proxy->response);
1182  cleanup(proxy);
1183  }
1184  else if(NULL == proxy->response && *(proxy->session_alive))
1185  {
1186  //generate error for the client
1187  PRINT_VERBOSE("will send Bad Gateway");
1188  SPDY_name_value_destroy(proxy->headers);
1189  proxy->headers = NULL;
1190  if(NULL == (proxy->response = SPDY_build_response(SPDY_HTTP_BAD_GATEWAY,
1191  NULL,
1193  NULL,
1195  strlen(ERROR_RESPONSE))))
1196  DIE("no response");
1197  if(SPDY_YES != SPDY_queue_response(proxy->request,
1198  proxy->response,
1199  true,
1200  false,
1202  proxy))
1203  {
1204  //clean and forget
1205  PRINT_VERBOSE("cleaning");
1206  SPDY_destroy_request(proxy->request);
1207  SPDY_destroy_response(proxy->response);
1208  cleanup(proxy);
1209  }
1210  }
1211  else
1212  {
1213  proxy->curl_error = true;
1214  }
1215  call_spdy_run = true;
1216  //TODO spdy should be notified to send RST_STREAM
1217  }
1218  }
1219  else PRINT_INFO("shouldn't happen");
1220  }
1221 
1222  if(call_spdy_run)
1223  {
1224  PRINT_VERBOSE("second call to SPDY_run");
1225  SPDY_run(daemon);
1226  call_spdy_run = false;
1227  }
1228 
1229  if(glob_opt.verbose)
1230  {
1231 
1232 #ifdef HAVE_CLOCK_GETTIME
1233 #ifdef CLOCK_MONOTONIC
1234  struct timespec ts;
1235 
1236  if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))
1237  PRINT_VERBOSE2 ("time now %lld %lld",
1238  (unsigned long long) ts.tv_sec,
1239  (unsigned long long) ts.tv_nsec);
1240 #endif
1241 #endif
1242  }
1243 
1244  }
1245  while(loop);
1246 
1247  SPDY_stop_daemon(daemon);
1248 
1249  curl_multi_cleanup(multi_handle);
1250 
1251  SPDY_deinit();
1252 
1254 
1255  return 0;
1256 }
1257 
1258 
1259 static void
1261 {
1262  printf(
1263  "Usage: microspdy2http -p <PORT> [-c <CERTIFICATE>] [-k <CERT-KEY>]\n"
1264  " [-rvh0DtT] [-b <HTTP-SERVER>] [-l <HOST>]\n\n"
1265  "OPTIONS:\n"
1266  " -p, --port Listening port.\n"
1267  " -l, --host Listening host. If not set, will listen on [::]\n"
1268  " -c, --certificate Path to a certificate file. Requiered if\n"
1269  " --no-tls is not set.\n"
1270  " -k, --certificate-key Path to a key file for the certificate.\n"
1271  " Requiered if --no-tls is not set.\n"
1272  " -b, --backend-server If set, the proxy will connect always to it.\n"
1273  " Otherwise the proxy will connect to the URL\n"
1274  " which is specified in the path or 'Host:'.\n"
1275  " -v, --verbose Print debug information.\n"
1276  " -r, --no-tls Do not use TLS. Client must use SPDY/3.\n"
1277  " -h, --curl-verbose Print debug information for curl.\n"
1278  " -0, --http10 Prefer HTTP/1.0 connections to the next hop.\n"
1279  " -D, --no-delay This makes sense only if --no-tls is used.\n"
1280  " TCP_NODELAY will be used for all sessions' sockets.\n"
1281  " -4, --curl-ipv4 Curl may use IPv4 to connect to the final destination.\n"
1282  " -6, --curl-ipv6 Curl may use IPv6 to connect to the final destination.\n"
1283  " If neither --curl-ipv4 nor --curl-ipv6 is set,\n"
1284  " both will be used by default.\n"
1285  " -T, --timeout Maximum time in seconds for each HTTP transfer.\n"
1286  " Use 0 for no timeout; this is the default value.\n"
1287  " -t, --transparent If set, the proxy will fetch an URL which\n"
1288  " is based on 'Host:' header and requested path.\n"
1289  " Otherwise, full URL in the requested path is required.\n\n"
1290 
1291  );
1292 }
1293 
1294 
1295 int
1296 main (int argc, char *const *argv)
1297 {
1298 
1299  int getopt_ret;
1300  int option_index;
1301  struct option long_options[] = {
1302  {"port", required_argument, 0, 'p'},
1303  {"certificate", required_argument, 0, 'c'},
1304  {"certificate-key", required_argument, 0, 'k'},
1305  {"backend-server", required_argument, 0, 'b'},
1306  {"no-tls", no_argument, 0, 'r'},
1307  {"verbose", no_argument, 0, 'v'},
1308  {"curl-verbose", no_argument, 0, 'h'},
1309  {"http10", no_argument, 0, '0'},
1310  {"no-delay", no_argument, 0, 'D'},
1311  {"transparent", no_argument, 0, 't'},
1312  {"curl-ipv4", no_argument, 0, '4'},
1313  {"curl-ipv6", no_argument, 0, '6'},
1314  {"timeout", required_argument, 0, 'T'},
1315  {0, 0, 0, 0}
1316  };
1317 
1318  while (1)
1319  {
1320  getopt_ret = getopt_long( argc, argv, "p:l:c:k:b:rv0Dth46T:", long_options, &option_index);
1321  if (getopt_ret == -1)
1322  break;
1323 
1324  switch(getopt_ret)
1325  {
1326  case 'p':
1327  glob_opt.listen_port = atoi(optarg);
1328  break;
1329 
1330  case 'l':
1331  glob_opt.listen_host= strdup(optarg);
1332  if(NULL == glob_opt.listen_host)
1333  return 1;
1334  break;
1335 
1336  case 'c':
1337  glob_opt.cert = strdup(optarg);
1338  break;
1339 
1340  case 'k':
1341  glob_opt.cert_key = strdup(optarg);
1342  break;
1343 
1344  case 'b':
1345  glob_opt.http_backend = strdup(optarg);
1346  if(NULL == glob_opt.http_backend)
1347  return 1;
1348  break;
1349 
1350  case 'r':
1351  glob_opt.notls = true;
1352  break;
1353 
1354  case 'v':
1355  glob_opt.verbose = true;
1356  break;
1357 
1358  case 'h':
1359  glob_opt.curl_verbose = true;
1360  break;
1361 
1362  case '0':
1363  glob_opt.http10 = true;
1364  break;
1365 
1366  case 'D':
1367  glob_opt.nodelay = true;
1368  break;
1369 
1370  case 't':
1371  glob_opt.transparent = true;
1372  break;
1373 
1374  case '4':
1375  glob_opt.ipv4 = true;
1376  break;
1377 
1378  case '6':
1379  glob_opt.ipv6 = true;
1380  break;
1381 
1382  case 'T':
1383  glob_opt.timeout = atoi(optarg);
1384  break;
1385 
1386  case 0:
1387  PRINT_INFO("0 from getopt");
1388  break;
1389 
1390  case '?':
1391  display_usage();
1392  return 1;
1393 
1394  default:
1395  DIE("default from getopt");
1396  }
1397  }
1398 
1399  if(
1400  0 == glob_opt.listen_port
1401  || (!glob_opt.notls && (NULL == glob_opt.cert || NULL == glob_opt.cert_key))
1402  //|| !glob_opt.transparent && NULL != glob_opt.http_backend
1403  )
1404  {
1405  display_usage();
1406  return 1;
1407  }
1408 
1409  return run();
1410 }
1411 
#define CURL_SETOPT(handle, opt, val)
Definition: proxy.c:126
static void standard_request_handler(void *cls, struct SPDY_Request *request, uint8_t priority, const char *method, const char *path, const char *version, const char *host, const char *scheme, struct SPDY_NameValue *headers, bool more)
Definition: proxy.c:830
#define SPDY_HTTP_METHOD_POST
Definition: microspdy.h:259
#define PRINT_INFO2(fmt,...)
Definition: proxy.c:97
static void free_uri(struct URI *uri)
Definition: proxy.c:189
_MHD_EXTERN int SPDY_get_timeout(struct SPDY_Daemon *daemon, unsigned long long *timeout)
static void deinit_parse_uri(regex_t *preg)
Definition: proxy.c:226
#define SPDY_YES
Definition: microspdy.h:93
public interface to libmicrospdy
#define SPDY_HTTP_VERSION_1_1
Definition: microspdy.h:249
static bool call_spdy_run
Definition: proxy.c:153
#define SPDY_HTTP_HEADER_TRANSFER_ENCODING
Definition: microspdy.h:237
_MHD_EXTERN struct SPDY_Response * SPDY_build_response_with_callback(int status, const char *statustext, const char *version, struct SPDY_NameValue *headers, SPDY_ResponseCallback rcb, void *rcb_cls, uint32_t block_size)
static size_t curl_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
Definition: proxy.c:683
int debug_num_curls
Definition: proxy.c:156
_MHD_EXTERN struct SPDY_Daemon * SPDY_start_daemon(uint16_t port, const char *certfile, const char *keyfile, SPDY_NewSessionCallback nscb, SPDY_SessionClosedCallback sccb, SPDY_NewRequestCallback nrcb, SPDY_NewDataCallback npdcb, void *cls,...)
_MHD_EXTERN int SPDY_name_value_add(struct SPDY_NameValue *container, const char *name, const char *value)
Definition: structures.c:56
static int loop
Definition: proxy.c:145
int main(int argc, char *const *argv)
Definition: proxy.c:1296
#define PRINT_VERBOSE(msg)
Definition: proxy.c:106
static int still_running
Definition: proxy.c:149
_MHD_EXTERN const char *const * SPDY_name_value_lookup(struct SPDY_NameValue *container, const char *name, int *num_values)
Definition: structures.c:190
platform-specific includes for libmicrohttpd
ssize_t response_callback(void *cls, void *buffer, size_t max, bool *more)
Definition: proxy.c:441
static int spdy_post_data_cb(void *cls, struct SPDY_Request *request, const void *buf, size_t size, bool more)
Definition: proxy.c:404
static regex_t uri_preg
Definition: proxy.c:151
static void new_session_cb(void *cls, struct SPDY_Session *session)
Definition: proxy.c:367
_MHD_EXTERN void * SPDY_get_cls_from_session(struct SPDY_Session *session)
static CURLM * multi_handle
Definition: proxy.c:147
_MHD_EXTERN void SPDY_run(struct SPDY_Daemon *daemon)
_MHD_EXTERN void SPDY_stop_daemon(struct SPDY_Daemon *daemon)
_MHD_EXTERN struct SPDY_NameValue * SPDY_name_value_create(void)
Definition: structures.c:42
#define SPDY_NO
Definition: microspdy.h:98
return NULL
Definition: tsearch.c:32
_MHD_EXTERN void SPDY_destroy_response(struct SPDY_Response *response)
Definition: structures.c:279
static int init_parse_uri(regex_t *preg)
Definition: proxy.c:209
static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp)
Definition: proxy.c:528
#define SPDY_HTTP_BAD_GATEWAY
Definition: microspdy.h:174
static void cleanup(struct Proxy *proxy)
Definition: proxy.c:483
_MHD_EXTERN void SPDY_destroy_request(struct SPDY_Request *request)
Definition: alstructures.c:30
_MHD_EXTERN void SPDY_deinit(void)
static void session_closed_cb(void *cls, struct SPDY_Session *session, int by_client)
Definition: proxy.c:386
#define PRINT_INFO(msg)
Definition: proxy.c:90
static void display_usage()
Definition: proxy.c:1260
static bool store_in_buffer(const void *src, size_t src_size, void **dst, size_t *dst_size)
Definition: proxy.c:312
_MHD_EXTERN int SPDY_get_fdset(struct SPDY_Daemon *daemon, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *except_fd_set)
_MHD_EXTERN struct SPDY_Response * SPDY_build_response(int status, const char *statustext, const char *version, struct SPDY_NameValue *headers, const void *data, size_t size)
#define DIE(msg)
Definition: proxy.c:137
static bool call_curl_run
Definition: proxy.c:154
static ssize_t get_from_buffer(void **src, size_t *src_size, void *dst, size_t max_size)
Definition: proxy.c:332
_MHD_EXTERN void SPDY_name_value_destroy(struct SPDY_NameValue *container)
Definition: structures.c:218
#define ERROR_RESPONSE
Definition: proxy.c:53
SPDY_RESPONSE_RESULT
Definition: microspdy.h:561
_MHD_EXTERN void SPDY_set_cls_to_session(struct SPDY_Session *session, void *cls)
#define PRINT_VERBOSE2(fmt,...)
Definition: proxy.c:115
SPDY_DAEMON_FLAG
Definition: microspdy.h:401
_MHD_EXTERN struct SPDY_Session * SPDY_get_session_for_request(const struct SPDY_Request *request)
struct global_options glob_opt
#define SPDY_HTTP_HEADER_CONNECTION
Definition: microspdy.h:197
static int iterate_cb(void *cls, const char *name, const char *const *value, int num_values)
Definition: proxy.c:790
static void response_done_callback(void *cls, struct SPDY_Response *response, struct SPDY_Request *request, enum SPDY_RESPONSE_RESULT status, bool streamopened)
Definition: proxy.c:506
_MHD_EXTERN void * SPDY_get_cls_from_request(struct SPDY_Request *request)
#define SPDY_init()
Definition: microspdy.h:826
_MHD_EXTERN void SPDY_set_cls_to_request(struct SPDY_Request *request, void *cls)
static int run()
Definition: proxy.c:972
SPDY_IO_SUBSYSTEM
Definition: microspdy.h:318
_MHD_EXTERN int SPDY_name_value_iterate(struct SPDY_NameValue *container, SPDY_NameValueIterator iterator, void *iterator_cls)
Definition: structures.c:237
#define SPDY_HTTP_HEADER_KEEP_ALIVE
Definition: microspdy.h:219
_MHD_EXTERN int SPDY_queue_response(struct SPDY_Request *request, struct SPDY_Response *response, bool closestream, bool consider_priority, SPDY_ResponseResultCallback rrcb, void *rrcb_cls)
static void catch_signal(int signal)
Definition: proxy.c:359
static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *userp)
Definition: proxy.c:727
static int parse_uri(regex_t *preg, const char *full_uri, struct URI **uri)
Definition: proxy.c:233