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