nanohttp-stream.c

Go to the documentation of this file.
00001 /******************************************************************
00002 *  $Id: nanohttp-stream.c,v 1.19 2006/12/01 10:56:00 m0gg Exp $
00003 *
00004 * CSOAP Project:  A http client/server library in C
00005 * Copyright (C) 2003-2004  Ferhat Ayaz
00006 *
00007 * This library is free software; you can redistribute it and/or
00008 * modify it under the terms of the GNU Library General Public
00009 * License as published by the Free Software Foundation; either
00010 * version 2 of the License, or (at your option) any later version.
00011 *
00012 * This library is distributed in the hope that it will be useful,
00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 * Library General Public License for more details.
00016 *
00017 * You should have received a copy of the GNU Library General Public
00018 * License along with this library; if not, write to the
00019 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00020 * Boston, MA  02111-1307, USA.
00021 *
00022 * Email: ferhatayaz@yahoo.com
00023 ******************************************************************/
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027 
00028 #ifdef HAVE_STDIO_H
00029 #include <stdio.h>
00030 #endif
00031 
00032 #ifdef HAVE_STDLIB_H
00033 #include <stdlib.h>
00034 #endif
00035 
00036 #ifdef HAVE_STRING_H
00037 #include <string.h>
00038 #endif
00039 
00040 #ifdef HAVE_ERRNO_H
00041 #include <errno.h>
00042 #endif
00043 
00044 #ifdef HAVE_NETINET_IN_H
00045 #include <netinet/in.h>
00046 #endif
00047 
00048 #include "nanohttp-logging.h"
00049 #include "nanohttp-error.h"
00050 #include "nanohttp-common.h"
00051 #include "nanohttp-socket.h"
00052 #include "nanohttp-stream.h"
00053 
00054 /*------------------------------------------------------------
00055 HTTP INPUT STREAM
00056 ------------------------------------------------------------*/
00057 
00058 static int
00059 _http_stream_is_content_length(hpair_t * header)
00060 {
00061   return hpairnode_get_ignore_case(header, HEADER_CONTENT_LENGTH) != NULL;
00062 }
00063 
00064 static int
00065 _http_stream_is_chunked(hpair_t * header)
00066 {
00067   char *chunked;
00068   chunked = hpairnode_get_ignore_case(header, HEADER_TRANSFER_ENCODING);
00069   if (chunked != NULL)
00070   {
00071     if (!strcmp(chunked, TRANSFER_ENCODING_CHUNKED))
00072     {
00073       return 1;
00074     }
00075   }
00076 
00077   return 0;
00078 }
00079 
00083 struct http_input_stream_t *
00084 http_input_stream_new(struct hsocket_t *sock, hpair_t * header)
00085 {
00086   struct http_input_stream_t *result;
00087   char *content_length;
00088 
00089   if (!(result = (struct http_input_stream_t *) malloc(sizeof(struct http_input_stream_t))))
00090   {
00091     log_error2("malloc failed (%s)", strerror(errno));
00092     return NULL;
00093   }
00094 
00095   result->sock = sock;
00096   result->err = H_OK;
00097 
00098   /* Find connection type */
00099   hpairnode_dump_deep(header);
00100   /* Check if Content-type */
00101   if (_http_stream_is_content_length(header))
00102   {
00103     log_verbose1("Stream transfer with 'Content-length'");
00104     content_length = hpairnode_get_ignore_case(header, HEADER_CONTENT_LENGTH);
00105     result->content_length = atoi(content_length);
00106     result->received = 0;
00107     result->type = HTTP_TRANSFER_CONTENT_LENGTH;
00108   }
00109   /* Check if Chunked */
00110   else if (_http_stream_is_chunked(header))
00111   {
00112     log_verbose1("Stream transfer with 'chunked'");
00113     result->type = HTTP_TRANSFER_CHUNKED;
00114     result->chunk_size = -1;
00115     result->received = -1;
00116   }
00117   /* Assume connection close */
00118   else
00119   {
00120     log_verbose1("Stream transfer with 'Connection: close'");
00121     result->type = HTTP_TRANSFER_CONNECTION_CLOSE;
00122     result->connection_closed = 0;
00123     result->received = 0;
00124   }
00125   return result;
00126 }
00127 
00133 struct http_input_stream_t *
00134 http_input_stream_new_from_file(const char *filename)
00135 {
00136   struct http_input_stream_t *result;
00137   FILE *fd;
00138  
00139   if (!(fd = fopen(filename, "rb"))) {
00140 
00141     log_error2("fopen failed (%s)", strerror(errno));
00142     return NULL;
00143   }
00144 
00145   /* Create object */
00146   if (!(result = (struct http_input_stream_t *) malloc(sizeof(struct http_input_stream_t)))) 
00147   {
00148     log_error2("malloc failed (%s)", strerror(errno));
00149     fclose(fd);
00150     return NULL;
00151   }
00152 
00153   result->type = HTTP_TRANSFER_FILE;
00154   result->fd = fd;
00155   result->deleteOnExit = 0;
00156   strcpy(result->filename, filename);
00157 
00158   return result;
00159 }
00160 
00161 void
00162 http_input_stream_free(struct http_input_stream_t * stream)
00163 {
00164   if (stream->type == HTTP_TRANSFER_FILE && stream->fd)
00165   {
00166     fclose(stream->fd);
00167     if (stream->deleteOnExit)
00168       log_info2("Removing '%s'", stream->filename);
00169     /* remove(stream->filename); */
00170   }
00171 
00172   free(stream);
00173 }
00174 
00175 static int
00176 _http_input_stream_is_content_length_ready(struct http_input_stream_t * stream)
00177 {
00178   return (stream->content_length > stream->received);
00179 }
00180 
00181 static int
00182 _http_input_stream_is_chunked_ready(struct http_input_stream_t * stream)
00183 {
00184   return stream->chunk_size != 0;
00185 }
00186 
00187 static int
00188 _http_input_stream_is_connection_closed_ready(struct http_input_stream_t * stream)
00189 {
00190   return !stream->connection_closed;
00191 }
00192 
00193 static int
00194 _http_input_stream_is_file_ready(struct http_input_stream_t * stream)
00195 {
00196   return !feof(stream->fd);
00197 }
00198 
00199 static int
00200 _http_input_stream_content_length_read(struct http_input_stream_t * stream, unsigned char *dest, int size)
00201 {
00202   herror_t status;
00203   int read;
00204 
00205   /* check limit */
00206   if (stream->content_length - stream->received < size)
00207     size = stream->content_length - stream->received;
00208 
00209   /* read from socket */
00210   if ((status = hsocket_recv(stream->sock, dest, size, 1, &read)) != H_OK)
00211   {
00212     stream->err = status;
00213     return -1;
00214   }
00215 
00216   stream->received += read;
00217   return read;
00218 }
00219 
00220 static int
00221 _http_input_stream_chunked_read_chunk_size(struct http_input_stream_t * stream)
00222 {
00223   char chunk[25];
00224   int status, i = 0;
00225   int chunk_size;
00226   herror_t err;
00227 
00228   while (1)
00229   {
00230     err = hsocket_recv(stream->sock, &(chunk[i]), 1, 1, &status);
00231     if (status != 1)
00232     {
00233       stream->err = herror_new("_http_input_stream_chunked_read_chunk_size",
00234                                GENERAL_INVALID_PARAM,
00235                                "This should never happen!");
00236       return -1;
00237     }
00238 
00239     if (err != H_OK)
00240     {
00241       log_error4("[%d] %s(): %s ", herror_code(err), herror_func(err),
00242                  herror_message(err));
00243 
00244       stream->err = err;
00245       return -1;
00246     }
00247 
00248     if (chunk[i] == '\r' || chunk[i] == ';')
00249     {
00250       chunk[i] = '\0';
00251     }
00252     else if (chunk[i] == '\n')
00253     {
00254       chunk[i] = '\0';          /* double check */
00255       chunk_size = strtol(chunk, (char **) NULL, 16);   /* hex to dec */
00256       /* 
00257          log_verbose3("chunk_size: '%s' as dec: '%d'", chunk, chunk_size); */
00258       return chunk_size;
00259     }
00260 
00261     if (i == 24)
00262     {
00263       stream->err =
00264         herror_new("_http_input_stream_chunked_read_chunk_size",
00265                    STREAM_ERROR_NO_CHUNK_SIZE, "reached max line == %d", i);
00266       return -1;
00267     }
00268     else
00269       i++;
00270   }
00271 
00272   /* this should never happen */
00273   stream->err =
00274     herror_new("_http_input_stream_chunked_read_chunk_size",
00275                STREAM_ERROR_NO_CHUNK_SIZE, "reached max line == %d", i);
00276   return -1;
00277 }
00278 
00279 static int
00280 _http_input_stream_chunked_read(struct http_input_stream_t * stream, unsigned char *dest,
00281                                 int size)
00282 {
00283   int status, counter;
00284   int remain, read = 0;
00285   char ch;
00286   herror_t err;
00287 
00288   while (size > 0)
00289   {
00290     remain = stream->chunk_size - stream->received;
00291 
00292     if (remain == 0 && stream->chunk_size != -1)
00293     {
00294       /* This is not the first chunk. so skip new line until chunk size
00295          string */
00296       counter = 100;            /* maximum for stop infinity */
00297       while (1)
00298       {
00299         if ((err = hsocket_recv(stream->sock, &ch, 1, 1, &status)) != H_OK)
00300         {
00301           stream->err = err;
00302           return -1;
00303         }
00304 
00305         if (ch == '\n')
00306         {
00307           break;
00308         }
00309         if (counter-- == 0)
00310         {
00311           stream->err = herror_new("_http_input_stream_chunked_read",
00312                                    STREAM_ERROR_WRONG_CHUNK_SIZE,
00313                                    "Wrong chunk-size");
00314           return -1;
00315         }
00316       }
00317     }
00318 
00319     if (remain == 0)
00320     {
00321       /* receive new chunk size */
00322       stream->chunk_size = _http_input_stream_chunked_read_chunk_size(stream);
00323       stream->received = 0;
00324 
00325       if (stream->chunk_size < 0)
00326       {
00327         /* TODO (#1#): set error flag */
00328         return stream->chunk_size;
00329       }
00330       else if (stream->chunk_size == 0)
00331       {
00332         return read;
00333       }
00334       remain = stream->chunk_size;
00335     }
00336 
00337     /* show remaining chunk size in socket */
00338     if (remain < size)
00339     {
00340       /* read from socket */
00341       if ((err = hsocket_recv(stream->sock, &(dest[read]), remain, 1, &status)) != H_OK)
00342       {
00343         stream->err = err;
00344         return -1;
00345       }
00346       if (status != remain)
00347       {
00348         stream->err = herror_new("_http_input_stream_chunked_read",
00349                                  GENERAL_INVALID_PARAM,
00350                                  "This should never happen (remain=%d)(status=%d)!",
00351                                  remain, status);
00352         return -1;
00353       }
00354     }
00355     else
00356     {
00357       /* read from socket */
00358       err = hsocket_recv(stream->sock, &(dest[read]), size, 1, &status);
00359       if (status != size)
00360       {
00361         stream->err = herror_new("_http_input_stream_chunked_read",
00362                                  GENERAL_INVALID_PARAM,
00363                                  "This should never happen (size=%d)(status=%d)!",
00364                                  size, status);
00365         return -1;
00366       }
00367       if (err != H_OK)
00368       {
00369         stream->err = err;
00370         return -1;
00371       }
00372     }
00373 
00374     read += status;
00375     size -= status;
00376     stream->received += status;
00377   }
00378 
00379   return read;
00380 }
00381 
00382 
00383 static int
00384 _http_input_stream_connection_closed_read(struct http_input_stream_t * stream, unsigned char *dest, int size)
00385 {
00386   int status;
00387   herror_t err;
00388 
00389   /* read from socket */
00390   if ((err = hsocket_recv(stream->sock, dest, size, 0, &status)) != H_OK)
00391   {
00392     stream->err = err;
00393     return -1;
00394   }
00395 
00396   if (status == 0)
00397     stream->connection_closed = 1;
00398 
00399   stream->received += status;
00400   return status;
00401 }
00402 
00403 static int
00404 _http_input_stream_file_read(struct http_input_stream_t * stream, unsigned char *dest, int size)
00405 {
00406   size_t len;
00407 
00408   if ((len = fread(dest, 1, size, stream->fd)) == -1)
00409   {
00410     stream->err = herror_new("_http_input_stream_file_read",
00411                              HSOCKET_ERROR_RECEIVE, "fread() returned -1");
00412     return -1;
00413   }
00414 
00415   return len;
00416 }
00417 
00421 int
00422 http_input_stream_is_ready(struct http_input_stream_t * stream)
00423 {
00424   /* paranoia check */
00425   if (stream == NULL)
00426     return 0;
00427 
00428   /* reset error flag */
00429   stream->err = H_OK;
00430 
00431   switch (stream->type)
00432   {
00433   case HTTP_TRANSFER_CONTENT_LENGTH:
00434     return _http_input_stream_is_content_length_ready(stream);
00435   case HTTP_TRANSFER_CHUNKED:
00436     return _http_input_stream_is_chunked_ready(stream);
00437   case HTTP_TRANSFER_CONNECTION_CLOSE:
00438     return _http_input_stream_is_connection_closed_ready(stream);
00439   case HTTP_TRANSFER_FILE:
00440     return _http_input_stream_is_file_ready(stream);
00441   default:
00442     return 0;
00443   }
00444 
00445 }
00446 
00451 int
00452 http_input_stream_read(struct http_input_stream_t * stream, unsigned char *dest, int size)
00453 {
00454   int len = 0;
00455   /* paranoia check */
00456   if (stream == NULL)
00457   {
00458     return -1;
00459   }
00460 
00461   /* XXX: possible memleak! reset error flag */
00462   stream->err = H_OK;
00463 
00464   switch (stream->type)
00465   {
00466   case HTTP_TRANSFER_CONTENT_LENGTH:
00467     len = _http_input_stream_content_length_read(stream, dest, size);
00468     break;
00469   case HTTP_TRANSFER_CHUNKED:
00470     len = _http_input_stream_chunked_read(stream, dest, size);
00471     break;
00472   case HTTP_TRANSFER_CONNECTION_CLOSE:
00473     len = _http_input_stream_connection_closed_read(stream, dest, size);
00474     break;
00475   case HTTP_TRANSFER_FILE:
00476     len = _http_input_stream_file_read(stream, dest, size);
00477     break;
00478   default:
00479     stream->err = herror_new("http_input_stream_read",
00480                              STREAM_ERROR_INVALID_TYPE,
00481                              "%d is invalid stream type", stream->type);
00482     return -1;
00483   }
00484 
00485   return len;
00486 }
00487 
00488 
00489 /*
00490 -------------------------------------------------------------------
00491 
00492 HTTP OUTPUT STREAM
00493 
00494 -------------------------------------------------------------------
00495 */
00496 
00497 
00498 
00502 struct http_output_stream_t *
00503 http_output_stream_new(struct hsocket_t *sock, hpair_t * header)
00504 {
00505   struct http_output_stream_t *result;
00506   char *content_length;
00507 
00508   /* Paranoya check */
00509 /*  if (header == NULL)
00510     return NULL;
00511 */
00512   /* Create object */
00513   if (!(result = (struct http_output_stream_t *) malloc(sizeof(struct http_output_stream_t))))
00514   {
00515     log_error2("malloc failed (%s)", strerror(errno));
00516     return NULL;
00517   }
00518 
00519   result->sock = sock;
00520   result->sent = 0;
00521 
00522   /* Find connection type */
00523 
00524   /* Check if Content-type */
00525   if (_http_stream_is_content_length(header))
00526   {
00527     log_verbose1("Stream transfer with 'Content-length'");
00528     content_length = hpairnode_get_ignore_case(header, HEADER_CONTENT_LENGTH);
00529     result->content_length = atoi(content_length);
00530     result->type = HTTP_TRANSFER_CONTENT_LENGTH;
00531   }
00532   /* Check if Chunked */
00533   else if (_http_stream_is_chunked(header))
00534   {
00535     log_verbose1("Stream transfer with 'chunked'");
00536     result->type = HTTP_TRANSFER_CHUNKED;
00537   }
00538   /* Assume connection close */
00539   else
00540   {
00541     log_verbose1("Stream transfer with 'Connection: close'");
00542     result->type = HTTP_TRANSFER_CONNECTION_CLOSE;
00543   }
00544 
00545   return result;
00546 }
00547 
00551 void
00552 http_output_stream_free(struct http_output_stream_t * stream)
00553 {
00554   free(stream);
00555 
00556   return;
00557 }
00558 
00563 herror_t
00564 http_output_stream_write(struct http_output_stream_t * stream,
00565                          const unsigned char *bytes, int size)
00566 {
00567   herror_t status;
00568   char chunked[15];
00569 
00570   if (stream->type == HTTP_TRANSFER_CHUNKED)
00571   {
00572     sprintf(chunked, "%x\r\n", size);
00573     if ((status = hsocket_send_string(stream->sock, chunked)) != H_OK)
00574       return status;
00575   }
00576 
00577   if (size > 0)
00578   {
00579     if ((status = hsocket_send(stream->sock, bytes, size)) != H_OK)
00580       return status;
00581   }
00582 
00583   if (stream->type == HTTP_TRANSFER_CHUNKED)
00584   {
00585     if ((status = hsocket_send_string(stream->sock, "\r\n")) != H_OK)
00586       return status;
00587   }
00588 
00589   return H_OK;
00590 }
00591 
00596 herror_t
00597 http_output_stream_write_string(struct http_output_stream_t * stream,
00598                                 const char *str)
00599 {
00600   return http_output_stream_write(stream, str, strlen(str));
00601 }
00602 
00603 
00604 herror_t
00605 http_output_stream_flush(struct http_output_stream_t * stream)
00606 {
00607   herror_t status;
00608 
00609   if (stream->type == HTTP_TRANSFER_CHUNKED)
00610   {
00611     if ((status = hsocket_send_string(stream->sock, "0\r\n\r\n")) != H_OK)
00612       return status;
00613   }
00614 
00615   return H_OK;
00616 }

Generated on Thu Jan 25 23:36:03 2007 for csoap by  doxygen 1.4.6