/*
    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;
}