Working on http thread loops

This commit is contained in:
2014-09-02 22:12:58 +01:00
parent e15e94dad2
commit 565bb6df30
11 changed files with 242 additions and 176 deletions

View File

@@ -112,7 +112,7 @@
</item>
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
</item>
<folder path="TestFiles/f1">
<!--<folder path="TestFiles/f1">
<cTool>
<incDir>
<pElem>.</pElem>
@@ -129,7 +129,7 @@
<linkerOptionItem>-lcunit</linkerOptionItem>
</linkerLibItems>
</linkerTool>
</folder>
</folder>-->
<item path="lib/http_parser.c" ex="false" tool="0" flavor2="0">
</item>
<item path="lib/http_parser.h" ex="false" tool="3" flavor2="0">
@@ -258,7 +258,7 @@
</item>
<item path="content/public_html/lorem.txt" ex="false" tool="3" flavor2="0">
</item>
<folder path="TestFiles/f1">
<!--<folder path="TestFiles/f1">
<cTool>
<incDir>
<pElem>.</pElem>
@@ -275,7 +275,7 @@
<linkerOptionItem>-lcunit</linkerOptionItem>
</linkerLibItems>
</linkerTool>
</folder>
</folder>-->
<item path="lib/http_parser.c" ex="false" tool="0" flavor2="0">
</item>
<item path="lib/http_parser.h" ex="false" tool="3" flavor2="0">

View File

