mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-10-01 04:03:18 +03:00
614 lines
14 KiB
C
614 lines
14 KiB
C
/*
|
|
This file is part of libmicrospdy
|
|
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"
|
|
#include <ctype.h>
|
|
|
|
|
|
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 || 0 == (len = strlen(name)))
|
|
return SPDY_INPUT_ERROR;
|
|
|
|
for(i=0; i<len; ++i)
|
|
{
|
|
if(isupper((int) name[i]))
|
|
return SPDY_INPUT_ERROR;
|
|
}
|
|
|
|
if(NULL == container->name && NULL == container->value)
|
|
{
|
|
//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 == (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 == (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(NULL == container->name && NULL == container->value)
|
|
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(NULL == container->name && NULL == container->value)
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|