aboutsummaryrefslogtreecommitdiff
path: root/src/microspdy/structures.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microspdy/structures.c')
-rw-r--r--src/microspdy/structures.c638
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;
+}