@@ -5,11 +5,34 @@
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <bits/stdio2.h>
#include "util.h"
#include "ut/utstring.h"
#include "http-body.h"
http_body_write_result _http_body_file_fill_buffer(char *buffer, size_t buf_len, size_t *read_len, FILE *src) {
assert(buffer!=NULL);
assert(buf_len>0);
assert(src!=NULL);
assert(read_len!=NULL);
if (*read_len > buf_len) {
*read_len = buf_len;
}
size_t read_count = fread(buffer, sizeof(char), *read_len, src);
if (read_count < *read_len) {
if (ferror(src) != 0) {
return HBWRITE_ERROR;
}
}
*read_len = read_count;
if (feof(src) != 0) {
return HBWRITE_DONE;
}
return HBWRITE_MORE;
}
http_body* http_body_new(http_body_type type, void* dataptr) {
http_body *body = calloc(1, sizeof(http_body));
ALLOC_CHECK(body);
@@ -36,9 +59,8 @@ http_body* http_body_new(http_body_type type, void* dataptr) {
return body;
}
void http_body_delete(http_body *body) {
void http_body_clear(http_body *body) {
assert(body!=NULL);
switch(body->type) {
case BODY_NONE:
break;
@@ -62,6 +84,11 @@ void http_body_delete(http_body *body) {
fatal("Invalid http body type");
break;
}
}
void http_body_delete(http_body *body) {
assert(body!=NULL);
http_body_clear(body);
free(body);
}
@@ -111,6 +138,13 @@ size_t http_body_len(http_body *body) {
}
return len;
}
void http_body_set_type(http_body *body, http_body_type newtype) {
assert(body!=NULL);
http_body_clear(body);
body->type = newtype;
}
//TODO: replace these with single function w/callbacks
http_body_write_result http_body_writeto_fd(http_body *body, int fd) {
assert(body!=NULL);
@@ -118,58 +152,52 @@ http_body_write_result http_body_writeto_fd(http_body *body, int fd) {
assert(body->data.ptr!=NULL);
}
char* buffer=NULL;
const size_t buffer_len = 16*1024;
char* buffer= NULL;
if (body->type == BODY_FILE) {
buffer = calloc(buffer_len, sizeof(char));
ALLOC_CHECK(buffer);
}
while(body->rOffset<http_body_len(body)) {
size_t write_len = http_body_len(body) - body->rOffset;
errno = EINVAL;
ssize_t result = -1;
ssize_t written = -1;
http_body_write_result result = HBWRITE_MORE;
switch(body->type) {
case BODY_STRING:
result = write(fd, body->data.str+body->rOffset, write_len);
written = write(fd, body->data.str+body->rOffset, write_len);
break;
case BODY_FILEMAP:
result = write(fd, body->data.filemap->map+body->rOffset, write_len);
written = write(fd, body->data.filemap->map+body->rOffset, write_len);
break;
case BODY_FILE:
if (buffer==NULL) {
buffer = calloc(buffer_len, sizeof(char));
ALLOC_CHECK(buffer);
case BODY_FILE:;
size_t read_count = write_len;
http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file);
if (read_res == HBWRITE_ERROR) {
result = read_res; break;
}
errno=0;
if (write_len > buffer_len) {
write_len = buffer_len;
}
size_t read_count = fread(buffer, sizeof(char), write_len, body->data.file);
if (read_count < write_len) {
if (ferror(body->data.file) != 0) {
free(buffer);
return HBWRITE_ERROR;
}
}
result = 0;
if (read_count > 0) {
result = write(fd, buffer, read_count);
written = write(fd, buffer, read_count);
}
if (feof(body->data.file) != 0) {
body->rOffset += result;
free(buffer);
return HBWRITE_DONE;
if (read_res == HBWRITE_DONE) {
result = read_res; break;
}
break;
}//switch body->type
if (result < 0) {
if (written < 0 || result == HBWRITE_ERROR) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
return HBWRITE_BLOCKED;
}
warning(true, "Error writing http body");
return HBWRITE_ERROR;
}
body->rOffset += result;
body->rOffset += written;
if (result == HBWRITE_DONE) {
break;
}
}//While data remaining
if (buffer != NULL) {
@@ -178,7 +206,7 @@ http_body_write_result http_body_writeto_fd(http_body *body, int fd) {
errno = 0;
return HBWRITE_DONE;
}
http_body_write_result http_body_writeto_str(http_body *body, const char** str) {
http_body_write_result http_body_writeto_str(http_body *body, char** str) {
assert(body!=NULL);
if (body->type!=BODY_NONE) {
assert(body->data.ptr!=NULL);
@@ -187,6 +215,10 @@ http_body_write_result http_body_writeto_str(http_body *body, const char** str)
char* buffer=NULL;
const size_t buffer_len = 16*1024;
if (body->type == BODY_FILE) {
buffer = calloc(buffer_len, sizeof(char));
ALLOC_CHECK(buffer);
}
while(body->rOffset<http_body_len(body)) {
size_t write_len = http_body_len(body) - body->rOffset;
@@ -197,53 +229,106 @@ http_body_write_result http_body_writeto_str(http_body *body, const char** str)
}
ALLOC_CHECK(*str);
errno = EINVAL;
ssize_t result = -1;
ssize_t written = -1;
http_body_write_result result = HBWRITE_MORE;
switch(body->type) {
case BODY_STRING:
case BODY_FILEMAP:
case BODY_FILEMAP:;
char* src = (body->type==BODY_STRING) ? body->data.str : body->data.filemap->map;
strncat(*str, src+body->rOffset, write_len);
result = write_len;
written = write_len;
break;
case BODY_FILE:
if (buffer==NULL) {
buffer = calloc(buffer_len, sizeof(char));
ALLOC_CHECK(buffer);
case BODY_FILE:;
size_t read_count = write_len;
http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file);
if (read_res == HBWRITE_ERROR) {
result = read_res; break;
}
if (write_len > buffer_len) {
write_len = buffer_len;
}
size_t read_count = fread(buffer, sizeof(char), write_len, body->data.file);
if (read_count < write_len) {
if (ferror(body->data.file) != 0) {
free(buffer);
return HBWRITE_ERROR;
}
}
result = 0;
if (read_count > 0) {
strncat(*str, buffer, read_count);
result = read_count;
written = read_count;
}
if (feof(body->data.file) != 0) {
body->rOffset += result;
free(buffer);
return HBWRITE_DONE;
if (read_res == HBWRITE_DONE) {
result = read_res; break;
}
break;
}//Switch body->type
body->rOffset += result;
body->rOffset += written;
if (result == HBWRITE_ERROR) {
if (buffer != NULL) {
free(buffer);
}
return HBWRITE_ERROR;
}
if (result == HBWRITE_DONE) {
break;
}
}//while data remaining
if (buffer != NULL) {
free(buffer);
}
errno = 0;
return HBWRITE_DONE;
}
http_body_write_result http_body_writeto_utstring(http_body *body, UT_string *utstr) {
assert(body!=NULL);
if (body->type!=BODY_NONE) {
assert(body->data.ptr!=NULL);
}
assert(utstr!=NULL);
char* buffer=NULL;
const size_t buffer_len = 16*1024;
if (body->type == BODY_FILE) {
buffer = calloc(buffer_len, sizeof(char));
ALLOC_CHECK(buffer);
}
while(body->rOffset<http_body_len(body)) {
size_t write_len = http_body_len(body) - body->rOffset;
ssize_t written = -1;
http_body_write_result result = HBWRITE_MORE;
switch(body->type) {
case BODY_STRING:
case BODY_FILEMAP:;
char* src = (body->type==BODY_STRING) ? body->data.str : body->data.filemap->map;
utstring_bincpy(utstr, src, write_len);
written = write_len;
break;
case BODY_FILE:;
size_t read_count = write_len;
http_body_write_result read_res = _http_body_file_fill_buffer(buffer, buffer_len, &read_count, body->data.file);
if (read_res == HBWRITE_ERROR) {
result = read_res; break;
}
if (read_count > 0) {
utstring_bincpy(utstr, src, write_len);
written = read_count;
}
if (read_res == HBWRITE_DONE) {
result = read_res; break;
}
break;
}//Switch body->type
body->rOffset += written;
if (result == HBWRITE_ERROR) {
if (buffer != NULL) {
free(buffer);
}
return HBWRITE_ERROR;
}
if (result == HBWRITE_DONE) {
break;
}
}//while data remaining
if (buffer != NULL) {
free(buffer);
}
return HBWRITE_DONE;
}

View File

@@ -38,13 +38,15 @@ extern "C" {
} http_body;
http_body* http_body_new(http_body_type type, void* dataptr);
void http_body_clear(http_body *body);
void http_body_delete(http_body *body);
size_t http_body_append_str(http_body *body, const char** str_ptr);
size_t http_body_append_str(http_body *body, const char* str, ssize_t str_len);
size_t http_body_len(http_body *body);
void http_body_set_type(http_body *body, http_body_type newtype);
http_body_write_result http_body_writeto_fd(http_body *body, int fd);
http_body_write_result http_body_writeto_str(http_body *body, const char* str, size_t str_len);
http_body_write_result http_body_writeto_str(http_body *body, char** str);
http_body_write_result http_body_writeto_utstring(http_body *body, UT_string *utstr);

View File

@@ -40,7 +40,7 @@ http_response* server_process_request(config_server* config, http_request *reque
}
if (host_config == NULL) {
//host not found and default not found
response = http_response_create_builtin(500, "Server configuration error (host/default not found)");
response = http_response_create_builtin(500, "Server configuration error. Host not provided and default not found.");
http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
return response;
}
@@ -152,9 +152,19 @@ http_response* server_process_request(config_server* config, http_request *reque
free(buffer);
free(filepath);
bool close_connection = false;
//Check to see if client requested the connection be closed
http_header* request_connection = http_header_list_get(request->headers, HEADER_CONNECTION);
if (request_connection != NULL && strcasecmp(request_connection->content, "close") == 0) {
close_connection = true;
}
//Close a http/1.0 unless the client requested keep-alive
if (close_connection == false && request->req->version == HTTP10 && request_connection != NULL) {
if (strcasecmp(request_connection->content, "Keep-Alive") != 0) {
close_connection = true;
}
}
if (close_connection == true) {
http_header_list_add(response->headers, http_header_new(HEADER_CONNECTION, "close"), false);
}
@@ -177,15 +187,14 @@ server_file_result* server_determine_file(config_host* hconfig, const char* requ
return result;
}
struct stat *pathstat = calloc(1, sizeof(struct stat));
if (stat(filepath_actual, pathstat) < 0) {
struct stat pathstat;
if (stat(filepath_actual, &pathstat) < 0) {
server_file_result_seterror(result, 404, "File not found");
free(filepath_actual);
free(pathstat);
return result;
}
//If directory
if (S_ISDIR(pathstat->st_mode) != 0) {
if (S_ISDIR(pathstat.st_mode) != 0) {
char* dir_index = server_find_directory_index(hconfig, filepath_actual);
if (hconfig->index_files_count > 0 && dir_index != NULL) {
//Use the directory index
@@ -197,10 +206,9 @@ server_file_result* server_determine_file(config_host* hconfig, const char* requ
} else {
server_file_result_seterror(result, 403, "No index file was found and directory indexes are disabled");
}
} else if (S_ISREG(pathstat->st_mode) != 0) {
} else if (S_ISREG(pathstat.st_mode) != 0) {
result->path = strdup(filepath_actual);
}
free(pathstat);
free(filepath_actual);
return result;
}

View File

@@ -69,6 +69,7 @@ http_request_line *http_request_line_new(http_request_method method, const char*
} else {
req->method_other = NULL;
}
req->version = HTTPXX;
return req;
}
void http_request_line_delete(http_request_line *req) {
@@ -252,20 +253,15 @@ http_request *http_request_new() {
ALLOC_CHECK(req);
req->headers = http_header_list_new();
req->parsestatus = PARSE_REQUESTLINE;
req->body = http_body_new(BODY_NONE, NULL);
return req;
}
void http_request_append_body(http_request *req, const char* body) {
assert(req!=NULL);
assert(body!=NULL);
assert(req->body->type == BODY_STRING);
uint32_t bodylen = 0;
if (req->body != NULL) {
bodylen = strlen(req->body);
}
bodylen += strlen(body) + 1;
req->body = realloc(req->body, bodylen * sizeof(char));
ALLOC_CHECK(req->body);
strcat(req->body, body);
http_body_append_str(req->body, body, strlen(body));
}
char* http_request_write(http_request *req) {
assert(req!=NULL);
@@ -289,15 +285,8 @@ char* http_request_write(http_request *req) {
utstring_printf(output, "\r\n");
if (req->body_type == BODY_STRING) {
if (req->body.str != NULL) {
utstring_printf(output, "%s\r\n", req->body);
}
} else if (req->body_type == BODY_FILE) {
if (req->body.file != NULL) {
utstring_printf(output, "%s\r\n", req->body.file->map);
}
}
http_body_writeto_utstring(req->body, output);
char* result = utstring_body(output);
free(output);
return result;
@@ -309,11 +298,7 @@ void http_request_delete(http_request *req) {
http_request_line_delete(req->req);
}
http_header_list_delete(req->headers);
if (req->body_type == BODY_STRING) {
free(req->body.str);
} else if (req->body_type == BODY_FILE && req->body.file != NULL) {
file_map_delete(req->body.file);
}
http_body_delete(req->body);
free(req);
}
@@ -321,42 +306,26 @@ http_response* http_response_new(http_response_line *resp) {
assert(resp!=NULL);
http_response *response = calloc(1, sizeof(http_response));
ALLOC_CHECK(response);
ALLOC_CHECK(resp);
response->resp = resp;
response->headers = http_header_list_new();
response->body_chunked = false;
response->body_type = BODY_NONE;
response->body = http_body_new(BODY_NONE, NULL);
return response;
}
void http_response_append_body(http_response *resp, const char* body) {
assert(resp!=NULL);
assert(body!=NULL);
assert(resp->body_type == BODY_STRING);
assert(resp->body->type == BODY_STRING);
size_t bodylen = 0;
if (resp->body.str != NULL) {
bodylen = strlen(resp->body.str);
}
bodylen += strlen(body) + 1;
if (resp->body.str == NULL) {
resp->body.str = calloc(bodylen, sizeof(char));
ALLOC_CHECK(resp->body.str);
} else {
resp->body.str = realloc(resp->body.str, bodylen * sizeof(char));
ALLOC_CHECK(resp->body.str);
}
strcat(resp->body.str, body);
http_body_append_str(resp->body, body, strlen(body));
}
void http_response_delete(http_response *resp) {
assert(resp!=NULL);
http_response_line_delete(resp->resp);
http_header_list_delete(resp->headers);
if (resp->body_type == BODY_STRING) {
free(resp->body.str);
} else if (resp->body_type == BODY_FILE && resp->body.file != NULL) {
file_map_delete(resp->body.file);
}
http_body_delete(resp->body);
free(resp);
}
char* http_response_write(http_response *resp) {
@@ -378,12 +347,7 @@ char* http_response_write(http_response *resp) {
if (resp->resp->code != 100) { //No additional headers for Continue messages
if (resp->body_chunked == false) {
//Add content length header
uint32_t messageLength = 0;
if (resp->body_type == BODY_STRING) {
messageLength = strlen(resp->body.str);
} else if (resp->body_type == BODY_FILE) {
messageLength = resp->body.file->size;
}
uint32_t messageLength = http_body_len(resp->body);
char messageLengthStr[100];
snprintf(messageLengthStr, 99, "%u", messageLength);
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_LENGTH, messageLengthStr), true);
@@ -416,21 +380,14 @@ char* http_response_write(http_response *resp) {
utstring_printf(output, "\r\n");
//Write the request (if string)
if (resp->body_type == BODY_STRING) {
if (resp->body_chunked == false && resp->body.str != NULL) {
utstring_printf(output, "%s\r\n", resp->body.str);
}
if (resp->body_chunked == true && resp->body.str != NULL) {
http_chunks_write(resp->body.str, output);
}
} else if (resp->body_type == BODY_FILE) {
if (resp->body->type == BODY_STRING) {
if (resp->body_chunked == false) {
file_map_copyto_utstring(resp->body.file, output);
} else {
http_chunks_write(resp->body.file->map, output);
http_body_writeto_utstring(resp->body, output);
}
if (resp->body_chunked == true) {
http_chunks_write(resp->body->data.str, output);
}
}
char* outputStr = utstring_body(output);
free(output);
return outputStr;
@@ -438,7 +395,7 @@ char* http_response_write(http_response *resp) {
http_response* http_response_create_builtin(uint16_t code, const char* errmsg) {
http_response *resp = http_response_new(http_response_line_new(code));
resp->body_type = BODY_STRING;
http_body_set_type(resp->body, BODY_STRING);
http_header_list_add(resp->headers, http_header_new(HEADER_CONTENT_TYPE, "text/html"), false);
@@ -456,9 +413,17 @@ http_response* http_response_create_builtin(uint16_t code, const char* errmsg) {
char* title_message = http_response_line_get_message(resp->resp);
snprintf(buffer, 1023, "%s %hu - %s", (code >= 400) ? "Error" : "Response Code", code, title_message);
resp->body.str = str_replace(resp->body.str, "{{title}}", buffer);
resp->body.str = str_replace(resp->body.str, "{{body_title}}", buffer);
resp->body.str = str_replace(resp->body.str, "{{message}}", errmsg);
char* str = resp->body->data.str;
str = str_replace(str, "{{title}}", buffer);
str = str_replace(str, "{{body_title}}", buffer);
if (errmsg == NULL) {
errmsg = "";
}
str = str_replace(str, "{{message}}", errmsg);
resp->body->data.str = str;
return resp;
}

View File

@@ -45,7 +45,7 @@ extern "C" {
} http_request_method;
typedef enum http_version {
HTTP10, HTTP11
HTTP10, HTTP11, HTTPXX
} http_version;
typedef struct http_request_line {
@@ -83,18 +83,15 @@ extern "C" {
http_request_line *req;
http_request_parsestatus parsestatus;
http_header_list *headers;
http_body_type body_type;
http_body body;
http_body *body;
struct http_request *next;
} http_request;
typedef struct http_response {
http_response_line *resp;
http_header_list *headers;
bool body_chunked;
http_body_type body_type;
http_body body;
http_body *body;
struct http_response *next;
} http_response;

View File

@@ -25,6 +25,8 @@ extern "C" {
#define CONN_LOCK(c) pthread_mutex_lock(&c->mutex)
#define CONN_UNLOCK(c) pthread_mutex_unlock(&c->mutex)
#define CONN_ENQUEUE(conn, pool, name) queue_add(conn->server->pools[pool]->queue, queue_item_new2(name, (void*)conn))
typedef enum server_parse_header_state {
HSTATE_NONE, HSTATE_VALUE, HSTATE_FIELD
} server_parse_header_state;

View File

@@ -69,14 +69,18 @@ void* server_loop_read(void* arg) {
//Request has been read successfully, notify worker queue
LL_APPEND(conn->pending_requests, conn->parse_state->current_request);
server_parser_status_reset(conn->parse_state);
queue_add(conn->server->pools[POOL_WORKER]->queue, queue_item_new2("REQ", (void*)conn));
} else if (conn->parse_state->current_request != NULL && conn->parse_state->current_request->req!=NULL) {
CONN_ENQUEUE(conn, POOL_WORKER, "REQ");
} else if (conn->parse_state->current_request != NULL &&
conn->parse_state->current_request->req!=NULL &&
conn->parse_state->current_request->req->version == HTTP11) {
//Send 100 Continue message
http_response *resp = http_response_new(http_response_line_new(100));
http_response_list_append(conn->pending_responses, resp);
CONN_ENQUEUE(conn, POOL_WRITE, "RESP");
}
if (error = true) {
//Write any error directly, this will also close the connection
queue_add(conn->server->pools[POOL_WRITE]->queue, queue_item_new2("RESP", (void*)conn));
CONN_ENQUEUE(conn, POOL_WRITE, "RESP");
break;
}
}

View File

@@ -2,6 +2,8 @@
#include <stdio.h>
#include <stdbool.h>
#include "ut/utlist.h"
#include "util.h"
#include "log.h"
#include "config.h"
@@ -9,7 +11,35 @@
#include "server-connection.h"
#include "server-state.h"
#include "server-loop.h"
#include "http.h"
#include "http-server.h"
void* server_loop_worker(void* arg) {
thread *th = (thread*)arg;
while(th->stop == false) {
queue_item *item = queue_fetchone(th->pool->queue, true);
if (item == NULL) {
continue;
}
server_connection *conn = (server_connection*)item->data;
CONN_LOCK(conn);
while (conn->pending_requests != NULL) {
http_request *request = conn->pending_requests;
http_response *resp = server_process_request(conn->server->config, request);
LL_DELETE(conn->pending_requests, request);
http_request_delete(request);
if (resp == NULL) {
LOG(LERROR, "Request did not generate a response");
resp = http_response_create_builtin(500, "Request did not complete successfully");
http_header_list_add(resp->headers, http_header_new(HEADER_CONNECTION, "Close"), false);
}
http_response_list_append(conn->pending_responses, resp);
CONN_ENQUEUE(conn, POOL_WRITE, "RESP");
}
CONN_UNLOCK(conn);
queue_return_item(th->pool->queue, item, true);
}
}

View File

@@ -14,6 +14,8 @@
#include "server-connection.h"
#include "server-state.h"
#include "server-loop.h"
#include "http.h"
#include "http-body.h"
void* server_loop_write(void* arg) {
thread *th = (thread*)arg;
@@ -26,35 +28,7 @@ void* server_loop_write(void* arg) {
server_connection *conn = (server_connection*)item->data;
CONN_LOCK(conn);
size_t count = 0;
while(conn->pending_writes->first != NULL) {
BUFFER_LIST_RD_LOCK(conn->pending_writes);
data_buffer *next = conn->pending_writes->first;
count = write(conn->skt->fd, next->buffer+next->rOffset, next->wOffset - next->rOffset);
if (count < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
item->blocked = true;
break;
} else {
if (errno != EBADF) {
char address[INET_ADDRSTRLEN];
skt_clientaddr(conn->skt, address, INET_ADDRSTRLEN);
warning(true, "[#%lu %s] write error", conn->id, address);
}
conn->skt->error = true;
break;
}
}
next->rOffset += count;
if (next->rOffset >= next->wOffset) {
LL_DELETE(conn->pending_writes->first, next);
data_buffer_free(next);
}
BUFFER_LIST_RD_UNLOCK(conn->pending_writes);
}
CONN_UNLOCK(conn);
queue_return_item(th->pool->queue, item, item->blocked == false);

View File

@@ -11,7 +11,6 @@
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <bits/stdio2.h>
#include "ut/utstring.h"
@@ -220,7 +219,7 @@ char* file_map_copyto_string(file_map* map, char* str, size_t str_len) {
}
void file_map_copyto_utstring(file_map* map, UT_string* string) {
assert(map!=NULL);
assert(str!=NULL);
assert(string!=NULL);
utstring_bincpy(string, map->map, map->size);
}