summaryrefslogtreecommitdiffstats
path: root/nanohttp
diff options
context:
space:
mode:
Diffstat (limited to 'nanohttp')
-rwxr-xr-xnanohttp/nanohttp-stream.c347
-rwxr-xr-xnanohttp/nanohttp-stream.h220
2 files changed, 514 insertions, 53 deletions
diff --git a/nanohttp/nanohttp-stream.c b/nanohttp/nanohttp-stream.c
index 8eb31d6..77aee28 100755
--- a/nanohttp/nanohttp-stream.c
+++ b/nanohttp/nanohttp-stream.c
@@ -1,5 +1,5 @@
/******************************************************************
-* $Id: nanohttp-stream.c,v 1.1 2004/09/19 07:05:03 snowdrop Exp $
+* $Id: nanohttp-stream.c,v 1.2 2004/10/15 14:26:15 snowdrop Exp $
*
* CSOAP Project: A http client/server library in C
* Copyright (C) 2003-2004 Ferhat Ayaz
@@ -22,13 +22,29 @@
* 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>
+#ifdef MEM_DEBUG
+#include <utils/alloc.h>
+#endif
+void _log_str(char *fn, char *str, int size)
+{
+/* FILE *f = fopen(fn, "ab");
+ if (!f) f=fopen(fn,"wb");
+ fwrite(str, size, 1, f);
+ fflush(f);
+ fclose(f);
+*/
+}
+
+/*
+-------------------------------------------------------------------
+
+HTTP INPUT STREAM
+
+-------------------------------------------------------------------
+*/
+
static
int _http_stream_is_content_length(hpair_t *header)
{
@@ -62,15 +78,16 @@ http_input_stream_t *http_input_stream_new(hsocket_t sock, hpair_t *header)
char *chunked;
/* Paranoya check */
- if (header == NULL)
+ /*if (header == NULL)
return NULL;
-
+ */
/* Create object */
result = (http_input_stream_t*)malloc(sizeof(http_input_stream_t));
result->sock = sock;
+ result->err = H_OK;
/* Find connection type */
-
+ hpairnode_dump_deep(header);
/* Check if Content-type */
if (_http_stream_is_content_length(header))
{
@@ -84,7 +101,7 @@ http_input_stream_t *http_input_stream_new(hsocket_t sock, hpair_t *header)
else if (_http_stream_is_chunked(header))
{
log_verbose1("Stream transfer with 'chunked'");
- result->type = HTTP_TRANSFER_CONTENT_CHUNKED;
+ result->type = HTTP_TRANSFER_CHUNKED;
result->chunk_size = -1;
result->received = -1;
}
@@ -93,17 +110,41 @@ http_input_stream_t *http_input_stream_new(hsocket_t sock, hpair_t *header)
{
log_verbose1("Stream transfer with 'Connection: close'");
result->type = HTTP_TRANSFER_CONNECTION_CLOSE;
+ result->connection_closed = 0;
+ result->received = 0;
}
-
return result;
}
+/**
+ Creates a new input stream from file.
+ This function was added for MIME messages
+ and for debugging.
+*/
+http_input_stream_t *http_input_stream_new_from_file(const char* filename)
+{
+ http_input_stream_t *result;
+ FILE *fd = fopen(filename, "rb");
+
+ if (fd == NULL)
+ return NULL;
+
+ /* Create object */
+ result = (http_input_stream_t*)malloc(sizeof(http_input_stream_t));
+ result->type = HTTP_TRANSFER_FILE;
+ result->fd = fd;
+
+ return result;
+}
/**
Free input stream
*/
void http_input_stream_free(http_input_stream_t *stream)
{
+ if (stream->type == HTTP_TRANSFER_FILE && stream->fd)
+ fclose(stream->fd);
+
free(stream);
}
@@ -126,12 +167,19 @@ static
int _http_input_stream_is_connection_closed_ready(
http_input_stream_t *stream)
{
-/* TODO (#1#): implement */
+ return !stream->connection_closed;
+}
+
+static
+int _http_input_stream_is_file_ready(
+ http_input_stream_t *stream)
+{
+ return !feof(stream->fd);
}
static
int _http_input_stream_content_length_read(
- http_input_stream_t *stream, byte_t *dest, size_t size)
+ http_input_stream_t *stream, byte_t *dest, int size)
{
int status;
@@ -141,8 +189,10 @@ int _http_input_stream_content_length_read(
/* read from socket */
status = hsocket_read(stream->sock, dest, size, 1);
- if (status == -1)
- return STREAM_SOCKET_ERROR;
+ if (status == -1) {
+ stream->err = HSOCKET_ERROR_RECEIVE;
+ return -1;
+ }
stream->received += status;
return status;
@@ -153,14 +203,17 @@ int _http_input_stream_chunked_read_chunk_size(
http_input_stream_t *stream)
{
char chunk[25];
- int chunk_size, status, i = 0;
+ int status, i = 0;
+ int chunk_size;
while (1)
{
status = hsocket_read(stream->sock, &(chunk[i]), 1, 1);
- if (status == -1)
- return STREAM_SOCKET_ERROR;
+ if (status == -1) {
+ stream->err = HSOCKET_ERROR_RECEIVE;
+ return -1;
+ }
if (chunk[i] == '\r' || chunk[i] == ';')
{
@@ -170,36 +223,66 @@ int _http_input_stream_chunked_read_chunk_size(
{
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);
+/* log_verbose3("chunk_size: '%s' as dec: '%d'", chunk, chunk_size);*/
return chunk_size;
}
- if (i == 24)
- return STREAM_NO_CHUNK_SIZE;
+ if (i == 24) {
+ stream->err = STREAM_ERROR_NO_CHUNK_SIZE;
+ return -1;
+ }
else
i++;
}
/* this should never happens */
- return STREAM_NO_CHUNK_SIZE;
+ stream->err = STREAM_ERROR_NO_CHUNK_SIZE;
+ return -1;
}
static
int _http_input_stream_chunked_read(
- http_input_stream_t *stream, byte_t *dest, size_t size)
+ http_input_stream_t *stream, byte_t *dest, int size)
{
- int status;
+ int status, counter;
int remain, read=0;
+ char ch;
while (size > 0)
{
remain = stream->chunk_size - stream->received ;
+ if (remain == 0 && stream->chunk_size != -1)
+ {
+ /* This is not the first chunk.
+ so skip new line until chunk size string */
+ counter = 100; /* maximum for stop infinity */
+ while (1)
+ {
+ status = hsocket_read(stream->sock, &ch, 1, 1);
+
+ if (status == -1) {
+ stream->err = HSOCKET_ERROR_RECEIVE;
+ return -1;
+ }
+
+ if (ch == '\n')
+ {
+ break;
+ }
+ if (counter-- == 0) {
+ stream->err = STREAM_ERROR_WRONG_CHUNK_SIZE;
+ return -1;
+ }
+ }
+ }
+
if (remain == 0)
{
/* receive new chunk size */
stream->chunk_size =
_http_input_stream_chunked_read_chunk_size(stream);
+ stream->received = 0;
if (stream->chunk_size < 0)
{
@@ -218,30 +301,65 @@ int _http_input_stream_chunked_read(
{
/* read from socket */
status = hsocket_read(stream->sock, &(dest[read]), remain, 1);
- if (status == -1)
- return STREAM_SOCKET_ERROR;
+ if (status == -1) {
+ stream->err = HSOCKET_ERROR_RECEIVE;
+ return -1;
+ }
}
else
{
/* read from socket */
status = hsocket_read(stream->sock, &(dest[read]), size, 1);
- if (status == -1)
- return STREAM_SOCKET_ERROR;
+ if (status == -1) {
+ stream->err = HSOCKET_ERROR_RECEIVE;
+ return -1;
+ }
}
read += status;
size -= status;
stream->received += status;
}
+
+ return read;
}
+
static
int _http_input_stream_connection_closed_read(
- http_input_stream_t *stream, byte_t *dest, size_t size)
+ http_input_stream_t *stream, byte_t *dest, int size)
{
-/* TODO (#1#): implement */
- return 0;
+ int status;
+
+ /* read from socket */
+ status = hsocket_read(stream->sock, dest, size, 0);
+ if (status == -1) {
+ stream->err = HSOCKET_ERROR_RECEIVE;
+ return -1;
+ }
+
+ if (status == 0)
+ stream->connection_closed = 1;
+
+ stream->received += status;
+ _log_str("stream.in", dest, size);
+ return status;
+}
+
+static
+int _http_input_stream_file_read(
+ http_input_stream_t *stream, byte_t *dest, int size)
+{
+ int readed;
+
+ readed = fread(dest, 1, size, stream->fd);
+ if (readed == -1) {
+ stream->err = HSOCKET_ERROR_RECEIVE;
+ return -1;
+ }
+
+ return readed;
}
/**
@@ -253,17 +371,23 @@ int http_input_stream_is_ready(http_input_stream_t *stream)
if (stream == NULL)
return 0;
+ /* reset error flag */
+ stream->err = H_OK;
+
switch (stream->type)
{
case HTTP_TRANSFER_CONTENT_LENGTH:
return _http_input_stream_is_content_length_ready(stream);
- case HTTP_TRANSFER_CONTENT_CHUNKED:
+ case HTTP_TRANSFER_CHUNKED:
return _http_input_stream_is_chunked_ready(stream);
case HTTP_TRANSFER_CONNECTION_CLOSE:
return _http_input_stream_is_connection_closed_ready(stream);
+ case HTTP_TRANSFER_FILE:
+ return _http_input_stream_is_file_ready(stream);
default:
return 0;
}
+
}
/**
@@ -271,22 +395,165 @@ int http_input_stream_is_ready(http_input_stream_t *stream)
<0 on error
*/
int http_input_stream_read(http_input_stream_t *stream,
- byte_t *dest, size_t size)
+ byte_t *dest, int size)
{
+ int readed=0;
/* paranoya check */
- if (stream == NULL)
- return STREAM_INVALID;
+ if (stream == NULL) {
+ return -1;
+ }
+
+ /* reset error flag */
+ stream->err = H_OK;
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);
+ readed=_http_input_stream_content_length_read(stream, dest, size);
+ break;
+ case HTTP_TRANSFER_CHUNKED:
+ readed=_http_input_stream_chunked_read(stream, dest, size);
+ break;
case HTTP_TRANSFER_CONNECTION_CLOSE:
- return _http_input_stream_connection_closed_read(stream, dest, size);
+ readed=_http_input_stream_connection_closed_read(stream, dest, size);
+ break;
+ case HTTP_TRANSFER_FILE:
+ readed=_http_input_stream_file_read(stream, dest, size);
+ break;
default:
- return STREAM_INVALID_TYPE;
+ stream->err = STREAM_ERROR_INVALID_TYPE;
+ return -1;
+ }
+
+ return readed;
+}
+
+
+/*
+-------------------------------------------------------------------
+
+HTTP OUTPUT STREAM
+
+-------------------------------------------------------------------
+*/
+
+
+
+/**
+ Creates a new output stream. Transfer code will be found from header.
+*/
+http_output_stream_t *http_output_stream_new(hsocket_t sock, hpair_t *header)
+{
+ http_output_stream_t *result;
+ char *content_length;
+ char *chunked;
+
+ /* Paranoya check */
+/* if (header == NULL)
+ return NULL;
+*/
+ /* Create object */
+ result = (http_output_stream_t*)malloc(sizeof(http_output_stream_t));
+ result->sock = sock;
+ result->sent = 0;
+
+ /* 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->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_CHUNKED;
+ }
+ /* Assume connection close */
+ else
+ {
+ log_verbose1("Stream transfer with 'Connection: close'");
+ result->type = HTTP_TRANSFER_CONNECTION_CLOSE;
+ }
+
+ return result;
+}
+
+/**
+ Free output stream
+*/
+void http_output_stream_free(http_output_stream_t *stream)
+{
+ free(stream);
+}
+
+/**
+ Writes 'size' bytes of 'bytes' into stream.
+ Returns socket error flags or H_OK.
+*/
+hstatus_t http_output_stream_write(http_output_stream_t *stream,
+ const byte_t *bytes, int size)
+{
+ int status;
+ char chunked[15];
+
+ if (stream->type == HTTP_TRANSFER_CHUNKED)
+ {
+ sprintf(chunked, "%x\r\n", size);
+ status = hsocket_send(stream->sock, chunked);
+ if (status != H_OK)
+ return status;
}
+
+ if (size > 0)
+ {
+ _log_str("stream.out", (char*)bytes, size);
+ status = hsocket_nsend(stream->sock, bytes, size);
+ if (status != H_OK)
+ return status;
+ }
+
+ if (stream->type == HTTP_TRANSFER_CHUNKED)
+ {
+ status = hsocket_send(stream->sock, "\r\n");
+ if (status != H_OK)
+ return status;
+ }
+
+ return H_OK;
}
+/**
+ Writes 'strlen()' bytes of 'str' into stream.
+ Returns socket error flags or H_OK.
+*/
+int http_output_stream_write_string(http_output_stream_t *stream,
+ const char *str)
+{
+ return http_output_stream_write(stream, str, strlen(str));
+}
+
+
+int http_output_stream_flush(http_output_stream_t *stream)
+{
+ int status;
+
+ if (stream->type == HTTP_TRANSFER_CHUNKED)
+ {
+ status = hsocket_send(stream->sock, "0\r\n\r\n");
+ if (status != H_OK)
+ return status;
+ }
+
+ return H_OK;
+}
+
+
+
+
+
+
diff --git a/nanohttp/nanohttp-stream.h b/nanohttp/nanohttp-stream.h
index edbf371..fb44284 100755
--- a/nanohttp/nanohttp-stream.h
+++ b/nanohttp/nanohttp-stream.h
@@ -1,5 +1,5 @@
/******************************************************************
- * $Id: nanohttp-stream.h,v 1.1 2004/09/19 07:05:03 snowdrop Exp $
+ * $Id: nanohttp-stream.h,v 1.2 2004/10/15 14:26:15 snowdrop Exp $
*
* CSOAP Project: A http client/server library in C
* Copyright (C) 2003-2004 Ferhat Ayaz
@@ -23,53 +23,247 @@
******************************************************************/
#ifndef NANO_HTTP_STREAM_H
#define NANO_HTTP_STREAM_H
+#include <stdio.h>
+
+void _log_str(char *fn, char *str, int size);
+
+/*
+ HTTP Stream modul:
+
+ nanohttp supports 2 different streams:
+
+ 1. http_input_stream_t
+ 2. http_output_stream_t
+
+ These are not regular streams. They will care about
+ transfer styles while sending/receiving data.
+
+ Supported transfer styles are
+
+ o Content-length
+ o Chunked encoding
+ o Connection: "close"
+
+ A stream will set its transfer style from the header
+ information, which must be given while creating a stream.
+
+ A stream will start sending/receiving data "after"
+ sending/receiving header information. (After <CF><CF>)"
+
+*/
#include <nanohttp/nanohttp-socket.h>
#include <nanohttp/nanohttp-common.h>
+#include <stdio.h>
+
+
/**
- Supported transfer types
+ Transfer types supported while
+ sending/receiving data.
*/
typedef enum http_transfer_type
{
+ /** The stream cares about Content-length */
HTTP_TRANSFER_CONTENT_LENGTH,
- HTTP_TRANSFER_CONTENT_CHUNKED,
- HTTP_TRANSFER_CONNECTION_CLOSE
+
+ /** The stream sends/receives chunked data */
+ HTTP_TRANSFER_CHUNKED,
+
+ /** The stream sends/receives data until
+ connection is closed */
+ HTTP_TRANSFER_CONNECTION_CLOSE,
+
+ /** This transfer style will be used by MIME support
+ and for debug purposes.*/
+ HTTP_TRANSFER_FILE
+
}http_transfer_type_t;
+
/**
- HTTP INPUT STREAM
+ HTTP INPUT STREAM. Receives data from a socket/file
+ and cares about the transfer style.
*/
typedef struct http_input_stream
{
- hsocket_t sock;
+ hsocket_t sock;
+ hstatus_t err;
http_transfer_type_t type;
- size_t received;
- size_t content_length;
- size_t chunk_size;
+ int received;
+ int content_length;
+ int chunk_size;
+ byte_t connection_closed;
+ FILE *fd;
}http_input_stream_t;
/**
- Creates a new input stream.
+ HTTP OUTPUT STREAM. Sends data to a socket
+ and cares about the transfer style.
+*/
+typedef struct http_output_stream
+{
+ hsocket_t sock;
+ http_transfer_type_t type;
+ int content_length;
+ int sent;
+}http_output_stream_t;
+
+
+/*
+--------------------------------------------------------------
+ HTTP INPUT STREAM
+--------------------------------------------------------------
+*/
+
+/**
+ Creates a new input stream. The transfer style will be
+ choosen from the given header.
+
+ @param sock the socket to receive data from
+ @param header the http header. This must be received before
+ creating a http_input_stream_t.
+
+ @returns a newly created http_input_stream_t object. If no
+ transfer style was found in the header,
+ HTTP_TRANSFER_CONNECTION_CLOSE will be used as default.
+
+ @see http_input_stream_free
*/
http_input_stream_t *http_input_stream_new(hsocket_t sock, hpair_t *header);
+
/**
- Free input stream
+ Creates a new input stream from file.
+ This function was added for MIME messages
+ and for debugging. The transfer style is always
+ HTTP_TRANSFER_FILE.
+
+ @param filename the name of the file to open and read.
+
+ @returns The return value is a http_input_stream_t object
+ if the file exists and could be opened. NULL otherwise.
+
+ @see http_input_stream_free
+*/
+http_input_stream_t *http_input_stream_new_from_file(const char* filename);
+
+
+/**
+ Free input stream. Note that the socket will not be closed
+ by this functions.
+
+ @param stream the input stream to free.
*/
void http_input_stream_free(http_input_stream_t *stream);
+
/**
Returns the actual status of the stream.
+
+ @param stream the stream to check its status
+ @returns <br>1 if there are still data to read.
+ <br>0 if no more data exists.
*/
int http_input_stream_is_ready(http_input_stream_t *stream);
+
/**
- Returns the actual read bytes
+ Tries to read 'size' bytes from the stream. Check always
+ with http_input_stream_is_ready() if there are some data
+ to read. If it returns 0, no more data is available on
+ stream.
+ <P>
+ On error this function will return -1. In this case the
+ "err" field of stream will be set to the actual error.
+ This can be one of the followings: <P>
+
+ <BR>STREAM_ERROR_NO_CHUNK_SIZE
+ <BR>STREAM_ERROR_WRONG_CHUNK_SIZE
+ <BR>STREAM_ERROR_INVALID_TYPE
+ <BR>HSOCKET_ERROR_RECEIVE
+
+ @param stream the stream to read data from
+ @param dest destination memory to store readed bytes
+ @param size maximum size of 'dest' (size to read)
+
+ @returns the actual readed bytes or -1 on error.
*/
int http_input_stream_read(http_input_stream_t *stream,
- byte_t *dest, size_t size);
+ byte_t *dest, int size);
+
+
+/*
+--------------------------------------------------------------
+ HTTP OUTPUT STREAM
+--------------------------------------------------------------
+*/
+
+/**
+ Creates a new output stream. Transfer style will be found
+ from the given header.
+
+ @param sock the socket to to send data to
+ @param header the header which must be sent before
+
+ @returns a http_output_stream_t object. If no proper transfer
+ style was found in the header, HTTP_TRANSFER_CONNECTION_CLOSE
+ will be used as default.
+
+ @see http_output_stream_free
+*/
+http_output_stream_t *http_output_stream_new(hsocket_t sock, hpair_t *header);
+
+
+/**
+ Free output stream. Note that this functions will not
+ close any socket connections.
+
+ @param stream the stream to free.
+*/
+void http_output_stream_free(http_output_stream_t *stream);
+
+
+/**
+ Writes 'size' bytes of 'bytes' into stream.
+
+ @param stream the stream to use to send data
+ @param bytes bytes to send
+ @param size size of bytes to send
+
+ @returns H_OK if success. One of the followings otherwise
+ <BR>HSOCKET_ERROR_NOT_INITIALIZED
+ <BR>HSOCKET_ERROR_SEND
+*/
+hstatus_t http_output_stream_write(http_output_stream_t *stream,
+ const byte_t *bytes, int size);
+
+/**
+ Writes a null terminated string to the stream.
+
+ @param stream the stream to use to send data
+ @param str a null terminated string to send
+
+ @returns H_OK if success. One of the followings otherwise
+ <BR>HSOCKET_ERROR_NOT_INITIALIZED
+ <BR>HSOCKET_ERROR_SEND
+*/
+hstatus_t http_output_stream_write_string(http_output_stream_t *stream,
+ const char *str);
+
+
+/**
+ Sends finish flags if nesseccary (like in chunked transport).
+ Call always this function before closing the connections.
+
+ @param stream the stream to send post data.
+
+ @returns H_OK if success. One of the followings otherwise
+ <BR>HSOCKET_ERROR_NOT_INITIALIZED
+ <BR>HSOCKET_ERROR_SEND
+*/
+hstatus_t http_output_stream_flush(http_output_stream_t *stream);
#endif