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