aboutsummaryrefslogtreecommitdiff
path: root/src/microspdy/session.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microspdy/session.c')
-rw-r--r--src/microspdy/session.c1769
1 files changed, 1769 insertions, 0 deletions
diff --git a/src/microspdy/session.c b/src/microspdy/session.c
new file mode 100644
index 00000000..3714c25f
--- /dev/null
+++ b/src/microspdy/session.c
@@ -0,0 +1,1769 @@
+/*
+ This file is part of libmicrospdy
+ Copyright 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 <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @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 "stream.h"
+#include "io.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->streams_head,
+ SPDY_RST_STREAM_STATUS_PROTOCOL_ERROR))
+ {
+ //no memory, try later to send RST
+ free(name_value_strm);
+ return;
+ }
+ }
+ else
+ {
+ 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;
+ }
+
+ //SPDYF_DEBUG("syn_stream received: id %i", session->current_stream_id);
+
+ //change state to wait for new frame
+ free(name_value_strm);
+ session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+ free(frame);
+}
+
+
+/**
+ * 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;
+ }
+
+ //SPDYF_DEBUG("goaway received: status %i", status);
+ }
+
+ 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; //for debug
+ 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); //for debug
+ 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)
+{
+ int ret;
+ struct SPDYF_Data_Frame * frame;
+ struct SPDYF_Stream * stream;
+
+ 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)
+ {
+ stream = SPDYF_stream_find(frame->stream_id, session);
+
+ if(NULL == stream || stream->is_in_closed || NULL == session->daemon->received_data_cb)
+ {
+ if(NULL == session->daemon->received_data_cb)
+ SPDYF_DEBUG("No callback for DATA frame set; Ignoring DATA frame!");
+
+ //TODO send error?
+
+ //TODO for now ignore frame
+ session->read_buffer_beginning += frame->length;
+ session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER;
+ free(frame);
+ return;
+ }
+
+ ret = session->daemon->freceived_data_cb(session->daemon->cls,
+ stream,
+ session->read_buffer + session->read_buffer_beginning,
+ frame->length,
+ 0 == (SPDY_DATA_FLAG_FIN & frame->flags));
+
+ session->read_buffer_beginning += frame->length;
+
+ stream->window_size -= frame->length;
+
+ //TODO close in and send rst maybe
+ SPDYF_ASSERT(SPDY_YES == ret, "Cancel POST data is not yet implemented");
+
+ if(SPDY_DATA_FLAG_FIN & frame->flags)
+ {
+ stream->is_in_closed = true;
+ }
+ else if(stream->window_size < SPDYF_INITIAL_WINDOW_SIZE / 2)
+ {
+ //very simple implementation of flow control
+ //when the window's size is under the half of the initial value,
+ //increase it again up to the initial value
+
+ //prepare WINDOW_UPDATE
+ if(SPDY_YES == SPDYF_prepare_window_update(session, stream,
+ SPDYF_INITIAL_WINDOW_SIZE - stream->window_size))
+ {
+ stream->window_size = SPDYF_INITIAL_WINDOW_SIZE;
+ }
+ //else: do it later
+ }
+
+ //SPDYF_DEBUG("data received: id %i", frame->stream_id);
+
+ 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;
+
+ //SPDYF_DEBUG("syn_reply sent: id %i", 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_DEBUG("goaway sent: status %i", NTOH31(*(uint32_t*)(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)
+ {
+ free(session->write_buffer);
+ session->write_buffer = NULL;
+
+ //send RST_STREAM
+ if(SPDY_YES == (ret = SPDYF_prepare_rst_stream(session,
+ response_queue->stream,
+ SPDY_RST_STREAM_STATUS_INTERNAL_ERROR)))
+ {
+ return SPDY_NO;
+ }
+
+ //else no memory
+ //for now close session
+ //TODO what?
+ session->status = SPDY_SESSION_STATUS_CLOSING;
+
+ 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);
+ session->write_buffer = NULL;
+ 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_DEBUG("data sent: id %i", NTOH31(data_frame.stream_id));
+
+ 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_DEBUG("rst_stream sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32));
+
+ 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_window_update (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; // delta-window-size 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 WINDOW_UPDATE
+ 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 and delta-window-size to write buffer
+ memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8);
+ session->write_buffer_offset += 8;
+
+ //SPDYF_DEBUG("window_update sent: id %i", NTOH31((((uint64_t)response_queue->data) & 0xFFFF0000) >> 32));
+
+ 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 = session->fio_recv(session,
+ session->read_buffer + session->read_buffer_offset,
+ session->read_buffer_size - session->read_buffer_offset);
+
+ switch(bytes_read)
+ {
+ 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
+ //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_IO_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)
+{
+ 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);
+}
+
+
+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 > 0 ? session->streams_head : NULL, //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
+ 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);
+ }
+}
+
+
+int
+SPDYF_session_accept(struct SPDY_Daemon *daemon)
+{
+ int new_socket_fd;
+ int ret;
+ 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;
+
+ 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;
+ session->max_num_frames = daemon->max_num_frames;
+
+ ret = SPDYF_io_set_session(session, daemon->io_subsystem);
+ SPDYF_ASSERT(SPDY_YES == ret, "Somehow daemon->io_subsystem iswrong here");
+
+ //init TLS context, handshake will be done
+ if(SPDY_YES != session->fio_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)))
+ {
+ session->fio_close_session(session);
+ goto free_and_fail;
+ }
+
+ //address of the client
+ if (NULL == (session->addr = malloc (addr_len)))
+ {
+ session->fio_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))
+ {
+ session->fio_close_session(session);
+ goto free_and_fail;
+ }
+ if(SPDY_YES != SPDYF_zlib_inflate_init(&session->zlib_recv_stream))
+ {
+ session->fio_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);
+ (void)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;
+
+ (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);
+}
+
+
+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,
+ struct SPDYF_Stream * stream,
+ enum SPDY_RST_STREAM_STATUS status)
+{
+ struct SPDYF_Response_Queue *response_to_queue;
+ struct SPDYF_Control_Frame *control_frame;
+ uint32_t *data;
+ uint32_t stream_id;
+
+ if(NULL == stream)
+ stream_id = 0;
+ else
+ stream_id = stream->stream_id;
+
+ 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;
+ response_to_queue->stream = stream;
+
+ SPDYF_queue_response (response_to_queue,
+ session,
+ -1);
+
+ return SPDY_YES;
+}
+
+
+int
+SPDYF_prepare_window_update (struct SPDY_Session *session,
+ struct SPDYF_Stream * stream,
+ int32_t delta_window_size)
+{
+ struct SPDYF_Response_Queue *response_to_queue;
+ struct SPDYF_Control_Frame *control_frame;
+ uint32_t *data;
+
+ SPDYF_ASSERT(NULL != stream, "stream cannot be NULL");
+
+ 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->stream_id);
+ *(data + 1) = HTON31(delta_window_size);
+
+ control_frame->control_bit = 1;
+ control_frame->version = SPDY_VERSION;
+ control_frame->type = SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE;
+ control_frame->flags = 0;
+
+ response_to_queue->control_frame = control_frame;
+ response_to_queue->process_response_handler = &SPDYF_handler_write_window_update;
+ response_to_queue->data = data;
+ response_to_queue->data_size = 8;
+ response_to_queue->stream = stream;
+
+ SPDYF_queue_response (response_to_queue,
+ session,
+ -1);
+
+ return SPDY_YES;
+}