diff options
Diffstat (limited to 'src/microspdy/structures.c')
-rw-r--r-- | src/microspdy/structures.c | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/src/microspdy/structures.c b/src/microspdy/structures.c new file mode 100644 index 00000000..f00806bc --- /dev/null +++ b/src/microspdy/structures.c @@ -0,0 +1,638 @@ +/* + 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 structures.c + * @brief Functions for handling most of the structures in defined + * in structures.h + * @author Andrey Uzunov + */ + +#include "platform.h" +#include "structures.h" +#include "internal.h" +#include "session.h" +//TODO not for here? +#include <ctype.h> + + +int +SPDYF_name_value_is_empty(struct SPDY_NameValue *container) +{ + SPDYF_ASSERT(NULL != container, "NULL is not an empty container!"); + return (NULL == container->name && NULL == container->value) ? SPDY_YES : SPDY_NO; +} + +struct SPDY_NameValue * +SPDY_name_value_create () +{ + struct SPDY_NameValue *pair; + + if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue)))) + return NULL; + + memset (pair, 0, sizeof (struct SPDY_NameValue)); + + return pair; +} + + +int +SPDY_name_value_add (struct SPDY_NameValue *container, + const char *name, + const char *value) +{ + unsigned int i; + unsigned int len; + struct SPDY_NameValue *pair; + struct SPDY_NameValue *temp; + char **temp_value; + char *temp_string; + + if(NULL == container || NULL == name || NULL == value || 0 == (len = strlen(name))) + return SPDY_INPUT_ERROR; + //TODO there is old code handling value==NULL + //update it to handle strlen(value)==0 + + for(i=0; i<len; ++i) + { + if(isupper((int) name[i])) + return SPDY_INPUT_ERROR; + } + + if(SPDYF_name_value_is_empty(container)) + { + //container is empty/just created + if (NULL == (container->name = strdup (name))) + { + return SPDY_NO; + } + if (NULL == (container->value = malloc(sizeof(char *)))) + { + free(container->name); + return SPDY_NO; + } + /*if(NULL == value) + container->value[0] = NULL; + else */if (NULL == (container->value[0] = strdup (value))) + { + free(container->value); + free(container->name); + return SPDY_NO; + } + container->num_values = 1; + return SPDY_YES; + } + + pair = container; + while(NULL != pair) + { + if(0 == strcmp(pair->name, name)) + { + //the value will be added to this pair + break; + } + pair = pair->next; + } + + if(NULL == pair) + { + //the name doesn't exist in container, add new pair + if(NULL == (pair = malloc(sizeof(struct SPDY_NameValue)))) + return SPDY_NO; + + memset(pair, 0, sizeof(struct SPDY_NameValue)); + + if (NULL == (pair->name = strdup (name))) + { + free(pair); + return SPDY_NO; + } + if (NULL == (pair->value = malloc(sizeof(char *)))) + { + free(pair->name); + free(pair); + return SPDY_NO; + } + /*if(NULL == value) + pair->value[0] = NULL; + else */if (NULL == (pair->value[0] = strdup (value))) + { + free(pair->value); + free(pair->name); + free(pair); + return SPDY_NO; + } + pair->num_values = 1; + + temp = container; + while(NULL != temp->next) + temp = temp->next; + temp->next = pair; + pair->prev = temp; + + return SPDY_YES; + } + + //check for duplication (case sensitive) + for(i=0; i<pair->num_values; ++i) + if(0 == strcmp(pair->value[i], value)) + return SPDY_NO; + + if(strlen(pair->value[0]) > 0) + { + //the value will be appended to the others for this name + if (NULL == (temp_value = malloc((pair->num_values + 1) * sizeof(char *)))) + { + return SPDY_NO; + } + memcpy(temp_value, pair->value, pair->num_values * sizeof(char *)); + if (NULL == (temp_value[pair->num_values] = strdup (value))) + { + free(temp_value); + return SPDY_NO; + } + free(pair->value); + pair->value = temp_value; + ++pair->num_values; + return SPDY_YES; + } + + //just replace the empty value + + if (NULL == (temp_string = strdup (value))) + { + return SPDY_NO; + } + free(pair->value[0]); + pair->value[0] = temp_string; + + return SPDY_YES; +} + + +const char * const * +SPDY_name_value_lookup (struct SPDY_NameValue *container, + const char *name, + int *num_values) +{ + struct SPDY_NameValue *temp = container; + + if(NULL == container || NULL == name || NULL == num_values) + return NULL; + if(SPDYF_name_value_is_empty(container)) + return NULL; + + do + { + if(strcmp(name, temp->name) == 0) + { + *num_values = temp->num_values; + return (const char * const *)temp->value; + } + + temp = temp->next; + } + while(NULL != temp); + + return NULL; +} + + +void +SPDY_name_value_destroy (struct SPDY_NameValue *container) +{ + unsigned int i; + struct SPDY_NameValue *temp = container; + + while(NULL != temp) + { + container = container->next; + free(temp->name); + for(i=0; i<temp->num_values; ++i) + free(temp->value[i]); + free(temp->value); + free(temp); + temp=container; + } +} + + +int +SPDY_name_value_iterate (struct SPDY_NameValue *container, + SPDY_NameValueIterator iterator, + void *iterator_cls) +{ + int count; + int ret; + struct SPDY_NameValue *temp = container; + + if(NULL == container) + return SPDY_INPUT_ERROR; + + //check if container is an empty struct + if(SPDYF_name_value_is_empty(container)) + return 0; + + count = 0; + + if(NULL == iterator) + { + do + { + ++count; + temp=temp->next; + } + while(NULL != temp); + + return count; + } + + //code duplication for avoiding if here + do + { + ++count; + ret = iterator(iterator_cls, temp->name, (const char * const *)temp->value, temp->num_values); + temp=temp->next; + } + while(NULL != temp && SPDY_YES == ret); + + return count; +} + +void +SPDY_destroy_response(struct SPDY_Response *response) +{ + if(NULL == response) + return; + free(response->data); + free(response->headers); + free(response); +} + + +struct SPDYF_Response_Queue * +SPDYF_response_queue_create(bool is_data, + void *data, + size_t data_size, + struct SPDY_Response *response, + struct SPDYF_Stream *stream, + bool closestream, + SPDYF_ResponseQueueResultCallback frqcb, + void *frqcb_cls, + SPDY_ResponseResultCallback rrcb, + void *rrcb_cls) +{ + struct SPDYF_Response_Queue *head = NULL; + struct SPDYF_Response_Queue *prev; + struct SPDYF_Response_Queue *response_to_queue; + struct SPDYF_Control_Frame *control_frame; + struct SPDYF_Data_Frame *data_frame; + unsigned int i; + bool is_last; + + SPDYF_ASSERT((! is_data) + || ((0 == data_size) && (NULL != response->rcb)) + || ((0 < data_size) && (NULL == response->rcb)), + "either data or request->rcb must not be null"); + + if (is_data && (data_size > SPDY_MAX_SUPPORTED_FRAME_SIZE)) + { + //separate the data in more frames and add them to the queue + + prev=NULL; + for(i = 0; i < data_size; i += SPDY_MAX_SUPPORTED_FRAME_SIZE) + { + is_last = (i + SPDY_MAX_SUPPORTED_FRAME_SIZE) >= data_size; + + if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) + goto free_and_fail; + + memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); + if(0 == i) + head = response_to_queue; + + if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame)))) + { + free(response_to_queue); + goto free_and_fail; + } + memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame)); + data_frame->control_bit = 0; + data_frame->stream_id = stream->stream_id; + if(is_last && closestream) + data_frame->flags |= SPDY_DATA_FLAG_FIN; + + response_to_queue->data_frame = data_frame; + response_to_queue->process_response_handler = &SPDYF_handler_write_data; + response_to_queue->is_data = is_data; + response_to_queue->stream = stream; + if(is_last) + { + response_to_queue->frqcb = frqcb; + response_to_queue->frqcb_cls = frqcb_cls; + response_to_queue->rrcb = rrcb; + response_to_queue->rrcb_cls = rrcb_cls; + } + response_to_queue->data = data + i; + response_to_queue->data_size = is_last + ? (data_size - 1) % SPDY_MAX_SUPPORTED_FRAME_SIZE + 1 + : SPDY_MAX_SUPPORTED_FRAME_SIZE; + response_to_queue->response = response; + + response_to_queue->prev = prev; + if(NULL != prev) + prev->next = response_to_queue; + prev = response_to_queue; + } + + return head; + + //for GOTO + free_and_fail: + while(NULL != head) + { + response_to_queue = head; + head = head->next; + free(response_to_queue->data_frame); + free(response_to_queue); + } + return NULL; + } + + //create only one frame for data, data with callback or control frame + + if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) + { + return NULL; + } + memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); + + if(is_data) + { + if(NULL == (data_frame = malloc(sizeof(struct SPDYF_Data_Frame)))) + { + free(response_to_queue); + return NULL; + } + memset(data_frame, 0, sizeof(struct SPDYF_Data_Frame)); + data_frame->control_bit = 0; + data_frame->stream_id = stream->stream_id; + if(closestream && NULL == response->rcb) + data_frame->flags |= SPDY_DATA_FLAG_FIN; + + response_to_queue->data_frame = data_frame; + response_to_queue->process_response_handler = &SPDYF_handler_write_data; + } + else + { + if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) + { + free(response_to_queue); + return NULL; + } + memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); + control_frame->control_bit = 1; + control_frame->version = SPDY_VERSION; + control_frame->type = SPDY_CONTROL_FRAME_TYPES_SYN_REPLY; + if(closestream) + control_frame->flags |= SPDY_SYN_REPLY_FLAG_FIN; + + response_to_queue->control_frame = control_frame; + response_to_queue->process_response_handler = &SPDYF_handler_write_syn_reply; + } + + response_to_queue->is_data = is_data; + response_to_queue->stream = stream; + response_to_queue->frqcb = frqcb; + response_to_queue->frqcb_cls = frqcb_cls; + response_to_queue->rrcb = rrcb; + response_to_queue->rrcb_cls = rrcb_cls; + response_to_queue->data = data; + response_to_queue->data_size = data_size; + response_to_queue->response = response; + + return response_to_queue; +} + + +void +SPDYF_response_queue_destroy(struct SPDYF_Response_Queue *response_queue) +{ + //data is not copied to the struct but only linked + //but this is not valid for GOAWAY and RST_STREAM + if(!response_queue->is_data + && (SPDY_CONTROL_FRAME_TYPES_RST_STREAM == response_queue->control_frame->type + || SPDY_CONTROL_FRAME_TYPES_GOAWAY == response_queue->control_frame->type)) + { + free(response_queue->data); + } + if(response_queue->is_data) + free(response_queue->data_frame); + else + free(response_queue->control_frame); + + free(response_queue); +} + + +/* Needed by testcase to be extern -- should this be + in the header? */ +_MHD_EXTERN ssize_t +SPDYF_name_value_to_stream(struct SPDY_NameValue * container[], + int num_containers, + void **stream) +{ + size_t size; + int32_t num_pairs = 0; + int32_t value_size; + int32_t name_size; + int32_t temp; + unsigned int i; + unsigned int offset; + unsigned int value_offset; + struct SPDY_NameValue * iterator; + int j; + + size = 4; //for num pairs + + for(j=0; j<num_containers; ++j) + { + iterator = container[j]; + while(iterator != NULL) + { + ++num_pairs; + size += 4 + strlen(iterator->name); //length + string + + SPDYF_ASSERT(iterator->num_values>0, "num_values is 0"); + + size += 4; //value length + + for(i=0; i<iterator->num_values; ++i) + { + //if(NULL == iterator->value[i]) + // continue; + size += strlen(iterator->value[i]); // string + if(i/* || !strlen(iterator->value[i])*/) ++size; //NULL separator + } + + iterator = iterator->next; + } + } + + if(NULL == (*stream = malloc(size))) + { + return -1; + } + + //put num_pairs to the stream + num_pairs = htonl(num_pairs); + memcpy(*stream, &num_pairs, 4); + offset = 4; + + //put all other headers to the stream + for(j=0; j<num_containers; ++j) + { + iterator = container[j]; + while(iterator != NULL) + { + name_size = strlen(iterator->name); + temp = htonl(name_size); + memcpy(*stream + offset, &temp, 4); + offset += 4; + strncpy(*stream + offset, iterator->name, name_size); + offset += name_size; + + value_offset = offset; + offset += 4; + for(i=0; i<iterator->num_values; ++i) + { + if(i /*|| !strlen(iterator->value[0])*/) + { + memset(*stream + offset, 0, 1); + ++offset; + //if(!i) continue; + } + //else if(NULL != iterator->value[i]) + //{ + strncpy(*stream + offset, iterator->value[i], strlen(iterator->value[i])); + offset += strlen(iterator->value[i]); + //} + } + value_size = offset - value_offset - 4; + value_size = htonl(value_size); + memcpy(*stream + value_offset, &value_size, 4); + + iterator = iterator->next; + } + } + + SPDYF_ASSERT(offset == size,"offset is wrong"); + + return size; +} + + +/* Needed by testcase to be extern -- should this be + in the header? */ +_MHD_EXTERN int +SPDYF_name_value_from_stream(void *stream, + size_t size, + struct SPDY_NameValue ** container) +{ + int32_t num_pairs; + int32_t value_size; + int32_t name_size; + int i; + unsigned int offset = 0; + unsigned int value_end_offset; + char *name; + char *value; + + if(NULL == (*container = SPDY_name_value_create ())) + { + return SPDY_NO; + } + + //get number of pairs + memcpy(&num_pairs, stream, 4); + offset = 4; + num_pairs = ntohl(num_pairs); + + if(num_pairs > 0) + { + for(i = 0; i < num_pairs; ++i) + { + //get name size + memcpy(&name_size, stream + offset, 4); + offset += 4; + name_size = ntohl(name_size); + //get name + if(NULL == (name = strndup(stream + offset, name_size))) + { + SPDY_name_value_destroy(*container); + return SPDY_NO; + } + offset+=name_size; + + //get value size + memcpy(&value_size, stream + offset, 4); + offset += 4; + value_size = ntohl(value_size); + value_end_offset = offset + value_size; + //get value + do + { + if(NULL == (value = strndup(stream + offset, value_size))) + { + free(name); + SPDY_name_value_destroy(*container); + return SPDY_NO; + } + offset += strlen(value); + if(offset < value_end_offset) + ++offset; //NULL separator + + //add name/value to the struct + if(SPDY_YES != SPDY_name_value_add(*container, name, value)) + { + free(name); + free(value); + SPDY_name_value_destroy(*container); + return SPDY_NO; + } + free(value); + } + while(offset < value_end_offset); + + free(name); + + if(offset != value_end_offset) + { + SPDY_name_value_destroy(*container); + return SPDY_INPUT_ERROR; + } + } + } + + if(offset == size) + return SPDY_YES; + + SPDY_name_value_destroy(*container); + return SPDY_INPUT_ERROR; +} |