/* This file is part of libmicrospdy Copyright (C) 2012 Andrey Uzunov This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** * @file session.c * @brief TCP connection/SPDY session handling. So far most of the * functions for handling SPDY framing layer are here. * @author Andrey Uzunov */ #include "platform.h" #include "structures.h" #include "internal.h" #include "session.h" #include "compression.h" #include "tls.h" #include "stream.h" /** * Handler for reading the full SYN_STREAM frame after we know that * the frame is such. * The function waits for the full frame and then changes status * of the session. New stream is created. * * @param session SPDY_Session whose read buffer is used. */ static void spdyf_handler_read_syn_stream (struct SPDY_Session *session) { size_t name_value_strm_size = 0; unsigned int compressed_data_size; int ret; void *name_value_strm = NULL; struct SPDYF_Control_Frame *frame; struct SPDY_NameValue *headers; SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status, "the function is called wrong"); frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; //handle subheaders if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status) { if(0 == frame->length) { //protocol error: incomplete frame //we just ignore it since there is no stream id for which to //send RST_STREAM //TODO maybe GOAWAY and closing session is appropriate SPDYF_DEBUG("zero long SYN_STREAM received"); session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; free(frame); return; } if(SPDY_YES != SPDYF_stream_new(session)) { /* waiting for some more fields to create new stream or something went wrong, SPDYF_stream_new has handled the situation */ return; } session->current_stream_id = session->streams_head->stream_id; if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) { //TODO no need to create stream if this happens session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; return; } else session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; } //handle body //start reading the compressed name/value pairs (http headers) compressed_data_size = frame->length //everything after length field - 10;//4B stream id, 4B assoc strem id, 2B priority, unused and slot if(session->read_buffer_offset - session->read_buffer_beginning < compressed_data_size) { // the full frame is not yet here, try later return; } if(compressed_data_size > 0 && SPDY_YES != SPDYF_zlib_inflate(&session->zlib_recv_stream, session->read_buffer + session->read_buffer_beginning, compressed_data_size, &name_value_strm, &name_value_strm_size)) { /* something went wrong on inflating, * the state of the stream for decompression is unknown * and we may not be able to read anything more received on * this session, * so it is better to close the session */ free(name_value_strm); free(frame); /* mark the session for closing and close it, when * everything on the output queue is already written */ session->status = SPDY_SESSION_STATUS_FLUSHING; SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false); return; } if(0 == name_value_strm_size || 0 == compressed_data_size) { //Protocol error: send RST_STREAM if(SPDY_YES != SPDYF_prepare_rst_stream(session, session->current_stream_id, SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR)) { //no memory, try later to send RST return; } } ret = SPDYF_name_value_from_stream(name_value_strm, name_value_strm_size, &headers); if(SPDY_NO == ret) { //memory error, try later free(name_value_strm); return; } session->streams_head->headers = headers; //inform the application layer for the new stream received if(SPDY_YES != session->daemon->fnew_stream_cb(session->daemon->fcls, session->streams_head)) { //memory error, try later free(name_value_strm); return; } session->read_buffer_beginning += compressed_data_size; //change state to wait for new frame session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; free(frame); free(name_value_strm); } /** * Handler for reading the GOAWAY frame after we know that * the frame is such. * The function waits for the full frame and then changes status * of the session. * * @param session SPDY_Session whose read buffer is used. */ static void spdyf_handler_read_goaway (struct SPDY_Session *session) { struct SPDYF_Control_Frame *frame; uint32_t last_good_stream_id; uint32_t status_int; enum SPDY_GOAWAY_STATUS status; SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status, "the function is called wrong"); frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) { //this is a protocol error/attack session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; return; } if(0 != frame->flags || 8 != frame->length) { //this is a protocol error SPDYF_DEBUG("wrong GOAWAY received"); //anyway, it will be handled } if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length) { //not all fields are received //try later return; } //mark that the session is almost closed session->is_goaway_received = true; if(8 == frame->length) { memcpy(&last_good_stream_id, session->read_buffer + session->read_buffer_beginning, 4); last_good_stream_id = NTOH31(last_good_stream_id); session->read_buffer_beginning += 4; memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4); status = ntohl(status_int); session->read_buffer_beginning += 4; //TODO do something with last_good //SPDYF_DEBUG("Received GOAWAY; status=%i; lastgood=%i",status,last_good_stream_id); //do something according to the status //TODO switch(status) { case SPDY_GOAWAY_STATUS_OK: break; case SPDY_GOAWAY_STATUS_PROTOCOL_ERROR: break; case SPDY_GOAWAY_STATUS_INTERNAL_ERROR: break; } } session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; free(frame); } /** * Handler for reading RST_STREAM frames. After receiving the frame * the stream moves into closed state and status * of the session is changed. Frames, belonging to this stream, which * are still at the output queue, will be ignored later. * * @param session SPDY_Session whose read buffer is used. */ static void spdyf_handler_read_rst_stream (struct SPDY_Session *session) { struct SPDYF_Control_Frame *frame; uint32_t stream_id; int32_t status_int; enum SPDY_RST_STREAM_STATUS status; struct SPDYF_Stream *stream; SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status, "the function is called wrong"); frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; if(0 != frame->flags || 8 != frame->length) { //this is a protocol error SPDYF_DEBUG("wrong RST_STREAM received"); //ignore as a large frame session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; return; } if((session->read_buffer_offset - session->read_buffer_beginning) < frame->length) { //not all fields are received //try later return; } memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4); stream_id = NTOH31(stream_id); session->read_buffer_beginning += 4; memcpy(&status_int, session->read_buffer + session->read_buffer_beginning, 4); status = ntohl(status_int); session->read_buffer_beginning += 4; session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; free(frame); //mark the stream as closed stream = session->streams_head; while(NULL != stream) { if(stream_id == stream->stream_id) { stream->is_in_closed = true; stream->is_out_closed = true; break; } stream = stream->next; } SPDYF_DEBUG("Received RST_STREAM; status=%i; id=%i",status,stream_id); //do something according to the status //TODO /*switch(status) { case SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR: break; }*/ } /** * Handler for reading DATA frames. In requests they are used for POST * arguments. * * @param session SPDY_Session whose read buffer is used. */ static void spdyf_handler_read_data (struct SPDY_Session *session) { //just ignore the whole frame for now struct SPDYF_Data_Frame * frame; SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status, "the function is called wrong"); SPDYF_DEBUG("DATA frame received (POST?). Ignoring"); //SPDYF_SIGINT(""); frame = (struct SPDYF_Data_Frame *)session->frame_handler_cls; //handle subheaders if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status) { if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) { session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; return; } else session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; } //handle body if(session->read_buffer_offset - session->read_buffer_beginning >= frame->length) { session->read_buffer_beginning += frame->length; session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; free(frame); } } int SPDYF_handler_write_syn_reply (struct SPDY_Session *session) { struct SPDYF_Response_Queue *response_queue = session->response_queue_head; struct SPDYF_Stream *stream = response_queue->stream; struct SPDYF_Control_Frame control_frame; void *compressed_headers = NULL; size_t compressed_headers_size=0; size_t used_data=0; size_t total_size; uint32_t stream_id_nbo; SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); if(SPDY_YES != SPDYF_zlib_deflate(&session->zlib_send_stream, response_queue->data, response_queue->data_size, &used_data, &compressed_headers, &compressed_headers_size)) { /* something went wrong on compressing, * the state of the stream for compression is unknown * and we may not be able to send anything more on * this session, * so it is better to close the session right now */ session->status = SPDY_SESSION_STATUS_CLOSING; free(compressed_headers); return SPDY_NO; } //TODO do we need this used_Data SPDYF_ASSERT(used_data == response_queue->data_size, "not everything was used by zlib"); total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header + 4 // stream id as "subheader" + compressed_headers_size; if(NULL == (session->write_buffer = malloc(total_size))) { /* no memory * since we do not save the compressed data anywhere and * the sending zlib stream is already in new state, we must * close the session */ session->status = SPDY_SESSION_STATUS_CLOSING; free(compressed_headers); return SPDY_NO; } session->write_buffer_beginning = 0; session->write_buffer_offset = 0; session->write_buffer_size = total_size; control_frame.length = compressed_headers_size + 4; // compressed data + stream_id SPDYF_CONTROL_FRAME_HTON(&control_frame); //put frame headers to write buffer memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); //put stream id to write buffer stream_id_nbo = HTON31(stream->stream_id); memcpy(session->write_buffer + session->write_buffer_offset, &stream_id_nbo, 4); session->write_buffer_offset += 4; //put compressed name/value pairs to write buffer memcpy(session->write_buffer + session->write_buffer_offset, compressed_headers, compressed_headers_size); session->write_buffer_offset += compressed_headers_size; SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); //DEBUG CODE, break compression state to see what happens /* SPDYF_zlib_deflate(&session->zlib_send_stream, "1234567890", 10, &used_data, &compressed_headers, &compressed_headers_size); */ free(compressed_headers); session->last_replied_to_stream_id = stream->stream_id; return SPDY_YES; } int SPDYF_handler_write_goaway (struct SPDY_Session *session) { struct SPDYF_Response_Queue *response_queue = session->response_queue_head; struct SPDYF_Control_Frame control_frame; size_t total_size; int last_good_stream_id; SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); session->is_goaway_sent = true; total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header + 4 // last good stream id as "subheader" + 4; // status code as "subheader" if(NULL == (session->write_buffer = malloc(total_size))) { return SPDY_NO; } session->write_buffer_beginning = 0; session->write_buffer_offset = 0; session->write_buffer_size = total_size; control_frame.length = 8; // always for GOAWAY SPDYF_CONTROL_FRAME_HTON(&control_frame); //put frame headers to write buffer memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); //put last good stream id to write buffer last_good_stream_id = HTON31(session->last_replied_to_stream_id); memcpy(session->write_buffer + session->write_buffer_offset, &last_good_stream_id, 4); session->write_buffer_offset += 4; //put "data" to write buffer. This is the status memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 4); session->write_buffer_offset += 4; //data is not freed by the destroy function so: //free(response_queue->data); SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); return SPDY_YES; } int SPDYF_handler_write_data (struct SPDY_Session *session) { struct SPDYF_Response_Queue *response_queue = session->response_queue_head; struct SPDYF_Response_Queue *new_response_queue; size_t total_size; struct SPDYF_Data_Frame data_frame; ssize_t ret; bool more; SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); memcpy(&data_frame, response_queue->data_frame, sizeof(data_frame)); if(NULL == response_queue->response->rcb) { //standard response with data into the struct SPDYF_ASSERT(NULL != response_queue->data, "no data for the response"); total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header + response_queue->data_size; if(NULL == (session->write_buffer = malloc(total_size))) { return SPDY_NO; } session->write_buffer_beginning = 0; session->write_buffer_offset = 0; session->write_buffer_size = total_size; data_frame.length = response_queue->data_size; SPDYF_DATA_FRAME_HTON(&data_frame); //put SPDY headers to the writing buffer memcpy(session->write_buffer + session->write_buffer_offset,&data_frame,sizeof(struct SPDYF_Data_Frame)); session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame); //put data to the writing buffer memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, response_queue->data_size); session->write_buffer_offset += response_queue->data_size; } else { /* response with callbacks. The lib will produce more than 1 * data frames */ total_size = sizeof(struct SPDYF_Data_Frame) //SPDY header + SPDY_MAX_SUPPORTED_FRAME_SIZE; //max possible size if(NULL == (session->write_buffer = malloc(total_size))) { return SPDY_NO; } session->write_buffer_beginning = 0; session->write_buffer_offset = 0; session->write_buffer_size = total_size; ret = response_queue->response->rcb(response_queue->response->rcb_cls, session->write_buffer + sizeof(struct SPDYF_Data_Frame), response_queue->response->rcb_block_size, &more); if(ret < 0 || ret > response_queue->response->rcb_block_size) { //TODO send RST_STREAM (app error) //for now close session session->status = SPDY_SESSION_STATUS_CLOSING; free(session->write_buffer); return SPDY_NO; } if(0 == ret && more) { //the app couldn't write anything to buf but later will free(session->write_buffer); session->write_buffer = NULL; session->write_buffer_size = 0; if(NULL != response_queue->next) { //put the frame at the end of the queue //otherwise - head of line blocking session->response_queue_head = response_queue->next; session->response_queue_head->prev = NULL; session->response_queue_tail->next = response_queue; response_queue->prev = session->response_queue_tail; response_queue->next = NULL; session->response_queue_tail = response_queue; } return SPDY_YES; } if(more) { //create another response queue object to call the user cb again if(NULL == (new_response_queue = SPDYF_response_queue_create(true, NULL, 0, response_queue->response, response_queue->stream, false, response_queue->frqcb, response_queue->frqcb_cls, response_queue->rrcb, response_queue->rrcb_cls))) { //TODO send RST_STREAM //for now close session session->status = SPDY_SESSION_STATUS_CLOSING; free(session->write_buffer); return SPDY_NO; } //put it at second position on the queue new_response_queue->prev = response_queue; new_response_queue->next = response_queue->next; if(NULL == response_queue->next) { session->response_queue_tail = new_response_queue; } else { response_queue->next->prev = new_response_queue; } response_queue->next = new_response_queue; response_queue->frqcb = NULL; response_queue->frqcb_cls = NULL; response_queue->rrcb = NULL; response_queue->rrcb_cls = NULL; } else { data_frame.flags |= SPDY_DATA_FLAG_FIN; } data_frame.length = ret; SPDYF_DATA_FRAME_HTON(&data_frame); //put SPDY headers to the writing buffer memcpy(session->write_buffer + session->write_buffer_offset, &data_frame, sizeof(struct SPDYF_Data_Frame)); session->write_buffer_offset += sizeof(struct SPDYF_Data_Frame); session->write_buffer_offset += ret; session->write_buffer_size = session->write_buffer_offset; } SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); return SPDY_YES; } int SPDYF_handler_write_rst_stream (struct SPDY_Session *session) { struct SPDYF_Response_Queue *response_queue = session->response_queue_head; struct SPDYF_Control_Frame control_frame; size_t total_size; SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header + 4 // stream id as "subheader" + 4; // status code as "subheader" if(NULL == (session->write_buffer = malloc(total_size))) { return SPDY_NO; } session->write_buffer_beginning = 0; session->write_buffer_offset = 0; session->write_buffer_size = total_size; control_frame.length = 8; // always for RST_STREAM SPDYF_CONTROL_FRAME_HTON(&control_frame); //put frame headers to write buffer memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); //put stream id to write buffer. This is the status memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8); session->write_buffer_offset += 8; //data is not freed by the destroy function so: //free(response_queue->data); SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); return SPDY_YES; } void SPDYF_handler_ignore_frame (struct SPDY_Session *session) { struct SPDYF_Control_Frame *frame; SPDYF_ASSERT(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status || SPDY_SESSION_STATUS_WAIT_FOR_BODY == session->status, "the function is called wrong"); frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; //handle subheaders if(SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER == session->status) { if(frame->length > SPDY_MAX_SUPPORTED_FRAME_SIZE) { session->status = SPDY_SESSION_STATUS_IGNORE_BYTES; return; } else session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; } //handle body if(session->read_buffer_offset - session->read_buffer_beginning >= frame->length) { session->read_buffer_beginning += frame->length; session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; free(frame); } } int SPDYF_session_read (struct SPDY_Session *session) { int bytes_read; bool reallocate; size_t actual_buf_size; if(SPDY_SESSION_STATUS_CLOSING == session->status || SPDY_SESSION_STATUS_FLUSHING == session->status) return SPDY_NO; //if the read buffer is full to the end, we need to reallocate space if (session->read_buffer_size == session->read_buffer_offset) { //but only if the state of the session requires it //i.e. no further proceeding is possible without reallocation reallocate = false; actual_buf_size = session->read_buffer_offset - session->read_buffer_beginning; switch(session->status) { case SPDY_SESSION_STATUS_WAIT_FOR_HEADER: case SPDY_SESSION_STATUS_IGNORE_BYTES: //we need space for a whole control frame header if(actual_buf_size < sizeof(struct SPDYF_Control_Frame)) reallocate = true; break; case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER: case SPDY_SESSION_STATUS_WAIT_FOR_BODY: //we need as many bytes as set in length field of the //header SPDYF_ASSERT(NULL != session->frame_handler_cls, "no frame for session"); if(session->frame_handler != &spdyf_handler_read_data) { if(actual_buf_size < ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length) reallocate = true; } else { if(actual_buf_size < ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length) reallocate = true; } break; case SPDY_SESSION_STATUS_CLOSING: case SPDY_SESSION_STATUS_FLUSHING: //nothing needed break; } if(reallocate) { //reuse the space in the buffer that was already read by the lib memmove(session->read_buffer, session->read_buffer + session->read_buffer_beginning, session->read_buffer_offset - session->read_buffer_beginning); session->read_buffer_offset -= session->read_buffer_beginning; session->read_buffer_beginning = 0; } else { //will read next time //TODO optimize it, memmove more often? return SPDY_NO; } } session->last_activity = SPDYF_monotonic_time(); //actual read from the TLS socket bytes_read = SPDYF_tls_recv(session, session->read_buffer + session->read_buffer_offset, session->read_buffer_size - session->read_buffer_offset); switch(bytes_read) { case SPDY_TLS_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_TLS_ERROR_ERROR: //any kind of error in the TLS subsystem //try to prepare GOAWAY frame SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_INTERNAL_ERROR, false); //try to flush the queue when write is called session->status = SPDY_SESSION_STATUS_FLUSHING; return SPDY_YES; case SPDY_TLS_ERROR_AGAIN: //read or write should be called again; leave it for the //next time return SPDY_NO; //default: //something was really read from the TLS subsystem //just continue } session->read_buffer_offset += bytes_read; return SPDY_YES; } int SPDYF_session_write (struct SPDY_Session *session, bool only_one_frame) { 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; for(i=0; only_one_frame ? i < 1 : i < SPDYF_NUM_SENT_FRAMES_AT_ONCE; ++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 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 break; } } } session->last_activity = SPDYF_monotonic_time(); //actual write to the TLS socket bytes_written = SPDYF_tls_send(session, session->write_buffer + session->write_buffer_beginning, session->write_buffer_offset - session->write_buffer_beginning); switch(bytes_written) { case SPDY_TLS_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_TLS_ERROR_ERROR: //any kind of error in the TLS subsystem //forbid more writing session->status = SPDY_SESSION_STATUS_CLOSING; return SPDY_YES; case SPDY_TLS_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(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; } int SPDYF_session_idle (struct SPDY_Session *session) { size_t read_buffer_beginning; size_t frame_length; struct SPDYF_Control_Frame* control_frame; struct SPDYF_Data_Frame *data_frame; //prepare session for closing if timeout is used and already passed if(SPDY_SESSION_STATUS_CLOSING != session->status && session->daemon->session_timeout && (session->last_activity + session->daemon->session_timeout < SPDYF_monotonic_time())) { session->status = SPDY_SESSION_STATUS_CLOSING; //best effort for sending GOAWAY SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_OK, true); SPDYF_session_write(session,true); } switch(session->status) { //expect new frame to arrive case SPDY_SESSION_STATUS_WAIT_FOR_HEADER: session->current_stream_id = 0; //check if the whole frame header is already here //both frame types have the same length if(session->read_buffer_offset - session->read_buffer_beginning < sizeof(struct SPDYF_Control_Frame)) return SPDY_NO; /* check the first bit to see if it is data or control frame * and also if the version is supported */ if(0x80 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning) && SPDY_VERSION == *((uint8_t *)session->read_buffer + session->read_buffer_beginning + 1)) { //control frame if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) { SPDYF_DEBUG("No memory"); return SPDY_NO; } //get frame headers memcpy(control_frame, session->read_buffer + session->read_buffer_beginning, sizeof(struct SPDYF_Control_Frame)); session->read_buffer_beginning += sizeof(struct SPDYF_Control_Frame); SPDYF_CONTROL_FRAME_NTOH(control_frame); session->status = SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER; //assign different frame handler according to frame type switch(control_frame->type){ case SPDY_CONTROL_FRAME_TYPES_SYN_STREAM: session->frame_handler = &spdyf_handler_read_syn_stream; break; case SPDY_CONTROL_FRAME_TYPES_GOAWAY: session->frame_handler = &spdyf_handler_read_goaway; break; case SPDY_CONTROL_FRAME_TYPES_RST_STREAM: session->frame_handler = &spdyf_handler_read_rst_stream; break; default: session->frame_handler = &SPDYF_handler_ignore_frame; } session->frame_handler_cls = control_frame; //DO NOT break the outer case } else if(0 == *(uint8_t *)(session->read_buffer + session->read_buffer_beginning)) { //needed for POST //data frame if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame)))) { SPDYF_DEBUG("No memory"); return SPDY_NO; } //get frame headers memcpy(data_frame, session->read_buffer + session->read_buffer_beginning, sizeof(struct SPDYF_Data_Frame)); session->read_buffer_beginning += sizeof(struct SPDYF_Data_Frame); SPDYF_DATA_FRAME_NTOH(data_frame); session->status = SPDY_SESSION_STATUS_WAIT_FOR_BODY; session->frame_handler = &spdyf_handler_read_data; session->frame_handler_cls = data_frame; //DO NOT brake the outer case } else { SPDYF_DEBUG("another protocol or version received!"); /* According to the draft the lib should send here * RST_STREAM with status UNSUPPORTED_VERSION. I don't * see any sense of keeping the session open since * we don't know how many bytes is the bogus "frame". * And the latter normally will be HTTP request. * */ //shutdown(session->socket_fd, SHUT_RD); session->status = SPDY_SESSION_STATUS_FLUSHING; SPDYF_prepare_goaway(session, SPDY_GOAWAY_STATUS_PROTOCOL_ERROR,false); //SPDYF_session_write(session,false); /* close connection since the client expects another protocol from us */ //SPDYF_session_close(session); return SPDY_YES; } //expect specific header fields after the standard header case SPDY_SESSION_STATUS_WAIT_FOR_SUBHEADER: if(NULL!=session->frame_handler) { read_buffer_beginning = session->read_buffer_beginning; //if everything is ok, the "body" will also be processed //by the handler session->frame_handler(session); if(SPDY_SESSION_STATUS_IGNORE_BYTES == session->status) { //check for larger than max supported frame if(session->frame_handler != &spdyf_handler_read_data) { frame_length = ((struct SPDYF_Control_Frame *)session->frame_handler_cls)->length; } else { frame_length = ((struct SPDYF_Data_Frame *)session->frame_handler_cls)->length; } //if(SPDY_MAX_SUPPORTED_FRAME_SIZE < frame_length) { SPDYF_DEBUG("received frame with unsupported size: %zu", frame_length); //the data being received must be ignored and //RST_STREAM sent //ignore bytes that will arive later session->read_ignore_bytes = frame_length + read_buffer_beginning - session->read_buffer_offset; //ignore what is already in read buffer session->read_buffer_beginning = session->read_buffer_offset; SPDYF_prepare_rst_stream(session, session->current_stream_id, //may be 0 here which is not good SPDY_RST_STREAM_STATUS_FRAME_TOO_LARGE); //actually the read buffer can be bigger than the //max supported size session->status = session->read_ignore_bytes ? SPDY_SESSION_STATUS_IGNORE_BYTES : SPDY_SESSION_STATUS_WAIT_FOR_HEADER; free(session->frame_handler_cls); } } } if(SPDY_SESSION_STATUS_IGNORE_BYTES != session->status) { break; } //ignoring data in read buffer case SPDY_SESSION_STATUS_IGNORE_BYTES: SPDYF_ASSERT(session->read_ignore_bytes > 0, "Session is in wrong state"); if(session->read_ignore_bytes > session->read_buffer_offset - session->read_buffer_beginning) { session->read_ignore_bytes -= session->read_buffer_offset - session->read_buffer_beginning; session->read_buffer_beginning = session->read_buffer_offset; } else { session->read_buffer_beginning += session->read_ignore_bytes; session->read_ignore_bytes = 0; session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; } break; //expect frame body (name/value pairs) case SPDY_SESSION_STATUS_WAIT_FOR_BODY: if(NULL!=session->frame_handler) session->frame_handler(session); break; case SPDY_SESSION_STATUS_FLUSHING: return SPDY_NO; //because of error the session needs to be closed case SPDY_SESSION_STATUS_CLOSING: //error should be already sent to the client SPDYF_session_close(session); return SPDY_YES; } return SPDY_YES; } 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 SPDYF_tls_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); } } int SPDYF_session_accept(struct SPDY_Daemon *daemon) { int new_socket_fd; //int fd_flags; struct SPDY_Session *session = NULL; socklen_t addr_len; struct sockaddr *addr; #if HAVE_INET6 struct sockaddr_in6 addr6; addr = (struct sockaddr *)&addr6; addr_len = sizeof(addr6); #else struct sockaddr_in addr4; addr = (struct sockaddr *)&addr4; addr_len = sizeof(addr6); #endif new_socket_fd = accept (daemon->socket_fd, addr, &addr_len); if(new_socket_fd < 1) return SPDY_NO; //setting the socket to be non-blocking /* * different handling is needed by libssl if non-blocking is used * fd_flags = fcntl (new_socket_fd, F_GETFL); if ( -1 == fd_flags || 0 != fcntl (new_socket_fd, F_SETFL, fd_flags | O_NONBLOCK)) { SPDYF_DEBUG("WARNING: Couldn't set the new connection to be non-blocking"); } */ if (NULL == (session = malloc (sizeof (struct SPDY_Session)))) { goto free_and_fail; } memset (session, 0, sizeof (struct SPDY_Session)); session->daemon = daemon; session->socket_fd = new_socket_fd; //init TLS context, handshake will be done if(SPDY_YES != SPDYF_tls_new_session(session)) { goto free_and_fail; } //read buffer session->read_buffer_size = SPDYF_BUFFER_SIZE; if (NULL == (session->read_buffer = malloc (session->read_buffer_size))) { SPDYF_tls_close_session(session); goto free_and_fail; } //address of the client if (NULL == (session->addr = malloc (addr_len))) { SPDYF_tls_close_session(session); goto free_and_fail; } memcpy (session->addr, addr, addr_len); session->addr_len = addr_len; session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; //init zlib context for the whole session if(SPDY_YES != SPDYF_zlib_deflate_init(&session->zlib_send_stream)) { SPDYF_tls_close_session(session); goto free_and_fail; } if(SPDY_YES != SPDYF_zlib_inflate_init(&session->zlib_recv_stream)) { SPDYF_tls_close_session(session); SPDYF_zlib_deflate_end(&session->zlib_send_stream); goto free_and_fail; } //add it to daemon's list DLL_insert(daemon->sessions_head,daemon->sessions_tail,session); session->last_activity = SPDYF_monotonic_time(); if(NULL != daemon->new_session_cb) daemon->new_session_cb(daemon->cls, session); return SPDY_YES; //for GOTO free_and_fail: /* something failed, so shutdown, close and free memory */ shutdown (new_socket_fd, SHUT_RDWR); close (new_socket_fd); if(NULL != session) { if(NULL != session->addr) free (session->addr); if(NULL != session->read_buffer) free (session->read_buffer); free (session); } return SPDY_NO; } void SPDYF_queue_response (struct SPDYF_Response_Queue *response_to_queue, struct SPDY_Session *session, int consider_priority) { struct SPDYF_Response_Queue *pos; struct SPDYF_Response_Queue *last; uint8_t priority; SPDYF_ASSERT(SPDY_YES != consider_priority || NULL != response_to_queue->stream, "called with consider_priority but no stream provided"); last = response_to_queue; while(NULL != last->next) { last = last->next; } if(SPDY_NO == consider_priority) { //put it at the end of the queue response_to_queue->prev = session->response_queue_tail; if (NULL == session->response_queue_head) session->response_queue_head = response_to_queue; else session->response_queue_tail->next = response_to_queue; session->response_queue_tail = last; return; } else if(-1 == consider_priority) { //put it at the head of the queue last->next = session->response_queue_head; if (NULL == session->response_queue_tail) session->response_queue_tail = last; else session->response_queue_head->prev = response_to_queue; session->response_queue_head = response_to_queue; return; } if(NULL == session->response_queue_tail) { session->response_queue_head = response_to_queue; session->response_queue_tail = last; return; } //search for the right position to put it pos = session->response_queue_tail; priority = response_to_queue->stream->priority; while(NULL != pos && pos->stream->priority > priority) { pos = pos->prev; } if(NULL == pos) { //put it on the head session->response_queue_head->prev = last; last->next = session->response_queue_head; session->response_queue_head = response_to_queue; } else if(NULL == pos->next) { //put it at the end response_to_queue->prev = pos; pos->next = response_to_queue; session->response_queue_tail = last; } else { response_to_queue->prev = pos; last->next = pos->next; pos->next = response_to_queue; last->next->prev = last; } } void SPDYF_session_destroy(struct SPDY_Session *session) { struct SPDYF_Stream *stream; struct SPDYF_Response_Queue *response_queue; 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); } int SPDYF_prepare_goaway (struct SPDY_Session *session, enum SPDY_GOAWAY_STATUS status, bool in_front) { struct SPDYF_Response_Queue *response_to_queue; struct SPDYF_Control_Frame *control_frame; uint32_t *data; if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) { return SPDY_NO; } memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) { free(response_to_queue); return SPDY_NO; } memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); if(NULL == (data = malloc(4))) { free(control_frame); free(response_to_queue); return SPDY_NO; } *(data) = htonl(status); control_frame->control_bit = 1; control_frame->version = SPDY_VERSION; control_frame->type = SPDY_CONTROL_FRAME_TYPES_GOAWAY; control_frame->flags = 0; response_to_queue->control_frame = control_frame; response_to_queue->process_response_handler = &SPDYF_handler_write_goaway; response_to_queue->data = data; response_to_queue->data_size = 4; SPDYF_queue_response (response_to_queue, session, in_front ? -1 : SPDY_NO); return SPDY_YES; } int SPDYF_prepare_rst_stream (struct SPDY_Session *session, uint32_t stream_id, enum SPDY_RST_STREAM_STATUS status) { struct SPDYF_Response_Queue *response_to_queue; struct SPDYF_Control_Frame *control_frame; uint32_t *data; if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) { return SPDY_NO; } memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) { free(response_to_queue); return SPDY_NO; } memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); if(NULL == (data = malloc(8))) { free(control_frame); free(response_to_queue); return SPDY_NO; } *(data) = HTON31(stream_id); *(data + 1) = htonl(status); control_frame->control_bit = 1; control_frame->version = SPDY_VERSION; control_frame->type = SPDY_CONTROL_FRAME_TYPES_RST_STREAM; control_frame->flags = 0; response_to_queue->control_frame = control_frame; response_to_queue->process_response_handler = &SPDYF_handler_write_rst_stream; response_to_queue->data = data; response_to_queue->data_size = 8; SPDYF_queue_response (response_to_queue, session, -1); return SPDY_YES; }