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