static void spdy_cb_before_ctrl_send(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, void *user_data) { (void)user_data; int32_t stream_id; struct Proxy *proxy; switch(type) { case SPDYLAY_SYN_STREAM: stream_id = frame->syn_stream.stream_id; proxy = spdylay_session_get_stream_user_data(session, stream_id); proxy->stream_id = stream_id; ++glob_opt.streams_opened; ++proxy->spdy_connection->streams_opened; PRINT_INFO2("opening stream: str open %i; %s", glob_opt.streams_opened, proxy->url); break; case SPDYLAY_RST_STREAM: //try to ignore duplicate RST_STREAMs //TODO this will ignore RST_STREAMs also for bogus data glob_opt.ignore_rst_stream = NULL==spdylay_session_get_stream_user_data(session, frame->rst_stream.stream_id); PRINT_INFO2("sending RST_STREAM for %i; ignore %i; status %i", frame->rst_stream.stream_id, glob_opt.ignore_rst_stream, frame->rst_stream.status_code); break; default: break; } }
void spdy_cb_on_ctrl_recv(spdylay_session *session, spdylay_frame_type type, spdylay_frame *frame, void *user_data) { (void)user_data; char **nv; int32_t stream_id; struct Proxy * proxy; switch(type) { case SPDYLAY_SYN_REPLY: nv = frame->syn_reply.nv; stream_id = frame->syn_reply.stream_id; break; case SPDYLAY_RST_STREAM: stream_id = frame->rst_stream.stream_id; break; case SPDYLAY_HEADERS: nv = frame->headers.nv; stream_id = frame->headers.stream_id; break; default: return; break; } proxy = spdylay_session_get_stream_user_data(session, stream_id); if(NULL == proxy) { PRINT_INFO2("received frame type %i for unkonwn stream id %i", type, stream_id); return; //DIE("no proxy obj"); } switch(type) { case SPDYLAY_SYN_REPLY: PRINT_INFO2("received headers for %s", proxy->url); http_create_response(proxy, nv); break; case SPDYLAY_RST_STREAM: PRINT_INFO2("received reset stream for %s", proxy->url); proxy->spdy_error = true; break; case SPDYLAY_HEADERS: PRINT_INFO2("received headers for %s", proxy->url); http_create_response(proxy, nv); break; default: return; break; } glob_opt.spdy_data_received = true; }
void http_cb_request_completed (void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { (void)cls; (void)connection; struct HTTP_URI *http_uri; struct Proxy *proxy; http_uri = (struct HTTP_URI *)*con_cls; if(NULL == http_uri) return; proxy = (struct Proxy *)http_uri->proxy; assert(NULL != proxy); PRINT_INFO2("http_cb_request_completed %i for %s; id %i",toe, http_uri->uri, proxy->id); if(NULL != proxy->http_response) { MHD_destroy_response (proxy->http_response); proxy->http_response = NULL; } if(proxy->spdy_active) { proxy->http_active = false; if(MHD_REQUEST_TERMINATED_COMPLETED_OK != toe) { proxy->http_error = true; if(proxy->stream_id > 0 /*&& NULL != proxy->spdy_connection->session*/) { //send RST_STREAM_STATUS_CANCEL PRINT_INFO2("send rst_stream %i %i",proxy->spdy_active, proxy->stream_id ); spdylay_submit_rst_stream(proxy->spdy_connection->session, proxy->stream_id, 5); } /*else { DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy); free_proxy(proxy); }*/ } } else { PRINT_INFO2("proxy free http id %i ", proxy->id); free_proxy(proxy); } --glob_opt.responses_pending; }
static void response_done_callback(void *cls, struct SPDY_Response *response, struct SPDY_Request *request, enum SPDY_RESPONSE_RESULT status, bool streamopened) { (void)streamopened; struct Proxy *proxy = (struct Proxy *)cls; int ret; if(SPDY_RESPONSE_RESULT_SUCCESS != status) { printf("answer was NOT sent, %i\n",status); } if(CURLM_OK != (ret = curl_multi_remove_handle(multi_handle, proxy->curl_handle))) { PRINT_INFO2("curl_multi_remove_handle failed (%i)", ret); } curl_slist_free_all(proxy->curl_headers); curl_easy_cleanup(proxy->curl_handle); SPDY_destroy_request(request); SPDY_destroy_response(response); free(proxy->url); free(proxy); }
/* * The implementation of spdylay_on_stream_close_callback type. We use * this function to know the response is fully received. Since we just * fetch 1 resource in this program, after reception of the response, * we submit GOAWAY and close the session. */ static void spdy_cb_on_stream_close(spdylay_session *session, int32_t stream_id, spdylay_status_code status_code, void *user_data) { (void)status_code; (void)user_data; struct Proxy * proxy = spdylay_session_get_stream_user_data(session, stream_id); assert(NULL != proxy); --glob_opt.streams_opened; --proxy->spdy_connection->streams_opened; PRINT_INFO2("closing stream: str opened %i; remove proxy %i", glob_opt.streams_opened, proxy->id); DLL_remove(proxy->spdy_connection->proxies_head, proxy->spdy_connection->proxies_tail, proxy); if(proxy->http_active) { proxy->spdy_active = false; } else { free_proxy(proxy); } }
void spdy_free_connection(struct SPDY_Connection * connection) { struct Proxy *proxy; struct Proxy *proxy_next; if(NULL != connection) { for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy_next) { proxy_next = proxy->next; DLL_remove(connection->proxies_head, connection->proxies_tail, proxy); proxy->spdy_active = false; proxy->spdy_error = true; PRINT_INFO2("spdy_free_connection for id %i", proxy->id); if(!proxy->http_active) { free_proxy(proxy); } } spdylay_session_del(connection->session); SSL_free(connection->ssl); free(connection->host); free(connection); //connection->session = NULL; } }
void http_create_response(struct Proxy* proxy, char **nv) { size_t i; if(!proxy->http_active) return; for(i = 0; nv[i]; i += 2) { if(0 == strcmp(":status", nv[i])) { char tmp[4]; memcpy(&tmp,nv[i+1],3); tmp[3]=0; proxy->status = atoi(tmp); continue; } else if(0 == strcmp(":version", nv[i])) { proxy->version = nv[i+1]; continue; } else if(0 == strcmp("content-length", nv[i])) { continue; } char *header = *(nv+i); if(MHD_NO == MHD_add_response_header (proxy->http_response, header, nv[i+1])) { PRINT_INFO2("SPDY_name_value_add failed: '%s' '%s'", header, nv[i+1]); } PRINT_INFO2("adding '%s: %s'",header, nv[i+1]); } if(MHD_NO == MHD_queue_response (proxy->http_connection, proxy->status, proxy->http_response)){ PRINT_INFO("No queue"); //TODO //abort(); proxy->http_error = true; } MHD_destroy_response (proxy->http_response); proxy->http_response = NULL; }
/* * Performs the network I/O. */ int spdy_exec_io(struct SPDY_Connection *connection) { int rv; rv = spdylay_session_recv(connection->session); if(rv != 0) { PRINT_INFO2("spdylay_session_recv %i", rv); return rv; } rv = spdylay_session_send(connection->session); if(rv != 0) PRINT_INFO2("spdylay_session_send %i", rv); return rv; }
void * http_cb_log(void * cls, const char * uri) { (void)cls; struct HTTP_URI * http_uri; PRINT_INFO2("log uri '%s'\n", uri); //TODO not freed once in a while if(NULL == (http_uri = au_malloc(sizeof(struct HTTP_URI )))) return NULL; http_uri->uri = strdup(uri); return http_uri; }
static int spdy_ssl_handshake(SSL *ssl, int fd) { int rv; if(SSL_set_fd(ssl, fd) == 0) spdy_dief("SSL_set_fd", ERR_error_string(ERR_get_error(), NULL)); ERR_clear_error(); rv = SSL_connect(ssl); if(rv <= 0) PRINT_INFO2("SSL_connect %s", ERR_error_string(ERR_get_error(), NULL)); return rv; }
static void spdy_cb_on_data_recv(spdylay_session *session, uint8_t flags, int32_t stream_id, int32_t length, void *user_data) { (void)length; (void)user_data; if(flags & SPDYLAY_DATA_FLAG_FIN) { struct Proxy *proxy; proxy = spdylay_session_get_stream_user_data(session, stream_id); proxy->done = true; PRINT_INFO2("last data frame received for %s", proxy->url); } }
/* * The implementation of spdylay_on_data_chunk_recv_callback type. We * use this function to print the received response body. */ static void spdy_cb_on_data_chunk_recv(spdylay_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data) { (void)flags; (void)user_data; struct Proxy *proxy; proxy = spdylay_session_get_stream_user_data(session, stream_id); if(NULL == proxy) { PRINT_INFO("proxy in spdy_cb_on_data_chunk_recv is NULL)"); return; } if(!copy_buffer(data, len, &proxy->http_body, &proxy->http_body_size)) { //TODO handle it better? PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); return; } /* if(NULL == proxy->http_body) proxy->http_body = au_malloc(len); else proxy->http_body = realloc(proxy->http_body, proxy->http_body_size + len); if(NULL == proxy->http_body) { PRINT_INFO("not enough memory (realloc returned NULL)"); return ; } memcpy(proxy->http_body + proxy->http_body_size, data, len); proxy->http_body_size += len; */ PRINT_INFO2("received data for %s; %zu bytes", proxy->url, len); glob_opt.spdy_data_received = true; }
/* * Fetches the resource denoted by |uri|. */ struct SPDY_Connection * spdy_connect(const struct URI *uri, uint16_t port, bool is_tls) { spdylay_session_callbacks callbacks; int fd; SSL *ssl=NULL; struct SPDY_Connection * connection = NULL; int rv; spdy_setup_spdylay_callbacks(&callbacks); /* Establish connection and setup SSL */ PRINT_INFO2("connecting to %s:%i", uri->host, port); fd = spdy_socket_connect_to(uri->host, port); if(fd == -1) { PRINT_INFO("Could not open file descriptor"); return NULL; } if(is_tls) { ssl = SSL_new(glob_opt.ssl_ctx); if(ssl == NULL) { spdy_dief("SSL_new", ERR_error_string(ERR_get_error(), NULL)); } //TODO non-blocking /* To simplify the program, we perform SSL/TLS handshake in blocking I/O. */ glob_opt.spdy_proto_version = 0; rv = spdy_ssl_handshake(ssl, fd); if(rv <= 0 || (glob_opt.spdy_proto_version != 3 && glob_opt.spdy_proto_version != 2)) { PRINT_INFO("Closing SSL"); //no spdy on the other side goto free_and_fail; } } else { glob_opt.spdy_proto_version = 3; } if(NULL == (connection = au_malloc(sizeof(struct SPDY_Connection)))) goto free_and_fail; connection->is_tls = is_tls; connection->ssl = ssl; connection->want_io = IO_NONE; if(NULL == (connection->host = strdup(uri->host))) goto free_and_fail; /* Here make file descriptor non-block */ spdy_socket_make_non_block(fd); spdy_socket_set_tcp_nodelay(fd); PRINT_INFO2("[INFO] SPDY protocol version = %d\n", glob_opt.spdy_proto_version); rv = spdylay_session_client_new(&(connection->session), glob_opt.spdy_proto_version, &callbacks, connection); if(rv != 0) { spdy_diec("spdylay_session_client_new", rv); } connection->fd = fd; return connection; //for GOTO free_and_fail: if(NULL != connection) { free(connection->host); free(connection); } if(is_tls) SSL_shutdown(ssl); MHD_socket_close_ (fd); if(is_tls) SSL_free(ssl); return NULL; }
int http_cb_request (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) { (void)cls; (void)url; (void)upload_data; (void)upload_data_size; int ret; struct Proxy *proxy; struct SPDY_Headers spdy_headers; bool with_body = false; struct HTTP_URI *http_uri; const char *header_value; if (NULL == ptr || NULL == *ptr) return MHD_NO; http_uri = (struct HTTP_URI *)*ptr; if(NULL == http_uri->proxy) { //first call for this request if (0 != strcmp (method, MHD_HTTP_METHOD_GET) && 0 != strcmp (method, MHD_HTTP_METHOD_POST)) { free(http_uri->uri); free(http_uri); PRINT_INFO2("unexpected method %s", method); return MHD_NO; } if(NULL == (proxy = au_malloc(sizeof(struct Proxy)))) { free(http_uri->uri); free(http_uri); PRINT_INFO("No memory"); return MHD_NO; } ++glob_opt.responses_pending; proxy->id = rand(); proxy->http_active = true; proxy->http_connection = connection; http_uri->proxy = proxy; return MHD_YES; } proxy = http_uri->proxy; if(proxy->spdy_error || proxy->http_error) return MHD_NO; // handled at different place TODO? leaks? if(proxy->spdy_active) { if(0 == strcmp (method, MHD_HTTP_METHOD_POST)) { PRINT_INFO("POST processing"); int rc= spdylay_session_resume_data(proxy->spdy_connection->session, proxy->stream_id); PRINT_INFO2("rc is %i stream is %i", rc, proxy->stream_id); proxy->spdy_connection->want_io |= WANT_WRITE; if(0 == *upload_data_size) { PRINT_INFO("POST http EOF"); proxy->receiving_done = true; return MHD_YES; } if(!copy_buffer(upload_data, *upload_data_size, &proxy->received_body, &proxy->received_body_size)) { //TODO handle it better? PRINT_INFO("not enough memory (malloc/realloc returned NULL)"); return MHD_NO; } *upload_data_size = 0; return MHD_YES; } //already handled PRINT_INFO("unnecessary call to http_cb_request"); return MHD_YES; } //second call for this request PRINT_INFO2("received request for '%s %s %s'", method, http_uri->uri, version); proxy->url = http_uri->uri; header_value = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); with_body = 0 == strcmp (method, MHD_HTTP_METHOD_POST) && (NULL == header_value || 0 != strcmp ("0", header_value)); PRINT_INFO2("body will be sent %i", with_body); ret = parse_uri(&glob_opt.uri_preg, proxy->url, &proxy->uri); if(ret != 0) DIE("parse_uri failed"); proxy->http_uri = http_uri; spdy_headers.num = MHD_get_connection_values (connection, MHD_HEADER_KIND, NULL, NULL); if(NULL == (spdy_headers.nv = au_malloc(((spdy_headers.num + 5) * 2 + 1) * sizeof(char *)))) DIE("no memory"); spdy_headers.nv[0] = ":method"; spdy_headers.nv[1] = method; spdy_headers.nv[2] = ":path"; spdy_headers.nv[3] = proxy->uri->path_and_more; spdy_headers.nv[4] = ":version"; spdy_headers.nv[5] = (char *)version; spdy_headers.nv[6] = ":scheme"; spdy_headers.nv[7] = proxy->uri->scheme; spdy_headers.nv[8] = ":host"; spdy_headers.nv[9] = NULL; //nv[14] = NULL; spdy_headers.cnt = 10; MHD_get_connection_values (connection, MHD_HEADER_KIND, &http_cb_iterate, &spdy_headers); spdy_headers.nv[spdy_headers.cnt] = NULL; if(NULL == spdy_headers.nv[9]) spdy_headers.nv[9] = proxy->uri->host_and_port; if(0 != spdy_request(spdy_headers.nv, proxy, with_body)) { free(spdy_headers.nv); //free_proxy(proxy); return MHD_NO; } free(spdy_headers.nv); proxy->http_response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN, 4096, &http_cb_response, proxy, &http_cb_response_done); if (NULL == proxy->http_response) DIE("no response"); if(MHD_NO == MHD_add_response_header (proxy->http_response, "Proxy-Connection", "keep-alive")) PRINT_INFO("SPDY_name_value_add failed: "); if(MHD_NO == MHD_add_response_header (proxy->http_response, "Connection", "Keep-Alive")) PRINT_INFO("SPDY_name_value_add failed: "); if(MHD_NO == MHD_add_response_header (proxy->http_response, "Keep-Alive", "timeout=5, max=100")) PRINT_INFO("SPDY_name_value_add failed: "); proxy->spdy_active = true; return MHD_YES; }
static int run () { unsigned long long timeoutlong = 0; unsigned long long timeout_spdy = 0; long timeout_curl = -1; struct timeval timeout; int ret; int ret_curl; int ret_spdy; fd_set rs; fd_set ws; fd_set es; int maxfd = -1; int maxfd_curl = -1; struct SPDY_Daemon *daemon; CURLMsg *msg; int msgs_left; struct Proxy *proxy; struct sockaddr_in *addr; struct addrinfo hints; char service[NI_MAXSERV]; struct addrinfo *gai; enum SPDY_IO_SUBSYSTEM io = glob_opt.notls ? SPDY_IO_SUBSYSTEM_RAW : SPDY_IO_SUBSYSTEM_OPENSSL; enum SPDY_DAEMON_FLAG flags = SPDY_DAEMON_FLAG_NO; signal(SIGPIPE, SIG_IGN); if (signal(SIGINT, catch_signal) == SIG_ERR) PRINT_VERBOSE("signal failed"); srand(time(NULL)); if(init_parse_uri(&uri_preg)) DIE("Regexp compilation failed"); SPDY_init(); if(glob_opt.nodelay) flags |= SPDY_DAEMON_FLAG_NO_DELAY; if(NULL == glob_opt.listen_host) { daemon = SPDY_start_daemon(glob_opt.listen_port, glob_opt.cert, glob_opt.cert_key, &new_session_cb, &session_closed_cb, &standard_request_handler, NULL, NULL, SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 1800, SPDY_DAEMON_OPTION_IO_SUBSYSTEM, io, SPDY_DAEMON_OPTION_FLAGS, flags, SPDY_DAEMON_OPTION_END); } else { snprintf (service, sizeof(service), "%u", glob_opt.listen_port); memset (&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; ret = getaddrinfo(glob_opt.listen_host, service, &hints, &gai); if(ret != 0) DIE("problem with specified host"); addr = (struct sockaddr_in *) gai->ai_addr; daemon = SPDY_start_daemon(0, glob_opt.cert, glob_opt.cert_key, &new_session_cb, &session_closed_cb, &standard_request_handler, NULL, NULL, SPDY_DAEMON_OPTION_SESSION_TIMEOUT, 1800, SPDY_DAEMON_OPTION_IO_SUBSYSTEM, io, SPDY_DAEMON_OPTION_FLAGS, flags, SPDY_DAEMON_OPTION_SOCK_ADDR, addr, SPDY_DAEMON_OPTION_END); } if(NULL==daemon){ printf("no daemon\n"); return 1; } multi_handle = curl_multi_init(); if(NULL==multi_handle) DIE("no multi_handle"); timeout.tv_usec = 0; do { FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es); ret_spdy = SPDY_get_timeout(daemon, &timeout_spdy); if(SPDY_NO == ret_spdy || timeout_spdy > 5000) timeoutlong = 5000; else timeoutlong = timeout_spdy; PRINT_VERBOSE2("SPDY timeout %i; %i", timeout_spdy, ret_spdy); if(CURLM_OK != (ret_curl = curl_multi_timeout(multi_handle, &timeout_curl))) { PRINT_VERBOSE2("curl_multi_timeout failed (%i)", ret_curl); //curl_timeo = timeoutlong; } else if(timeoutlong > timeout_curl) timeoutlong = timeout_curl; PRINT_VERBOSE2("curl timeout %i", timeout_curl); timeout.tv_sec = timeoutlong / 1000; timeout.tv_usec = (timeoutlong % 1000) * 1000; maxfd = SPDY_get_fdset (daemon, &rs, &ws, &es); assert(-1 != maxfd); if(CURLM_OK != (ret = curl_multi_fdset(multi_handle, &rs, &ws, &es, &maxfd_curl))) { PRINT_INFO2("curl_multi_fdset failed (%i)", ret); abort(); } if(maxfd_curl > maxfd) maxfd = maxfd_curl; PRINT_VERBOSE2("timeout before %i %i", timeout.tv_sec, timeout.tv_usec); ret = select(maxfd+1, &rs, &ws, &es, &timeout); PRINT_VERBOSE2("timeout after %i %i; ret is %i", timeout.tv_sec, timeout.tv_usec, ret); /*switch(ret) { case -1: PRINT_INFO2("select error: %i", errno); break; case 0: break; default:*/ //the second part should not happen with current implementation if(ret > 0 || (SPDY_YES == ret_spdy && 0 == timeout_spdy)) { PRINT_VERBOSE("run spdy"); SPDY_run(daemon); call_spdy_run = false; } if(ret > 0 || (CURLM_OK == ret_curl && 0 == timeout_curl) || call_curl_run) { PRINT_VERBOSE("run curl"); if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running)) && CURLM_CALL_MULTI_PERFORM != ret) { PRINT_INFO2("curl_multi_perform failed (%i)", ret); abort(); } call_curl_run = false; } /*break; }*/ while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) { if (msg->msg == CURLMSG_DONE) { if(CURLE_OK == msg->data.result) { if(CURLE_OK != (ret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &proxy))) { PRINT_INFO2("err %i",ret); abort(); } proxy->done = true; call_spdy_run = true; } else { PRINT_VERBOSE2("bad curl result for '%s'", proxy->url); proxy->done = true; call_spdy_run = true; //TODO spdy should be notified to send RST_STREAM } } else PRINT_INFO("shouldn't happen"); } if(call_spdy_run) { PRINT_VERBOSE("second call to SPDY_run"); SPDY_run(daemon); call_spdy_run = false; } if(glob_opt.verbose) { struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) PRINT_VERBOSE2("time now %i %i", ts.tv_sec, ts.tv_nsec); } } while(loop); curl_multi_cleanup(multi_handle); SPDY_stop_daemon(daemon); SPDY_deinit(); deinit_parse_uri(&uri_preg); return 0; }
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) { (void)cls; (void)priority; (void)host; (void)scheme; struct Proxy *proxy; int ret; struct URI *uri; struct SPDY_Session *session; PRINT_VERBOSE2("received request for '%s %s %s'\n", method, path, version); if(NULL == (proxy = malloc(sizeof(struct Proxy)))) DIE("No memory"); memset(proxy, 0, sizeof(struct Proxy)); session = SPDY_get_session_for_request(request); assert(NULL != session); proxy->session_alive = SPDY_get_cls_from_session(session); assert(NULL != proxy->session_alive); proxy->request = request; if(NULL == (proxy->headers = SPDY_name_value_create())) DIE("No memory"); if(glob_opt.transparent) { if(NULL != glob_opt.http_backend) //use always same host ret = asprintf(&(proxy->url),"%s://%s%s", scheme, glob_opt.http_backend, path); else //use host header ret = asprintf(&(proxy->url),"%s://%s%s", scheme, host, path); if(-1 == ret) DIE("No memory"); ret = parse_uri(&uri_preg, proxy->url, &uri); if(ret != 0) DIE("parsing built uri failed"); } else { ret = parse_uri(&uri_preg, path, &uri); PRINT_INFO2("path %s '%s' '%s'", path, uri->scheme, uri->host); if(ret != 0 || !strlen(uri->scheme) || !strlen(uri->host)) DIE("parsing received uri failed"); if(NULL != glob_opt.http_backend) //use backend host { ret = asprintf(&(proxy->url),"%s://%s%s", uri->scheme, glob_opt.http_backend, uri->path_and_more); if(-1 == ret) DIE("No memory"); } else //use request path if(NULL == (proxy->url = strdup(path))) DIE("No memory"); } free_uri(uri); PRINT_VERBOSE2("curl will request '%s'", proxy->url); SPDY_name_value_iterate(headers, &iterate_cb, proxy); if(NULL == (proxy->curl_handle = curl_easy_init())) { PRINT_INFO("curl_easy_init failed"); abort(); } if(glob_opt.curl_verbose) CURL_SETOPT(proxy->curl_handle, CURLOPT_VERBOSE, 1); CURL_SETOPT(proxy->curl_handle, CURLOPT_URL, proxy->url); if(glob_opt.http10) CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEFUNCTION, curl_write_cb); CURL_SETOPT(proxy->curl_handle, CURLOPT_WRITEDATA, proxy); CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERFUNCTION, curl_header_cb); CURL_SETOPT(proxy->curl_handle, CURLOPT_HEADERDATA, proxy); CURL_SETOPT(proxy->curl_handle, CURLOPT_PRIVATE, proxy); CURL_SETOPT(proxy->curl_handle, CURLOPT_HTTPHEADER, proxy->curl_headers); CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); CURL_SETOPT(proxy->curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); if(glob_opt.ipv4 && !glob_opt.ipv6) CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); else if(glob_opt.ipv6 && !glob_opt.ipv4) CURL_SETOPT(proxy->curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); if(CURLM_OK != (ret = curl_multi_add_handle(multi_handle, proxy->curl_handle))) { PRINT_INFO2("curl_multi_add_handle failed (%i)", ret); abort(); } //~5ms additional latency for calling this if(CURLM_OK != (ret = curl_multi_perform(multi_handle, &still_running)) && CURLM_CALL_MULTI_PERFORM != ret) { PRINT_INFO2("curl_multi_perform failed (%i)", ret); abort(); } call_curl_run = true; }
static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *userp) { size_t realsize = size * nmemb; struct Proxy *proxy = (struct Proxy *)userp; char *line = (char *)ptr; char *name; char *value; char *status; int i; int pos; int ret; int num_values; const char * const * values; bool abort_it; //printf("curl_header_cb %s\n", line); if(!*(proxy->session_alive)) { PRINT_VERBOSE("headers received, but session is dead"); return 0; } //trailer if(NULL != proxy->response) return 0; if('\r' == line[0]) { //all headers were already handled; prepare spdy frames if(NULL == (proxy->response = SPDY_build_response_with_callback(proxy->status, proxy->status_msg, proxy->version, proxy->headers, &response_callback, proxy, 0))) DIE("no response"); SPDY_name_value_destroy(proxy->headers); free(proxy->status_msg); free(proxy->version); if(SPDY_YES != SPDY_queue_response(proxy->request, proxy->response, true, false, &response_done_callback, proxy)) DIE("no queue"); call_spdy_run = true; return realsize; } pos = 0; if(NULL == proxy->version) { //first line from headers //version for(i=pos; i<realsize && ' '!=line[i]; ++i); if(i == realsize) DIE("error on parsing headers"); if(NULL == (proxy->version = strndup(line, i - pos))) DIE("No memory"); pos = i+1; //status (number) for(i=pos; i<realsize && ' '!=line[i] && '\r'!=line[i]; ++i); if(NULL == (status = strndup(&(line[pos]), i - pos))) DIE("No memory"); proxy->status = atoi(status); free(status); if(i<realsize && '\r'!=line[i]) { //status (message) pos = i+1; for(i=pos; i<realsize && '\r'!=line[i]; ++i); if(NULL == (proxy->status_msg = strndup(&(line[pos]), i - pos))) DIE("No memory"); } PRINT_VERBOSE2("Header line received '%s' '%i' '%s' ", proxy->version, proxy->status, proxy->status_msg); return realsize; } //other lines //header name for(i=pos; i<realsize && ':'!=line[i] && '\r'!=line[i]; ++i) line[i] = tolower(line[i]); //spdy requires lower case if(NULL == (name = strndup(line, i - pos))) DIE("No memory"); if(0 == strcmp(SPDY_HTTP_HEADER_CONNECTION, name) || 0 == strcmp(SPDY_HTTP_HEADER_KEEP_ALIVE, name) || 0 == strcmp(SPDY_HTTP_HEADER_TRANSFER_ENCODING, name) ) { //forbidden in spdy, ignore free(name); return realsize; } if(i == realsize || '\r'==line[i]) { //no value. is it possible? if(SPDY_YES != SPDY_name_value_add(proxy->headers, name, "")) DIE("SPDY_name_value_add failed"); return realsize; } //header value pos = i+1; while(pos<realsize && isspace(line[pos])) ++pos; //remove leading space for(i=pos; i<realsize && '\r'!=line[i]; ++i); if(NULL == (value = strndup(&(line[pos]), i - pos))) DIE("No memory"); PRINT_VERBOSE2("Adding header: '%s': '%s'", name, value); if(SPDY_YES != (ret = SPDY_name_value_add(proxy->headers, name, value))) { abort_it=true; if(NULL != (values = SPDY_name_value_lookup(proxy->headers, name, &num_values))) for(i=0; i<num_values; ++i) if(0 == strcasecmp(value, values[i])) { abort_it=false; PRINT_INFO2("header appears more than once with same value '%s: %s'", name, value); break; } if(abort_it) { PRINT_INFO2("SPDY_name_value_add failed (%i) for '%s'", ret, name); abort(); } } free(name); free(value); return realsize; }
int spdy_request(const char **nv, struct Proxy *proxy, bool with_body) { int ret; uint16_t port; struct SPDY_Connection *connection; spdylay_data_provider post_data; if(glob_opt.only_proxy) { connection = glob_opt.spdy_connection; } else { connection = glob_opt.spdy_connections_head; while(NULL != connection) { if(0 == strcasecmp(proxy->uri->host, connection->host)) break; connection = connection->next; } if(NULL == connection) { //connect to host port = proxy->uri->port; if(0 == port) port = 443; connection = spdy_connect(proxy->uri, port, true); if(NULL != connection) { DLL_insert(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection); glob_opt.total_spdy_connections++; } else connection = glob_opt.spdy_connection; } } if(NULL == connection) { PRINT_INFO("there is no proxy!"); return -1; } proxy->spdy_connection = connection; if(with_body) { post_data.source.ptr = proxy; post_data.read_callback = &spdy_cb_data_source_read; ret = spdylay_submit_request(connection->session, 0, nv, &post_data, proxy); } else ret = spdylay_submit_request(connection->session, 0, nv, NULL, proxy); if(ret != 0) { spdy_diec("spdylay_spdy_submit_request", ret); } PRINT_INFO2("adding proxy %i", proxy->id); if(NULL != connection->proxies_head) PRINT_INFO2("before proxy %i", connection->proxies_head->id); DLL_insert(connection->proxies_head, connection->proxies_tail, proxy); return ret; }
static ssize_t spdy_cb_data_source_read(spdylay_session *session, int32_t stream_id, uint8_t *buf, size_t length, int *eof, spdylay_data_source *source, void *user_data) { (void)session; (void)stream_id; (void)user_data; ssize_t ret; assert(NULL != source); assert(NULL != source->ptr); struct Proxy *proxy = (struct Proxy *)(source->ptr); void *newbody; if(length < 1) { PRINT_INFO("spdy_cb_data_source_read: length is 0"); return 0; } if(!proxy->received_body_size)//nothing to write now { if(proxy->receiving_done) { PRINT_INFO("POST spdy EOF"); *eof = 1; } PRINT_INFO("POST SPDYLAY_ERR_DEFERRED"); return SPDYLAY_ERR_DEFERRED;//TODO SPDYLAY_ERR_DEFERRED should be used } if(length >= proxy->received_body_size) { ret = proxy->received_body_size; newbody = NULL; } else { ret = length; if(NULL == (newbody = malloc(proxy->received_body_size - length))) { PRINT_INFO("no memory"); return SPDYLAY_ERR_TEMPORAL_CALLBACK_FAILURE; } memcpy(newbody, proxy->received_body + length, proxy->received_body_size - length); } memcpy(buf, proxy->received_body, ret); free(proxy->received_body); proxy->received_body = newbody; proxy->received_body_size -= ret; if(0 == proxy->received_body_size && proxy->receiving_done) { PRINT_INFO("POST spdy EOF"); *eof = 1; } PRINT_INFO2("given POST bytes to spdylay: %zd", ret); return ret; }
static ssize_t http_cb_response (void *cls, uint64_t pos, char *buffer, size_t max) { (void)pos; int ret; struct Proxy *proxy = (struct Proxy *)cls; void *newbody; const union MHD_ConnectionInfo *info; int val = 1; PRINT_INFO2("http_cb_response for %s", proxy->url); if(proxy->spdy_error) return MHD_CONTENT_READER_END_WITH_ERROR; if(0 == proxy->http_body_size && (proxy->done || !proxy->spdy_active)) { PRINT_INFO("sent end of stream"); return MHD_CONTENT_READER_END_OF_STREAM; } if(!proxy->http_body_size)//nothing to write now { //flush data info = MHD_get_connection_info (proxy->http_connection, MHD_CONNECTION_INFO_CONNECTION_FD); ret = setsockopt(info->connect_fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); if(ret == -1) { DIE("setsockopt"); } PRINT_INFO("FLUSH data"); return 0; } if(max >= proxy->http_body_size) { ret = proxy->http_body_size; newbody = NULL; } else { ret = max; if(NULL == (newbody = au_malloc(proxy->http_body_size - max))) { PRINT_INFO("no memory"); return MHD_CONTENT_READER_END_WITH_ERROR; } memcpy(newbody, proxy->http_body + max, proxy->http_body_size - max); } memcpy(buffer, proxy->http_body, ret); free(proxy->http_body); proxy->http_body = newbody; proxy->http_body_size -= ret; if(proxy->length >= 0) { proxy->length -= ret; } PRINT_INFO2("response_callback, size: %i",ret); return ret; }
/* * The implementation of spdylay_send_callback type. Here we write * |data| with size |length| to the network and return the number of * bytes actually written. See the documentation of * spdylay_send_callback for the details. */ static ssize_t spdy_cb_send(spdylay_session *session, const uint8_t *data, size_t length, int flags, void *user_data) { (void)session; (void)flags; //PRINT_INFO("spdy_cb_send called"); struct SPDY_Connection *connection; ssize_t rv; connection = (struct SPDY_Connection*)user_data; connection->want_io = IO_NONE; if(glob_opt.ignore_rst_stream && 16 == length && 0x80 == data[0] && 0x00 == data[2] && 0x03 == data[3] ) { PRINT_INFO2("ignoring RST_STREAM for stream_id %i %i %i %i", data[8], data[9], data[10], data[11]); glob_opt.ignore_rst_stream = false; return 16; } glob_opt.ignore_rst_stream = false; if(connection->is_tls) { ERR_clear_error(); rv = SSL_write(connection->ssl, data, length); if(rv < 0) { int err = SSL_get_error(connection->ssl, rv); if(err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) { connection->want_io |= (err == SSL_ERROR_WANT_READ ? WANT_READ : WANT_WRITE); rv = SPDYLAY_ERR_WOULDBLOCK; } else { rv = SPDYLAY_ERR_CALLBACK_FAILURE; } } } else { rv = write(connection->fd, data, length); if (rv < 0) { switch(errno) { case EAGAIN: #if EAGAIN != EWOULDBLOCK case EWOULDBLOCK: #endif connection->want_io |= WANT_WRITE; rv = SPDYLAY_ERR_WOULDBLOCK; break; default: rv = SPDYLAY_ERR_CALLBACK_FAILURE; } } } PRINT_INFO2("%zd bytes written by spdy", rv); if(rv > 0) UPDATE_STAT(glob_stat.spdy_bytes_sent, rv); return rv; }