1/*
2    This file is part of libmicrospdy
3    Copyright Copyright (C) 2012 Andrey Uzunov
4
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/**
20 * @file stream.c
21 * @brief  SPDY streams handling
22 * @author Andrey Uzunov
23 */
24
25#include "platform.h"
26#include "structures.h"
27#include "internal.h"
28#include "session.h"
29
30
31int
32SPDYF_stream_new (struct SPDY_Session *session)
33{
34	uint32_t stream_id;
35	uint32_t assoc_stream_id;
36	uint8_t priority;
37	uint8_t slot;
38	size_t buffer_pos = session->read_buffer_beginning;
39	struct SPDYF_Stream *stream;
40	struct SPDYF_Control_Frame *frame;
41
42	if((session->read_buffer_offset - session->read_buffer_beginning) < 10)
43	{
44		//not all fields are received to create new stream
45		return SPDY_NO;
46	}
47
48	frame = (struct SPDYF_Control_Frame *)session->frame_handler_cls;
49
50	//get stream id of the new stream
51    memcpy(&stream_id, session->read_buffer + session->read_buffer_beginning, 4);
52	stream_id = NTOH31(stream_id);
53	session->read_buffer_beginning += 4;
54	if(stream_id <= session->last_in_stream_id || 0==(stream_id % 2))
55	{
56		//wrong stream id sent by client
57		//GOAWAY with PROTOCOL_ERROR MUST be sent
58		//TODO
59
60		//ignore frame
61		session->frame_handler = &SPDYF_handler_ignore_frame;
62		return SPDY_NO;
63	}
64	else if(session->is_goaway_sent)
65	{
66		//the client is not allowed to create new streams anymore
67		//we MUST ignore the frame
68		session->frame_handler = &SPDYF_handler_ignore_frame;
69		return SPDY_NO;
70	}
71
72	//set highest stream id for session
73	session->last_in_stream_id = stream_id;
74
75	//get assoc stream id of the new stream
76	//this value is used with SPDY PUSH, thus nothing to do with it here
77    memcpy(&assoc_stream_id, session->read_buffer + session->read_buffer_beginning, 4);
78	assoc_stream_id = NTOH31(assoc_stream_id);
79	session->read_buffer_beginning += 4;
80
81	//get stream priority (3 bits)
82	//after it there are 5 bits that are not used
83	priority = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning) >> 5;
84	session->read_buffer_beginning++;
85
86	//get slot (see SPDY draft)
87	slot = *(uint8_t *)(session->read_buffer + session->read_buffer_beginning);
88	session->read_buffer_beginning++;
89
90	if(NULL == (stream = malloc(sizeof(struct SPDYF_Stream))))
91	{
92		SPDYF_DEBUG("No memory");
93		//revert buffer state
94		session->read_buffer_beginning = buffer_pos;
95		return SPDY_NO;
96	}
97	memset(stream,0, sizeof(struct SPDYF_Stream));
98	stream->session = session;
99	stream->stream_id = stream_id;
100	stream->assoc_stream_id = assoc_stream_id;
101	stream->priority = priority;
102	stream->slot = slot;
103	stream->is_in_closed = (frame->flags & SPDY_SYN_STREAM_FLAG_FIN) != 0;
104	stream->flag_unidirectional = (frame->flags & SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL) != 0;
105	stream->is_out_closed = stream->flag_unidirectional;
106	stream->is_server_initiator = false;
107	stream->window_size = SPDYF_INITIAL_WINDOW_SIZE;
108
109	//put the stream to the list of streams for the session
110	DLL_insert(session->streams_head, session->streams_tail, stream);
111
112	return SPDY_YES;
113}
114
115
116void
117SPDYF_stream_destroy(struct SPDYF_Stream *stream)
118{
119	SPDY_name_value_destroy(stream->headers);
120	free(stream);
121	stream = NULL;
122}
123
124
125void
126SPDYF_stream_set_flags_on_write(struct SPDYF_Response_Queue *response_queue)
127{
128	struct SPDYF_Stream * stream = response_queue->stream;
129
130	if(NULL != response_queue->data_frame)
131	{
132		stream->is_out_closed = (bool)(response_queue->data_frame->flags & SPDY_DATA_FLAG_FIN);
133	}
134	else if(NULL != response_queue->control_frame)
135	{
136		switch(response_queue->control_frame->type)
137		{
138			case SPDY_CONTROL_FRAME_TYPES_SYN_REPLY:
139				stream->is_out_closed = (bool)(response_queue->control_frame->flags & SPDY_SYN_REPLY_FLAG_FIN);
140				break;
141
142			case SPDY_CONTROL_FRAME_TYPES_RST_STREAM:
143				if(NULL != stream)
144				{
145					stream->is_out_closed = true;
146					stream->is_in_closed = true;
147				}
148				break;
149
150		}
151	}
152}
153
154
155//TODO add function *on_read
156
157
158struct SPDYF_Stream *
159SPDYF_stream_find(uint32_t stream_id, struct SPDY_Session * session)
160{
161  struct SPDYF_Stream * stream = session->streams_head;
162
163  while(NULL != stream && stream_id != stream->stream_id)
164  {
165    stream = stream->next;
166  }
167
168  return stream;
169}
170