void SPDYF_session_close (struct SPDY_Session *session) { struct SPDY_Daemon *daemon = session->daemon; int by_client = session->read_closed ? SPDY_YES : SPDY_NO; //shutdown the tls and deinit the tls context session->fio_close_session(session); shutdown (session->socket_fd, session->read_closed ? SHUT_WR : SHUT_RDWR); session->read_closed = true; //remove session from the list DLL_remove (daemon->sessions_head, daemon->sessions_tail, session); //add the session for the list for cleaning up DLL_insert (daemon->cleanup_head, daemon->cleanup_tail, session); //call callback for closed session if(NULL != daemon->session_closed_cb) { daemon->session_closed_cb(daemon->cls, session, by_client); } }
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; } }
/* * 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 SPDYF_session_destroy(struct SPDY_Session *session) { struct SPDYF_Stream *stream; struct SPDYF_Response_Queue *response_queue; (void)close (session->socket_fd); SPDYF_zlib_deflate_end(&session->zlib_send_stream); SPDYF_zlib_inflate_end(&session->zlib_recv_stream); //clean up unsent data in the output queue while (NULL != (response_queue = session->response_queue_head)) { DLL_remove (session->response_queue_head, session->response_queue_tail, response_queue); if(NULL != response_queue->frqcb) { response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_SESSION_CLOSED); } SPDYF_response_queue_destroy(response_queue); } //clean up the streams belonging to this session while (NULL != (stream = session->streams_head)) { DLL_remove (session->streams_head, session->streams_tail, stream); SPDYF_stream_destroy(stream); } free(session->addr); free(session->read_buffer); free(session->write_buffer); free(session); }
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); } } }
int SPDYF_session_write (struct SPDY_Session *session, bool only_one_frame) { unsigned int i; int bytes_written; struct SPDYF_Response_Queue *queue_head; struct SPDYF_Response_Queue *response_queue; if(SPDY_SESSION_STATUS_CLOSING == session->status) return SPDY_NO; if(SPDY_NO == session->fio_before_write(session)) return SPDY_NO; for(i=0; only_one_frame ? i < 1 : i < session->max_num_frames; ++i) { //if the buffer is not null, part of the last frame is still //pending to be sent if(NULL == session->write_buffer) { //discard frames on closed streams response_queue = session->response_queue_head; while(NULL != response_queue) { //if stream is closed, remove not yet sent frames //associated with it //GOAWAY frames are not associated to streams //and still need to be sent if(NULL == response_queue->stream || !response_queue->stream->is_out_closed) break; DLL_remove(session->response_queue_head,session->response_queue_tail,response_queue); if(NULL != response_queue->frqcb) { response_queue->frqcb(response_queue->frqcb_cls, response_queue, SPDY_RESPONSE_RESULT_STREAM_CLOSED); } SPDYF_response_queue_destroy(response_queue); response_queue = session->response_queue_head; } if(NULL == session->response_queue_head) break;//nothing on the queue //get next data from queue and put it to the write buffer // to send it if(SPDY_NO == session->response_queue_head->process_response_handler(session)) { //error occured and the handler changed or not the //session's status appropriately if(SPDY_SESSION_STATUS_CLOSING == session->status) { //try to send GOAWAY first if the current frame is different if(session->response_queue_head->is_data || SPDY_CONTROL_FRAME_TYPES_GOAWAY != session->response_queue_head->control_frame->type) { session->status = SPDY_SESSION_STATUS_FLUSHING; SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, true); SPDYF_session_write(session,true); session->status = SPDY_SESSION_STATUS_CLOSING; } return SPDY_YES; } //just return from the loop to return from this function ++i; break; } //check if something was prepared for writing //on respones with callbacks it is possible that their is no //data available if(0 == session->write_buffer_size)//nothing to write { if(response_queue != session->response_queue_head) { //the handler modified the queue continue; } else { //no need to try the same frame again ++i; break; } } } session->last_activity = SPDYF_monotonic_time(); //actual write to the IO bytes_written = session->fio_send(session, session->write_buffer + session->write_buffer_beginning, session->write_buffer_offset - session->write_buffer_beginning); switch(bytes_written) { case SPDY_IO_ERROR_CLOSED: //The TLS connection was closed by the other party, clean //or not shutdown (session->socket_fd, SHUT_RD); session->read_closed = true; session->status = SPDY_SESSION_STATUS_CLOSING; return SPDY_YES; case SPDY_IO_ERROR_ERROR: //any kind of error in the TLS subsystem //forbid more writing session->status = SPDY_SESSION_STATUS_CLOSING; return SPDY_YES; case SPDY_IO_ERROR_AGAIN: //read or write should be called again; leave it for the //next time; return from the function as we do not now //whether reading or writing is needed return i>0 ? SPDY_YES : SPDY_NO; //default: //something was really read from the TLS subsystem //just continue } session->write_buffer_beginning += bytes_written; //check if the full buffer was written if(session->write_buffer_beginning == session->write_buffer_size) { //that response is handled, remove it from queue free(session->write_buffer); session->write_buffer = NULL; session->write_buffer_size = 0; queue_head = session->response_queue_head; if(NULL == queue_head->next) { session->response_queue_head = NULL; session->response_queue_tail = NULL; } else { session->response_queue_head = queue_head->next; session->response_queue_head->prev = NULL; } //set stream to closed if the frame's fin flag is set SPDYF_stream_set_flags_on_write(queue_head); if(NULL != queue_head->frqcb) { //application layer callback to notify sending of the response queue_head->frqcb(queue_head->frqcb_cls, queue_head, SPDY_RESPONSE_RESULT_SUCCESS); } SPDYF_response_queue_destroy(queue_head); } } if(SPDY_SESSION_STATUS_FLUSHING == session->status && NULL == session->response_queue_head) session->status = SPDY_SESSION_STATUS_CLOSING; //return i>0 ? SPDY_YES : SPDY_NO; return session->fio_after_write(session, i>0 ? SPDY_YES : SPDY_NO); }
/** * Run through the suspended connections and move any that are no * longer suspended back to the active state. * * @remark To be called only from thread that process * daemon's select()/poll()/etc. * * @param daemon daemon context * @return true if a connection was actually resumed */ bool MHD_resume_suspended_connections_ (struct MHD_Daemon *daemon) /* FIXME: rename connections -> requests? */ { struct MHD_Connection *pos; struct MHD_Connection *prev = NULL; bool ret; const bool used_thr_p_c = (MHD_TM_THREAD_PER_CONNECTION == daemon->threading_mode); mhd_assert (NULL == daemon->worker_pool); ret = false; MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex); if (daemon->resuming) { prev = daemon->suspended_connections_tail; /* During shutdown check for resuming is forced. */ mhd_assert((NULL != prev) || (daemon->shutdown)); } daemon->resuming = false; while (NULL != (pos = prev)) { #ifdef UPGRADE_SUPPORT struct MHD_UpgradeResponseHandle * const urh = pos->request.urh; #else /* ! UPGRADE_SUPPORT */ static const void * const urh = NULL; #endif /* ! UPGRADE_SUPPORT */ prev = pos->prev; if ( (! pos->resuming) #ifdef UPGRADE_SUPPORT || ( (NULL != urh) && ( (! urh->was_closed) || (! urh->clean_ready) ) ) #endif /* UPGRADE_SUPPORT */ ) continue; ret = true; mhd_assert (pos->suspended); DLL_remove (daemon->suspended_connections_head, daemon->suspended_connections_tail, pos); pos->suspended = false; if (NULL == urh) { DLL_insert (daemon->connections_head, daemon->connections_tail, pos); if (! used_thr_p_c) { /* Reset timeout timer on resume. */ if (0 != pos->connection_timeout) pos->last_activity = MHD_monotonic_sec_counter(); if (pos->connection_timeout == daemon->connection_default_timeout) XDLL_insert (daemon->normal_timeout_head, daemon->normal_timeout_tail, pos); else XDLL_insert (daemon->manual_timeout_head, daemon->manual_timeout_tail, pos); } #ifdef EPOLL_SUPPORT if (MHD_ELS_EPOLL == daemon->event_loop_syscall) { if (0 != (pos->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)) MHD_PANIC ("Resumed connection was already in EREADY set\n"); /* we always mark resumed connections as ready, as we might have missed the edge poll event during suspension */ EDLL_insert (daemon->eready_head, daemon->eready_tail, pos); pos->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL | \ MHD_EPOLL_STATE_READ_READY | MHD_EPOLL_STATE_WRITE_READY; pos->epoll_state &= ~MHD_EPOLL_STATE_SUSPENDED; } #endif } #ifdef UPGRADE_SUPPORT else { struct MHD_Response *response = pos->request.response; /* Data forwarding was finished (for TLS connections) AND * application was closed upgraded connection. * Insert connection into cleanup list. */ if ( (NULL != response) && (MHD_TM_THREAD_PER_CONNECTION != daemon->threading_mode) && (NULL != response->termination_cb) ) response->termination_cb (response->termination_cb_cls, MHD_REQUEST_TERMINATED_COMPLETED_OK, &pos->request.client_context); DLL_insert (daemon->cleanup_head, daemon->cleanup_tail, pos); } #endif /* UPGRADE_SUPPORT */ pos->resuming = false; } MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex); if ( (used_thr_p_c) && (ret) ) { /* Wake up suspended connections. */ if (! MHD_itc_activate_(daemon->itc, "w")) { #ifdef HAVE_MESSAGES MHD_DLOG (daemon, MHD_SC_ITC_USE_FAILED, _("Failed to signal resume of connection via inter-thread communication channel.")); #endif } } return ret; }
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; }