diff options
Diffstat (limited to 'src/microspdy/session.c')
-rw-r--r-- | src/microspdy/session.c | 1769 |
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; +} |