/* 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 . */ /** * @file stream.c * @brief SPDY streams handling * @author Andrey Uzunov */ #include "platform.h" #include "structures.h" #include "internal.h" #include "session.h" int SPDYF_stream_new (struct SPDY_Session *session) { uint32_t stream_id; uint32_t assoc_stream_id; uint8_t priority; uint8_t slot; size_t buffer_pos = session->read_buffer_beginning; struct SPDYF_Stream *stream; struct SPDYF_Control_Frame *frame; if((session->read_buffer_offset - session->read_buffer_beginning) < 10) { //not all fields are received to create new stream return SPDY_NO; } frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls; //get stream id of the new stream memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4); stream_id = NTOH31(stream_id); session->read_buffer_beginning += 4; if(stream_id <= session->last_in_stream_id || 0==(stream_id % 2)) { //wrong stream id sent by client //GOAWAY with PROTOCOL_ERROR MUST be sent //TODO //ignore frame session->frame_handler = &SPDYF_handler_ignore_frame; return SPDY_NO; } else if(session->is_goaway_sent) { //the client is not allowed to create new streams anymore //we MUST ignore the frame session->frame_handler = &SPDYF_handler_ignore_frame; return SPDY_NO; } //set highest stream id for session session->last_in_stream_id = stream_id; //get assoc stream id of the new stream //this value is used with SPDY PUSH, thus nothing to do with it here memcpy(&assoc_stream_id, session->read_buffer + session->read_buffer_beginning, 4); assoc_stream_id = NTOH31(assoc_stream_id); session->read_buffer_beginning += 4; //get stream priority (3 bits) //after it there are 5 bits that are not used priority = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning) >> 5; session->read_buffer_beginning++; //get slot (see SPDY draft) slot = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning); session->read_buffer_beginning++; if(NULL == (stream = malloc(sizeof(struct SPDYF_Stream)))) { SPDYF_DEBUG("No memory"); //revert buffer state session->read_buffer_beginning = buffer_pos; return SPDY_NO; } memset(stream,0, sizeof(struct SPDYF_Stream)); stream->session = session; stream->stream_id = stream_id; stream->assoc_stream_id = assoc_stream_id; stream->priority = priority; stream->slot = slot; stream->is_in_closed = (frame->flags & SPDY_SYN_STREAM_FLAG_FIN) != 0; stream->flag_unidirectional = (frame->flags & SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL) != 0; stream->is_out_closed = stream->flag_unidirectional; stream->is_server_initiator = false; stream->window_size = SPDYF_INITIAL_WINDOW_SIZE; //put the stream to the list of streams for the session DLL_insert(session->streams_head, session->streams_tail, stream); return SPDY_YES; } void SPDYF_stream_destroy(struct SPDYF_Stream *stream) { SPDY_name_value_destroy(stream->headers); free(stream); stream = NULL; } void SPDYF_stream_set_flags_on_write(struct SPDYF_Response_Queue *response_queue) { struct SPDYF_Stream * stream = response_queue->stream; if(NULL != response_queue->data_frame) { stream->is_out_closed = (bool)(response_queue->data_frame->flags & SPDY_DATA_FLAG_FIN); } else if(NULL != response_queue->control_frame) { switch(response_queue->control_frame->type) { case SPDY_CONTROL_FRAME_TYPES_SYN_REPLY: stream->is_out_closed = (bool)(response_queue->control_frame->flags & SPDY_SYN_REPLY_FLAG_FIN); break; case SPDY_CONTROL_FRAME_TYPES_RST_STREAM: if(NULL != stream) { stream->is_out_closed = true; stream->is_in_closed = true; } break; } } } //TODO add function *on_read struct SPDYF_Stream * SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session) { struct SPDYF_Stream * stream = session->streams_head; while(NULL != stream && stream_id != stream->stream_id) { stream = stream->next; } return stream; }