END_TEST START_TEST (test_spdy_control_frame_parse) { spdy_zlib_context zlib_ctx; spdy_control_frame frame; spdy_data data; int ret; spdy_zlib_inflate_init(&zlib_ctx); spdy_control_frame_init(&frame); ret = spdy_control_frame_parse( &frame, spdy_data_use(&data, test_control_syn_stream_frame, 296), &zlib_ctx); fail_unless(ret == SPDY_ERROR_NONE, "spdy_control_frame_parse failed."); fail_unless(data.cursor - data.data == 296, "data_used is incorrect."); fail_unless(frame.version == 2, "Version failed."); fail_unless(frame.type == SPDY_CTRL_SYN_STREAM, "Type failed."); fail_unless(frame.flags == 1, "Flag failed."); fail_unless(frame.length == 288, "Length failed."); spdy_control_frame_destroy(&frame); spdy_zlib_inflate_end(&zlib_ctx); }
END_TEST START_TEST (test_spdy_syn_stream_parse) { #if 0 int ret; spdy_syn_stream syn_stream; spdy_data data; struct hash *hash = NULL; spdy_data_use(&data, test_control_syn_stream_frame+8, 288); ret = spdy_syn_stream_parse(&syn_stream, hash, &data, 288); fail_unless(ret == 0, "spdy_syn_stream_parse failed."); spdy_syn_stream_destroy(&syn_stream); #endif }
END_TEST START_TEST (test_spdy_control_frame_parse_pack_header) { spdy_control_frame frame; char out[8]; spdy_data data; int ret; size_t got; spdy_control_frame_init(&frame); ret = spdy_control_frame_parse_header(&frame, spdy_data_use(&data, test_control_syn_stream_frame, 8)); fail_unless(ret == 0, "spdy_control_frame_parse_header failed."); ret = spdy_control_frame_pack_header(out, sizeof(out), &got, &frame); fail_unless(ret == 0, "spdy_control_frame_pack_header failed."); fail_unless(memcmp(out, test_control_syn_stream_frame, 8) == 0, "Input is different than repacked frame."); fail_unless(got == 8); }
END_TEST START_TEST (test_spdy_syn_stream_parse) { int ret; spdy_zlib_context zlib_ctx; spdy_syn_stream syn_stream; spdy_data data; ret = spdy_zlib_inflate_init(&zlib_ctx); fail_unless(ret == 0, "spdy_zlib_inflate_init failed."); ret = spdy_syn_stream_parse( &syn_stream, spdy_data_use(&data, test_control_syn_stream_frame+8, 288), 288, &zlib_ctx); fail_unless(ret == 0, "spdy_syn_stream_parse failed."); spdy_syn_stream_destroy(&syn_stream); spdy_zlib_inflate_end(&zlib_ctx); }
END_TEST START_TEST (test_spdy_syn_reply_parse) { int ret; spdy_zlib_context zlib_ctx; spdy_syn_reply syn_reply; spdy_data data; ret = spdy_zlib_inflate_init(&zlib_ctx); fail_unless(ret == 0, "spdy_zlib_inflate_init failed."); ret = spdy_syn_reply_parse( &syn_reply, spdy_data_use(&data, test_control_syn_reply_frame+8, 55), 55, &zlib_ctx); fail_unless(ret == 0, "spdy_syn_reply_parse failed."); spdy_syn_reply_destroy(&syn_reply); spdy_zlib_inflate_end(&zlib_ctx); }
static int parse_append(struct spindly_phys *phys, bool *more) { /* the existing data is too small, merge it with the next in the malloc'ed buffer */ struct list_node *n = _spindly_list_first(&phys->inq); struct list_node *next = _spindly_list_next(n); struct spindly_indata *in= (struct spindly_indata *)next; spdy_data *data = &phys->data; size_t needed; size_t copylen; if(in) { *more = true; needed = data->needed + in->datalen; copylen = data->data_end - data->cursor; if(phys->parsealloc < needed) { char *newp = realloc(phys->parse, needed); if(!newp) return SPINDLYE_NOMEM; phys->parsealloc = needed; } /* the buffer might already be used by the first chunk, so we either copy * new data to it or we move the trailing piece to the start of the * buffer */ memmove(phys->parse, data->cursor, copylen); phys->parselen = copylen; /* append the entire next chunk */ memcpy(&phys->parse[copylen], in->data, in->datalen); /* now store the combined data amount */ spdy_data_use(&phys->data, phys->parse, needed); } else *more = false; return SPINDLYE_OK; }
/* * Create a handle for a single duplex physical connection, SIDE is either * client or server - what side the handle is made to handle. PROTVER is the * specific SPDY protocol version. * * TODO: provide a means to replace the memory functions */ struct spindly_phys *spindly_phys_init(spindly_side_t side, spindly_spdyver_t protver, struct spindly_phys_config *config) { struct spindly_phys *phys; int rc; struct spindly_outdata *od; /* this is the first malloc, it should use the malloc function provided in the config struct if set, but probably cannot use the MALLOC macro */ phys = malloc(sizeof(struct spindly_phys)); if(!phys) goto fail; phys->config = config; phys->side = side; phys->protver = protver; phys->num_streams = 0; phys->streamid = side == SPINDLY_SIDE_CLIENT?1:2; phys->outgoing = NULL; phys->outgoing_tosend = 0; /* create zlib contexts for incoming and outgoing data */ rc = spdy_zlib_inflate_init(&phys->zlib_in); if(rc) goto fail; rc = spdy_zlib_inflate_init(&phys->zlib_out); if(rc) goto fail; _spindly_list_init(&phys->streams); _spindly_list_init(&phys->outq); _spindly_list_init(&phys->inq); _spindly_list_init(&phys->pendq); /* now add all outdata nodes to the pending queue */ for(rc=0; rc < PHYS_NUM_OUTDATA; rc++) { od = CALLOC(phys, sizeof(struct spindly_outdata)); if(!od) goto fail; _spindly_list_add(&phys->pendq, &od->node); } /* init receiver variables */ spdy_frame_init(&phys->frame); spdy_data_use(&phys->data, NULL, 0); /* for stream-ID to stream struct lookups */ _spindly_hash_init(&phys->streamhash, phys); return phys; fail: spdy_zlib_inflate_end(&phys->zlib_in); spdy_zlib_inflate_end(&phys->zlib_out); /* TODO: clean up the pendq list */ if(phys) free(phys); return NULL; }
/* * Returns information about incoming data, split up for consumption. * Subsequent calls will return the next result and so on until there's * nothing left to demux - until spindly_phys_incoming() is called again to * feed it with more data. * * When this function returns that there is no more message, it may still hold * trailing data that forms the beginning of the subsequent message. * * 'ptr' must point to the correct struct, read the first 'type' field of that * to know how to interpret the rest! */ spindly_error_t spindly_phys_demux(struct spindly_phys *phys, struct spindly_demux *ptr) { struct list_node *n = _spindly_list_first(&phys->inq); struct spindly_indata *in= (struct spindly_indata *)n; int rc = SPINDLYE_OK; assert(ptr != NULL); ptr->type = SPINDLY_DX_NONE; do { if(phys->data.data_end <= phys->data.cursor) /* if the previously stored data is all consumed then get the current queue data */ spdy_data_use(&phys->data, in->data, in->datalen); /* * Parse data. The parsing function wants a full frame to be in a * contiguous buffer so unless it is, we must create a full frame from the * input we have. */ rc = spdy_frame_parse(&phys->frame, phys, &phys->data); if(rc == SPDY_ERROR_NONE) { if(phys->frame.type == SPDY_CONTROL_FRAME) { spdy_syn_stream *syn; spdy_syn_reply *rep; struct spindly_stream *stream; struct hashnode *n; switch(phys->frame.frame.control.type) { case SPDY_CTRL_SYN_STREAM: /* * At this point there's a syn_stream struct that needs to be * converted to a full spinly_stream struct! * * phys->frame.frame.control.obj.syn_stream */ syn = &phys->frame.frame.control.obj.syn_stream; rc = _spindly_stream_init(phys, syn->priority, &stream, NULL, NULL, syn->stream_id); if(rc) /* TODO: how do we deal with a failure here? */ break; ptr->type = SPINDLY_DX_STREAM_REQ; ptr->msg.stream.stream = stream; break; case SPDY_CTRL_SYN_REPLY: /* * At this point there's a syn_reply struct that needs to be * converted to a full spinly_stream struct! * * phys->frame.frame.control.obj.syn_reply */ rep = &phys->frame.frame.control.obj.syn_reply; n = _spindly_hash_get(&phys->streamhash, rep->stream_id); if(!n) /* received a SYN_REPLY for an unknown streamid */ rc = SPINDLYE_PROTOCOL; else { stream = n->ptr; stream->state = STREAM_ACKED; ptr->type = SPINDLY_DX_STREAM_ACK; ptr->msg.stream.stream = stream; } break; case SPDY_CTRL_RST_STREAM: assert(0); /* not implemented yet! */ break; case SPDY_CTRL_SETTINGS: assert(0); /* not implemented yet! */ break; case SPDY_CTRL_NOOP: assert(0); /* not implemented yet! */ break; case SPDY_CTRL_PING: assert(0); /* not implemented yet! */ break; case SPDY_CTRL_GOAWAY: assert(0); /* not implemented yet! */ break; case SPDY_CTRL_HEADERS: assert(0); /* not implemented yet! */ break; case SPDY_CTRL_WINDOW_UPDATE: assert(0); /* not implemented yet! */ break; default: assert(0); /* internal error */ break; } spdy_frame_destroy(&phys->frame); } else { /* data */ ptr->type = SPINDLY_DX_DATA; } /* TODO: if the complete inq node was consumed, call the callback */ return SPINDLYE_OK; } else if(rc == SPDY_ERROR_INSUFFICIENT_DATA) { /* if there's too little data to parse, merge the buffer with the next * in the queue and loop and parse the bigger one */ bool more; rc = parse_append(phys, &more); if(!more) /* there's no more right now */ return SPINDLYE_OK; } } while(!rc); return rc; }