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