diff options
Diffstat (limited to 'src/examples/mhd2spdy_spdy.c')
-rw-r--r-- | src/examples/mhd2spdy_spdy.c | 1150 |
1 files changed, 1150 insertions, 0 deletions
diff --git a/src/examples/mhd2spdy_spdy.c b/src/examples/mhd2spdy_spdy.c new file mode 100644 index 00000000..95ec43c8 --- /dev/null +++ b/src/examples/mhd2spdy_spdy.c @@ -0,0 +1,1150 @@ +/* + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * @file mhd2spdy_spdy.c + * @brief SPDY part of the proxy. libspdylay is used for the client side. + * The example spdycli.c from spdylay was used as basis; + * however, multiple changes were made. + * @author Tatsuhiro Tsujikawa + * @author Andrey Uzunov + */ + +#include "mhd2spdy_structures.h" +#include "mhd2spdy_spdy.h" +#include "mhd2spdy_http.h" + + +/* + * Prints error containing the function name |func| and message |msg| + * and exit. + */ +static void +spdy_dief(const char *func, + const char *msg) +{ + fprintf(stderr, "FATAL: %s: %s\n", func, msg); + exit(EXIT_FAILURE); +} + + +/* + * Prints error containing the function name |func| and error code + * |error_code| and exit. + */ +void +spdy_diec(const char *func, + int error_code) +{ + fprintf(stderr, "FATAL: %s: error_code=%d, msg=%s\n", func, error_code, + spdylay_strerror(error_code)); + exit(EXIT_FAILURE); +} + + +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; +} + + +/* + * 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; +} + + +/* + * The implementation of spdylay_recv_callback type. Here we read data + * from the network and write them in |buf|. The capacity of |buf| is + * |length| bytes. Returns the number of bytes stored in |buf|. See + * the documentation of spdylay_recv_callback for the details. + */ +static ssize_t +spdy_cb_recv(spdylay_session *session, + uint8_t *buf, + size_t length, + int flags, + void *user_data) +{ + (void)session; + (void)flags; + + struct SPDY_Connection *connection; + ssize_t rv; + + connection = (struct SPDY_Connection*)user_data; + //prevent monopolizing everything + if(!(++connection->counter % 10)) return SPDYLAY_ERR_WOULDBLOCK; + connection->want_io = IO_NONE; + if(connection->is_tls) + { + ERR_clear_error(); + rv = SSL_read(connection->ssl, buf, 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 if(rv == 0) { + rv = SPDYLAY_ERR_EOF; + } + } + else + { + rv = read(connection->fd, + buf, + length); + + if (rv < 0) + { + switch(errno) + { + case EAGAIN: + #if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: + #endif + connection->want_io |= WANT_READ; + rv = SPDYLAY_ERR_WOULDBLOCK; + break; + + default: + rv = SPDYLAY_ERR_CALLBACK_FAILURE; + } + } + else if(rv == 0) + rv = SPDYLAY_ERR_EOF; + } + + if(rv > 0) + UPDATE_STAT(glob_stat.spdy_bytes_received, rv); + + return rv; +} + + +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; +} + + +/* + * 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); + } +} + + +/* + * 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; +} + + +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); + } +} + + +/* + * Setup callback functions. Spdylay API offers many callback + * functions, but most of them are optional. The send_callback is + * always required. Since we use spdylay_session_recv(), the + * recv_callback is also required. + */ +static void +spdy_setup_spdylay_callbacks(spdylay_session_callbacks *callbacks) +{ + memset(callbacks, 0, sizeof(spdylay_session_callbacks)); + callbacks->send_callback = spdy_cb_send; + callbacks->recv_callback = spdy_cb_recv; + callbacks->before_ctrl_send_callback = spdy_cb_before_ctrl_send; + callbacks->on_ctrl_recv_callback = spdy_cb_on_ctrl_recv; + callbacks->on_stream_close_callback = spdy_cb_on_stream_close; + callbacks->on_data_chunk_recv_callback = spdy_cb_on_data_chunk_recv; + callbacks->on_data_recv_callback = spdy_cb_on_data_recv; +} + + +/* + * Callback function for SSL/TLS NPN. Since this program only supports + * SPDY protocol, if server does not offer SPDY protocol the Spdylay + * library supports, we terminate program. + */ +static int +spdy_cb_ssl_select_next_proto(SSL* ssl, + unsigned char **out, + unsigned char *outlen, + const unsigned char *in, + unsigned int inlen, + void *arg) +{ + (void)ssl; + + int rv; + uint16_t *spdy_proto_version; + + /* spdylay_select_next_protocol() selects SPDY protocol version the + Spdylay library supports. */ + rv = spdylay_select_next_protocol(out, outlen, in, inlen); + if(rv <= 0) { + PRINT_INFO("Server did not advertise spdy/2 or spdy/3 protocol."); + return rv; + } + spdy_proto_version = (uint16_t*)arg; + *spdy_proto_version = rv; + return SSL_TLSEXT_ERR_OK; +} + + +/* + * Setup SSL context. We pass |spdy_proto_version| to get negotiated + * SPDY protocol version in NPN callback. + */ +void +spdy_ssl_init_ssl_ctx(SSL_CTX *ssl_ctx, + uint16_t *spdy_proto_version) +{ + /* Disable SSLv2 and enable all workarounds for buggy servers */ + SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2 | SSL_OP_NO_COMPRESSION); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); + /* Set NPN callback */ + SSL_CTX_set_next_proto_select_cb(ssl_ctx, spdy_cb_ssl_select_next_proto, + spdy_proto_version); +} + + +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; +} + + +/* + * Connects to the host |host| and port |port|. This function returns + * the file descriptor of the client socket. + */ +static int +spdy_socket_connect_to(const char *host, + uint16_t port) +{ + struct addrinfo hints; + int fd = -1; + int rv; + char service[NI_MAXSERV]; + struct addrinfo *res, *rp; + + //TODO checks + snprintf(service, sizeof(service), "%u", port); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + rv = getaddrinfo(host, service, &hints, &res); + if(rv != 0) + { + printf("%s\n",host); + spdy_dief("getaddrinfo", gai_strerror(rv)); + } + for(rp = res; rp; rp = rp->ai_next) + { + fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(fd == -1) + continue; + while((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && + errno == EINTR); + if(rv == 0) + break; + close(fd); + fd = -1; + } + freeaddrinfo(res); + + return fd; +} + + +static void +spdy_socket_make_non_block(int fd) +{ + int flags; + int rv; + + while((flags = fcntl(fd, F_GETFL, 0)) == -1 && errno == EINTR); + + if(flags == -1) + spdy_dief("fcntl", strerror(errno)); + + while((rv = fcntl(fd, F_SETFL, flags | O_NONBLOCK)) == -1 && errno == EINTR); + + if(rv == -1) + spdy_dief("fcntl", strerror(errno)); +} + + +/* + * Setting TCP_NODELAY is not mandatory for the SPDY protocol. + */ +static void +spdy_socket_set_tcp_nodelay(int fd) +{ + int val = 1; + int rv; + + rv = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, (socklen_t)sizeof(val)); + if(rv == -1) + spdy_dief("setsockopt", strerror(errno)); +} + +/* + * Update |pollfd| based on the state of |connection|. + */ + /* +void +spdy_ctl_poll(struct pollfd *pollfd, + struct SPDY_Connection *connection) +{ + pollfd->events = 0; + if(spdylay_session_want_read(connection->session) || + connection->want_io & WANT_READ) + { + pollfd->events |= POLLIN; + } + if(spdylay_session_want_write(connection->session) || + connection->want_io & WANT_WRITE) + { + pollfd->events |= POLLOUT; + } +}*/ + + +/* + * Update |selectfd| based on the state of |connection|. + */ +bool +spdy_ctl_select(fd_set * read_fd_set, + fd_set * write_fd_set, + fd_set * except_fd_set, + struct SPDY_Connection *connection) +{ + (void)except_fd_set; + + bool ret = false; + + if(spdylay_session_want_read(connection->session) || + connection->want_io & WANT_READ) + { + FD_SET(connection->fd, read_fd_set); + ret = true; + } + if(spdylay_session_want_write(connection->session) || + connection->want_io & WANT_WRITE) + { + FD_SET(connection->fd, write_fd_set); + ret = true; + } + + return ret; +} + + +/* + * 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; +} + + +/* + * 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); + + close(fd); + + if(is_tls) + SSL_free(ssl); + + return NULL; +} + + +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; + } +} + + +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; +} + +/* +void +spdy_get_pollfdset(struct pollfd fds[], + struct SPDY_Connection *connections[], + unsigned int max_size, + nfds_t *real_size) +{ + struct SPDY_Connection *connection; + struct Proxy *proxy; + + *real_size = 0; + if(max_size<1) + return; + + if(NULL != glob_opt.spdy_connection) + { + spdy_ctl_poll(&(fds[*real_size]), glob_opt.spdy_connection); + if(!fds[*real_size].events) + { + //PRINT_INFO("TODO drop connection"); + glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened; + + for(proxy = glob_opt.spdy_connection->proxies_head; NULL != proxy; proxy=proxy->next) + { + abort(); + DLL_remove(glob_opt.spdy_connection->proxies_head, glob_opt.spdy_connection->proxies_tail, proxy); + proxy->spdy_active = false; + } + spdy_free_connection(glob_opt.spdy_connection); + glob_opt.spdy_connection = NULL; + } + else + { + fds[*real_size].fd = glob_opt.spdy_connection->fd; + connections[*real_size] = glob_opt.spdy_connection; + ++(*real_size); + } + } + + connection = glob_opt.spdy_connections_head; + + while(NULL != connection && *real_size < max_size) + { + assert(!glob_opt.only_proxy); + spdy_ctl_poll(&(fds[*real_size]), connection); + if(!fds[*real_size].events) + { + //PRINT_INFO("TODO drop connection"); + glob_opt.streams_opened -= connection->streams_opened; + DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection); + glob_opt.total_spdy_connections--; + + for(proxy = connection->proxies_head; NULL != proxy; proxy=proxy->next) + { + abort(); + DLL_remove(connection->proxies_head, connection->proxies_tail, proxy); + proxy->spdy_active = false; + } + spdy_free_connection(connection); + } + else + { + fds[*real_size].fd = connection->fd; + connections[*real_size] = connection; + ++(*real_size); + } + connection = connection->next; + } + + //, "TODO max num of conn reached; close something" + assert(NULL == connection); +} +*/ + +int +spdy_get_selectfdset(fd_set * read_fd_set, + fd_set * write_fd_set, + fd_set * except_fd_set, + struct SPDY_Connection *connections[], + unsigned int max_size, + nfds_t *real_size) +{ + struct SPDY_Connection *connection; + struct SPDY_Connection *next_connection; + bool ret; + int maxfd = 0; + + *real_size = 0; + if(max_size<1) + return 0; + + if(NULL != glob_opt.spdy_connection) + { + ret = spdy_ctl_select(read_fd_set, + write_fd_set, + except_fd_set, glob_opt.spdy_connection); + if(!ret) + { + glob_opt.streams_opened -= glob_opt.spdy_connection->streams_opened; + + PRINT_INFO("spdy_free_connection in spdy_get_selectfdset"); + spdy_free_connection(glob_opt.spdy_connection); + glob_opt.spdy_connection = NULL; + } + else + { + connections[*real_size] = glob_opt.spdy_connection; + ++(*real_size); + if(maxfd < glob_opt.spdy_connection->fd) maxfd = glob_opt.spdy_connection->fd; + } + } + + connection = glob_opt.spdy_connections_head; + + while(NULL != connection && *real_size < max_size) + { + assert(!glob_opt.only_proxy); + ret = spdy_ctl_select(read_fd_set, + write_fd_set, + except_fd_set, connection); + + next_connection = connection->next; + if(!ret) + { + glob_opt.streams_opened -= connection->streams_opened; + DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connection); + glob_opt.total_spdy_connections--; + + PRINT_INFO("spdy_free_connection in spdy_get_selectfdset"); + spdy_free_connection(connection); + } + else + { + connections[*real_size] = connection; + ++(*real_size); + if(maxfd < connection->fd) maxfd = connection->fd; + } + connection = next_connection; + } + + //, "TODO max num of conn reached; close something" + assert(NULL == connection); + + return maxfd; +} + +/* +void +spdy_run(struct pollfd fds[], + struct SPDY_Connection *connections[], + int size) +{ + int i; + int ret; + struct Proxy *proxy; + + for(i=0; i<size; ++i) + { + // PRINT_INFO2("exec about to be called for %s", connections[i]->host); + if(fds[i].revents & (POLLIN | POLLOUT)) + { + ret = spdy_exec_io(connections[i]); + //PRINT_INFO2("%i",ret); + //if((spdy_pollfds[i].revents & POLLHUP) || (spdy_pollfds[0].revents & POLLERR)) + // PRINT_INFO("SPDY SPDY_Connection error"); + + //TODO POLLRDHUP + // always close on ret != 0? + + if(0 != ret) + { + glob_opt.streams_opened -= connections[i]->streams_opened; + if(connections[i] == glob_opt.spdy_connection) + { + glob_opt.spdy_connection = NULL; + } + else + { + DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]); + glob_opt.total_spdy_connections--; + } + for(proxy = connections[i]->proxies_head; NULL != proxy; proxy=proxy->next) + { + abort(); + DLL_remove(connections[i]->proxies_head, connections[i]->proxies_tail, proxy); + proxy->spdy_active = false; + proxy->spdy_error = true; + PRINT_INFO2("spdy_free_connection for id %i", proxy->id); + } + PRINT_INFO("spdy_free_connection in loop"); + spdy_free_connection(connections[i]); + } + } + else + PRINT_INFO("not called"); + } +} +*/ + +void +spdy_run_select(fd_set * read_fd_set, + fd_set * write_fd_set, + fd_set * except_fd_set, + struct SPDY_Connection *connections[], + int size) +{ + int i; + int ret; + + for(i=0; i<size; ++i) + { + // PRINT_INFO2("exec about to be called for %s", connections[i]->host); + if(FD_ISSET(connections[i]->fd, read_fd_set) || FD_ISSET(connections[i]->fd, write_fd_set) || FD_ISSET(connections[i]->fd, except_fd_set)) + { + //raise(SIGINT); + ret = spdy_exec_io(connections[i]); + + if(0 != ret) + { + glob_opt.streams_opened -= connections[i]->streams_opened; + if(connections[i] == glob_opt.spdy_connection) + { + glob_opt.spdy_connection = NULL; + } + else + { + DLL_remove(glob_opt.spdy_connections_head, glob_opt.spdy_connections_tail, connections[i]); + glob_opt.total_spdy_connections--; + } + PRINT_INFO("in spdy_run_select"); + spdy_free_connection(connections[i]); + } + } + else + { + PRINT_INFO("not called"); + //PRINT_INFO2("connection->want_io %i",connections[i]->want_io); + //PRINT_INFO2("read %i",spdylay_session_want_read(connections[i]->session)); + //PRINT_INFO2("write %i",spdylay_session_want_write(connections[i]->session)); + //raise(SIGINT); + } + } +} |