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