diff options
Diffstat (limited to 'nanohttp/nanohttp-stream.c')
-rwxr-xr-x | nanohttp/nanohttp-stream.c | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/nanohttp/nanohttp-stream.c b/nanohttp/nanohttp-stream.c new file mode 100755 index 0000000..8eb31d6 --- /dev/null +++ b/nanohttp/nanohttp-stream.c @@ -0,0 +1,292 @@ +/****************************************************************** +* $Id: nanohttp-stream.c,v 1.1 2004/09/19 07:05:03 snowdrop Exp $ +* +* CSOAP Project: A http client/server library in C +* Copyright (C) 2003-2004 Ferhat Ayaz +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library 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 +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* +* Email: ferhatayaz@yahoo.com +******************************************************************/ + +#define STREAM_INVALID -1 +#define STREAM_INVALID_TYPE -2 +#define STREAM_SOCKET_ERROR -3 +#define STREAM_NO_CHUNK_SIZE -4 + +#include <nanohttp/nanohttp-stream.h> + +static +int _http_stream_is_content_length(hpair_t *header) +{ + return + hpairnode_get_ignore_case(header, HEADER_CONTENT_LENGTH) != NULL; +} + +static +int _http_stream_is_chunked(hpair_t *header) +{ + char *chunked; + chunked = hpairnode_get_ignore_case(header, HEADER_TRANSFER_ENCODING); + if (chunked != NULL) + { + if (!strcmp(chunked, TRANSFER_ENCODING_CHUNKED)) + { + return 1; + } + } + + return 0; +} + +/** + Creates a new input stream. +*/ +http_input_stream_t *http_input_stream_new(hsocket_t sock, hpair_t *header) +{ + http_input_stream_t *result; + char *content_length; + char *chunked; + + /* Paranoya check */ + if (header == NULL) + return NULL; + + /* Create object */ + result = (http_input_stream_t*)malloc(sizeof(http_input_stream_t)); + result->sock = sock; + + /* Find connection type */ + + /* Check if Content-type */ + if (_http_stream_is_content_length(header)) + { + log_verbose1("Stream transfer with 'Content-length'"); + content_length = hpairnode_get_ignore_case(header, HEADER_CONTENT_LENGTH); + result->content_length = atoi(content_length); + result->received = 0; + result->type = HTTP_TRANSFER_CONTENT_LENGTH; + } + /* Check if Chunked */ + else if (_http_stream_is_chunked(header)) + { + log_verbose1("Stream transfer with 'chunked'"); + result->type = HTTP_TRANSFER_CONTENT_CHUNKED; + result->chunk_size = -1; + result->received = -1; + } + /* Assume connection close */ + else + { + log_verbose1("Stream transfer with 'Connection: close'"); + result->type = HTTP_TRANSFER_CONNECTION_CLOSE; + } + + return result; +} + + +/** + Free input stream +*/ +void http_input_stream_free(http_input_stream_t *stream) +{ + free(stream); +} + +static +int _http_input_stream_is_content_length_ready( + http_input_stream_t *stream) +{ + return (stream->content_length > stream->received); +} + +static +int _http_input_stream_is_chunked_ready( + http_input_stream_t *stream) +{ + return stream->chunk_size != 0; + +} + +static +int _http_input_stream_is_connection_closed_ready( + http_input_stream_t *stream) +{ +/* TODO (#1#): implement */ +} + +static +int _http_input_stream_content_length_read( + http_input_stream_t *stream, byte_t *dest, size_t size) +{ + int status; + + /* check limit */ + if (stream->content_length - stream->received < size) + size = stream->content_length - stream->received; + + /* read from socket */ + status = hsocket_read(stream->sock, dest, size, 1); + if (status == -1) + return STREAM_SOCKET_ERROR; + + stream->received += status; + return status; +} + +static +int _http_input_stream_chunked_read_chunk_size( + http_input_stream_t *stream) +{ + char chunk[25]; + int chunk_size, status, i = 0; + + while (1) + { + status = hsocket_read(stream->sock, &(chunk[i]), 1, 1); + + if (status == -1) + return STREAM_SOCKET_ERROR; + + if (chunk[i] == '\r' || chunk[i] == ';') + { + chunk[i] = '\0'; + } + else if (chunk[i] == '\n') + { + chunk[i] = '\0'; /* double check*/ + chunk_size = strtol(chunk, (char **) NULL, 16); /* hex to dec */ + log_debug3("chunk_size: '%s' as dec: '%d'", chunk, chunk_size); + return chunk_size; + } + + if (i == 24) + return STREAM_NO_CHUNK_SIZE; + else + i++; + } + + /* this should never happens */ + return STREAM_NO_CHUNK_SIZE; +} + +static +int _http_input_stream_chunked_read( + http_input_stream_t *stream, byte_t *dest, size_t size) +{ + int status; + int remain, read=0; + + while (size > 0) + { + remain = stream->chunk_size - stream->received ; + + if (remain == 0) + { + /* receive new chunk size */ + stream->chunk_size = + _http_input_stream_chunked_read_chunk_size(stream); + + if (stream->chunk_size < 0) + { + /* TODO (#1#): set error flag */ + return stream->chunk_size; + } + else if (stream->chunk_size == 0) + { + return read; + } + remain = stream->chunk_size; + } + + /* show remaining chunk size in socket */ + if (remain < size) + { + /* read from socket */ + status = hsocket_read(stream->sock, &(dest[read]), remain, 1); + if (status == -1) + return STREAM_SOCKET_ERROR; + + } + else + { + /* read from socket */ + status = hsocket_read(stream->sock, &(dest[read]), size, 1); + if (status == -1) + return STREAM_SOCKET_ERROR; + } + + read += status; + size -= status; + stream->received += status; + } +} + +static +int _http_input_stream_connection_closed_read( + http_input_stream_t *stream, byte_t *dest, size_t size) +{ +/* TODO (#1#): implement */ + return 0; +} + +/** + Returns the actual status of the stream. +*/ +int http_input_stream_is_ready(http_input_stream_t *stream) +{ + /* paranoya check */ + if (stream == NULL) + return 0; + + switch (stream->type) + { + case HTTP_TRANSFER_CONTENT_LENGTH: + return _http_input_stream_is_content_length_ready(stream); + case HTTP_TRANSFER_CONTENT_CHUNKED: + return _http_input_stream_is_chunked_ready(stream); + case HTTP_TRANSFER_CONNECTION_CLOSE: + return _http_input_stream_is_connection_closed_ready(stream); + default: + return 0; + } +} + +/** + Returns the actual read bytes + <0 on error +*/ +int http_input_stream_read(http_input_stream_t *stream, + byte_t *dest, size_t size) +{ + /* paranoya check */ + if (stream == NULL) + return STREAM_INVALID; + + switch (stream->type) + { + case HTTP_TRANSFER_CONTENT_LENGTH: + return _http_input_stream_content_length_read(stream, dest, size); + case HTTP_TRANSFER_CONTENT_CHUNKED: + return _http_input_stream_chunked_read(stream, dest, size); + case HTTP_TRANSFER_CONNECTION_CLOSE: + return _http_input_stream_connection_closed_read(stream, dest, size); + default: + return STREAM_INVALID_TYPE; + } +} + |