summaryrefslogblamecommitdiff
path: root/nuttx/netutils/thttpd/thttpd.c
blob: 3608c94860ac0ed599583fb157842f834a084c73 (plain) (tree)






















































                                                                               

                           
 
                   



                     

                    



                                                                              








                           
                        
 


                                                               























                                                                              

                              
                            















































                                                                                  
                                
                
                                              





                                                   
                                    



                                                
                                   


         
         

                             
                

                               
                                             












                                                               

                                                                             

     
                                                          


                                                  
 
                                        


                                                                            


                       

                                          
                    



                                                                 
                   





                                                               
      



                                            
                    

                                        
                               



                                                               
 
                                    
                             











                                                                     
                    



                                                     



                   

         

                                                         



                                      
                                                        
                                   





                                           



                                                               
                                                  



                                                      


                                            














                                                                   
                                                   
         
                                   

                               
                                                                                                  






                                                                                      
                        




                           
                                                                            








                                                                         


                                                           












                                          

                                     





























































                                                                                     
                           





                                                           
                          
                                  


                
                       




































                                                                       
                                                                      
 
                                         
     

                                                                  
 
                                                               
 

                                
         
                                               

                                       
                                                             
 
                           
 











                                                                       
 


                                                                          
 

                                             
 
                                                        
 




                                              
 
                                                              
 

                               
         

                        
                             








                                                                     
                                                                             





























                                                                         
                                 





















                                                                             
                                     




                                      
                                            
                                        
                                                  

                           

                                                                               
                                     
         
                 
         
                                                           
     



                                                                                           






                                                                               
 
                                        
                                 
                                 




                                     
                                      









                                                                                   
                                              














                                                                                            













                                                                                            
                              
                                            
                            

















                                                                    
            
     
                                    















                                                                       
                                   


























                                                                                                                     















                                                                              

                                      

                

                             
                    
                    


                        
 

                            


                          
                                           





                                                   


                                                                   
 

                                                      




















                                                              
                                        
                             

          
                                          




                                   
                                                                                                     






                                              
                                                                   









                                                      



                                     





                                        
                                                  
                       
     
                                                            


              
                                              
     
                                                   
                                                  
                                              

     
                                                                               
                         

                         
 
                 

                              


                                                  



                 
                                    
                                



                                          
                                                  


                                                





                                           
                                              


                  
                                    










                                                  
                                              
         
                                                     











                                                                           
                                                                                                   
         
                   
             



                                                     
 



                                                 
                 



                                                                    












                                                                                 
                                        







                                                                                     
                              
 
                                          




                                                           

                              












                                
                          
 
/****************************************************************************
 * netutils/thttpd/thttpd.c
 * Tiny HTTP Server
 *
 *   Copyright (C) 2009 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * Derived from the file of the same name in the original THTTPD package:
 *
 *   Copyright � 1995,1998,1999,2000,2001 by Jef Poskanzer <jef@mail.acme.com>.
 *   All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <debug.h>

#include <nuttx/compiler.h>
#include <nuttx/symtab.h>
#include <net/uip/thttpd.h>

#include "config.h"
#include "fdwatch.h"
#include "libhttpd.h"
#include "timers.h"

#ifdef CONFIG_THTTPD

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#ifndef MAXPATHLEN
#  define MAXPATHLEN 64
#endif

/* The connection states */

#define CNST_FREE      0
#define CNST_READING   1
#define CNST_SENDING   2
#define CNST_LINGERING 3

#define SPARE_FDS      2
#define AVAILABLE_FDS  (CONFIG_NSOCKET_DESCRIPTORS - SPARE_FDS)

/****************************************************************************
 * Private Types
 ****************************************************************************/

struct connect_s
{
  int conn_state;
  int next_free_connect;
  httpd_conn *hc;
  time_t active_at;
  Timer *wakeup_timer;
  Timer *linger_timer;
  off_t end_offset;            /* The final offset+1 of the file to send */
  off_t offset;                /* The current offset into the file to send */
  boolean eof;                 /* Set TRUE when length==0 read from file */
};

/****************************************************************************
 * Private Data
 ****************************************************************************/

static httpd_server *hs = NULL;
static struct connect_s *connects;
static int num_connects;
static int first_free_connect;
static int httpd_conn_count;
static struct fdwatch_s *fw;

/****************************************************************************
 * Public Data
 ****************************************************************************/

int terminate = 0;

#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
time_t start_time;
time_t stats_time;
long   stats_connections;
off_t  stats_bytes;
int    stats_simultaneous;
#endif

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

static void shut_down(void);
static int  handle_newconnect(struct timeval *tv, int listen_fd);
static void handle_read(struct connect_s *conn, struct timeval *tv);
static void handle_send(struct connect_s *conn, struct timeval *tv);
static void handle_linger(struct connect_s *conn, struct timeval *tv);
static void finish_connection(struct connect_s *conn, struct timeval *tv);
static void clear_connection(struct connect_s *conn, struct timeval *tv);
static void really_clear_connection(struct connect_s *conn, struct timeval *tv);
static void idle(ClientData client_data, struct timeval *nowP);
static void linger_clear_connection(ClientData client_data, struct timeval *nowP);
static void occasional(ClientData client_data, struct timeval *nowP);

#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
static void logstats(struct timeval *nowP);
static void thttpd_logstats(long secs);
#else
#  define logstats(nowP)
#  define thttpd_logstats(secs)
#endif

/****************************************************************************
 * Private Functions
 ****************************************************************************/

static void shut_down(void)
{
  int cnum;
  struct timeval tv;

  (void)gettimeofday(&tv, NULL);
  logstats(&tv);
  for (cnum = 0; cnum < AVAILABLE_FDS; ++cnum)
    {
      if (connects[cnum].conn_state != CNST_FREE)
        {
          httpd_close_conn(connects[cnum].hc, &tv);
        }

      if (connects[cnum].hc != NULL)
        {
          httpd_destroy_conn(connects[cnum].hc);
          free((void *)connects[cnum].hc);
          --httpd_conn_count;
          connects[cnum].hc = NULL;
        }
    }

  if (hs)
    {
      httpd_server *ths = hs;
      hs = NULL;
      if (ths->listen_fd != -1)
        {
          fdwatch_del_fd(fw, ths->listen_fd);
        }
      httpd_terminate(ths);
    }

  tmr_destroy();
  free((void *)connects);
}

static int handle_newconnect(struct timeval *tv, int listen_fd)
{
  struct connect_s *conn;
  ClientData client_data;

  /* This loops until the accept() fails, trying to start new connections as 
   * fast as possible so we don't overrun the listen queue.
   */

  nvdbg("New connection(s) on listen_fd %d\n", listen_fd);
  for (;;)
    {
      /* Is there room in the connection table? */

      if (num_connects >= AVAILABLE_FDS)
        {
          /* Out of connection slots.  Run the timers, then the  existing
           * connections, and maybe we'll free up a slot  by the time we get
           * back here.
           */

          ndbg("too many connections!\n");
          tmr_run(tv);
          return -1;
        }

      /* Get the first free connection entry off the free list */

#ifdef CONFIG_DEBUG
      if (first_free_connect == -1 ||
          connects[first_free_connect].conn_state != CNST_FREE)
        {
          ndbg("the connects free list is messed up\n");
          exit(1);
        }
#endif
      conn = &connects[first_free_connect];

      /* Make the httpd_conn if necessary */

      if (!conn->hc)
        {
          conn->hc = NEW(httpd_conn, 1);
          if (conn->hc == NULL)
            {
              ndbg("out of memory allocating an httpd_conn\n");
              exit(1);
            }

          conn->hc->initialized = 0;
          httpd_conn_count++;
        }

      /* Get the connection */

      switch (httpd_get_conn(hs, listen_fd, conn->hc))
        {
          /* Some error happened.  Run the timers, then the  existing
           * connections.  Maybe the error will clear.
           */

        case GC_FAIL:
          tmr_run(tv);
          return -1;

          /* No more connections to accept for now */

        case GC_NO_MORE:
          return 0;

        default:
          break;
        }

      nvdbg("New connection fd %d\n", conn->hc->conn_fd);

      conn->conn_state = CNST_READING;

      /* Pop it off the free list */

      first_free_connect      = conn->next_free_connect;
      conn->next_free_connect = -1;
      num_connects++;
      client_data.p           = conn;
      conn->active_at         = tv->tv_sec;
      conn->wakeup_timer      = NULL;
      conn->linger_timer      = NULL;
      conn->offset            = 0;
 
      /* Set the connection file descriptor to no-delay mode */

      httpd_set_ndelay(conn->hc->conn_fd);
      fdwatch_add_fd(fw, conn->hc->conn_fd, conn);

#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
      ++stats_connections;
      if (num_connects > stats_simultaneous)
        {
          stats_simultaneous = num_connects;
        }
#endif
    }
}

static void handle_read(struct connect_s *conn, struct timeval *tv)
{
  ClientData client_data;
  httpd_conn *hc = conn->hc;
  off_t actual;
  int sz;

  /* Is there room in our buffer to read more bytes? */

  if (hc->read_idx >= hc->read_size)
    {
      if (hc->read_size > CONFIG_THTTPD_MAXREALLOC)
        {
          BADREQUEST("MAXREALLOC");
          goto errout_with_400;
        }
      httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + CONFIG_THTTPD_REALLOCINCR);
    }

  /* Read some more bytes */

  sz = read(hc->conn_fd, &(hc->read_buf[hc->read_idx]), hc->read_size - hc->read_idx);
  if (sz == 0)
    {
      BADREQUEST("EOF");
      goto errout_with_400;
    }

  if (sz < 0)
    {
      /* Ignore EINTR and EAGAIN.  Also ignore EWOULDBLOCK.  At first glance
       * you would think that connections returned by fdwatch as readable
       * should never give an EWOULDBLOCK; however, this apparently can
       * happen if a packet gets garbled.
       */

      if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
        {
          return;
        }
 
      ndbg("read(fd=%d) failed: %d\n", hc->conn_fd, errno);
      BADREQUEST("read");
      goto errout_with_400;
    }

  hc->read_idx += sz;
  conn->active_at = tv->tv_sec;

  /* Do we have a complete request yet? */

  switch (httpd_got_request(hc))
    {
    case GR_NO_REQUEST:
      return;
    case GR_BAD_REQUEST:
     BADREQUEST("httpd_got_request");
     goto errout_with_400;
    }

  /* Yes.  Try parsing and resolving it */

  if (httpd_parse_request(hc) < 0)
    {
      goto errout_with_connection;
    }

  /* Start the connection going */

  if (httpd_start_request(hc, tv) < 0)
    {
      /* Something went wrong.  Close down the connection */

      goto errout_with_connection;
    }

  /* Set up the file offsets to read */

  conn->eof            = FALSE;
  if (hc->got_range)
    {
      conn->offset     = hc->range_start;
      conn->end_offset = hc->range_end + 1;
    }
  else
    {
      conn->offset     = 0;
      if (hc->bytes_to_send < 0)
        {
          conn->end_offset = 0;
        }
      else
        {
          conn->end_offset = hc->bytes_to_send;
        }
    }

  /* Check if it's already handled */

  if (hc->file_fd < 0)
    {
      /* No file descriptor means someone else is handling it */

      conn->offset = hc->bytes_sent;
      goto errout_with_connection;
    }

  if (conn->offset >= conn->end_offset)
    {
      /* There's nothing to send */

      goto errout_with_connection;
    }

  /* Seek to the offset of the next byte to send */

  actual = lseek(hc->file_fd, conn->offset, SEEK_SET);
  if (actual != conn->offset)
    {
       ndbg("fseek to %d failed: offset=%d errno=%d\n", conn->offset, actual, errno);
       BADREQUEST("lseek");
       goto errout_with_400;
    }

  /* We have a valid connection and a file to send to it */

  conn->conn_state = CNST_SENDING;
  client_data.p    = conn;
  fdwatch_del_fd(fw, hc->conn_fd);
  return;

errout_with_400:
  BADREQUEST("errout");
  httpd_send_err(hc, 400, httpd_err400title, "", httpd_err400form, "");

errout_with_connection:
  finish_connection(conn, tv);
  return;
}

static inline int read_buffer(struct connect_s *conn)
{
  httpd_conn *hc = conn->hc;
  ssize_t nread = 0;

  if (hc->buflen < CONFIG_THTTPD_IOBUFFERSIZE && !conn->eof)
    {
      nread = read(hc->file_fd, &hc->buffer[hc->buflen],
                   CONFIG_THTTPD_IOBUFFERSIZE - hc->buflen);
      if (nread == 0)
        {
          /* Reading zero bytes means we are at the end of file */

          conn->end_offset = conn->offset;
          conn->eof        = TRUE;
        }
      else if (nread > 0)
        {
          hc->buflen      += nread;
        }
    }
  return nread;
}

static void handle_send(struct connect_s *conn, struct timeval *tv)
{
  httpd_conn *hc = conn->hc;
  int nwritten;
  int nread;

  /* Read until the entire file is sent -- this could take awhile!! */

  while (conn->offset < conn->end_offset)
    {
      nvdbg("offset: %d end_offset: %d bytes_sent: %d\n",
            conn->offset, conn->end_offset, conn->hc->bytes_sent);

      /* Fill the rest of the response buffer with file data */

      nread = read_buffer(conn);
      if (nread < 0)
        {
          ndbg("File read error: %d\n", errno);
          goto errout_clear_connection;
        }
      nvdbg("Read %d bytes, buflen %d\n", nread, hc->buflen);

      /* Send the buffer */

      if (hc->buflen > 0)
        {
          /* httpd_write does not return until all bytes have been sent
           * (or an error occurs).
           */
 
          nwritten = httpd_write(hc->conn_fd, hc->buffer, hc->buflen);
          if (nwritten < 0)
            {
              ndbg("Error sending %s: %d\n", hc->encodedurl, errno);
              goto errout_clear_connection;
            }

          /* We wrote one full buffer of data (httpd_write does not
           * return until the full buffer is written (or an error occurs).
           */

          conn->active_at       = tv->tv_sec;
          hc->buflen            = 0;

          /* And update how much of the file we wrote */

          conn->offset         += nwritten;
          conn->hc->bytes_sent += nwritten;
          nvdbg("Wrote %d bytes\n", nwritten);
        }
    }

  /* The file transfer is complete -- finish the connection */

  nvdbg("Finish connection\n");
  finish_connection(conn, tv);
  return;

errout_clear_connection:
  ndbg("Clear connection\n");
  clear_connection(conn, tv);
  return;
}

static void handle_linger(struct connect_s *conn, struct timeval *tv)
{
  char buf[4096];
  int ret;

  /* In lingering-close mode we just read and ignore bytes.  An error or EOF 
   * ends things, otherwise we go until a timeout 
   */

  ret = read(conn->hc->conn_fd, buf, sizeof(buf));
  if (ret < 0 && (errno == EINTR || errno == EAGAIN))
    {
      return;
    }

  if (ret <= 0)
    {
      really_clear_connection(conn, tv);
    }
}

static void finish_connection(struct connect_s *conn, struct timeval *tv)
{
  /* If we haven't actually sent the buffered response yet, do so now */

  httpd_write_response(conn->hc);

  /* And clear */

  clear_connection(conn, tv);
}

static void clear_connection(struct connect_s *conn, struct timeval *tv)
{
  ClientData client_data;

  if (conn->wakeup_timer != NULL)
    {
      tmr_cancel(conn->wakeup_timer);
      conn->wakeup_timer = 0;
    }

  /* This is our version of Apache's lingering_close() routine, which is
   * their version of the often-broken SO_LINGER socket option.  For why
   * this is necessary, see http://www.apache.org/docs/misc/fin_wait_2.html
   * What we do is delay the actual closing for a few seconds, while reading
   * any bytes that come over the connection.  However, we don't want to do
   * this unless it's necessary, because it ties up a connection slot and
   * file descriptor which means our maximum connection-handling rateis
   * lower.  So, elsewhere we set a flag when we detect the few
   * circumstances that make a lingering close necessary.  If the flag isn't 
   * set we do the real close now.
   */

  if (conn->conn_state == CNST_LINGERING)
    {
      /* If we were already lingering, shut down for real */

      tmr_cancel(conn->linger_timer);
      conn->linger_timer      = NULL;
      conn->hc->should_linger = FALSE;
    }

  if (conn->hc->should_linger)
    {
      fdwatch_del_fd(fw, conn->hc->conn_fd);
      conn->conn_state = CNST_LINGERING;
      fdwatch_add_fd(fw, conn->hc->conn_fd, conn);
      client_data.p = conn;

      conn->linger_timer = tmr_create(tv, linger_clear_connection, client_data,
                                      CONFIG_THTTPD_LINGER_MSEC, 0);
      if (conn->linger_timer != NULL)
        {
          return;
        }
      ndbg("tmr_create(linger_clear_connection) failed\n");
    }

  /* Either we are done lingering, we shouldn't linger, or we failed to setup the linger */

  really_clear_connection(conn, tv);
}

static void really_clear_connection(struct connect_s *conn, struct timeval *tv)
{
#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
  stats_bytes += conn->hc->bytes_sent;
#endif

  fdwatch_del_fd(fw, conn->hc->conn_fd);
  httpd_close_conn(conn->hc, tv);
  if (conn->linger_timer != NULL)
    {
      tmr_cancel(conn->linger_timer);
      conn->linger_timer = 0;
    }

  conn->conn_state        = CNST_FREE;
  conn->next_free_connect = first_free_connect;
  first_free_connect      = conn - connects;    /* division by sizeof is implied */
  num_connects--;
}

static void idle(ClientData client_data, struct timeval *nowP)
{
  int cnum;
  struct connect_s *conn;

  for (cnum = 0; cnum < AVAILABLE_FDS; ++cnum)
    {
      conn = &connects[cnum];
      switch (conn->conn_state)
        {
        case CNST_READING:
          if (nowP->tv_sec - conn->active_at >= CONFIG_THTTPD_IDLE_READ_LIMIT_SEC)
            {
              ndbg("%s connection timed out reading\n", httpd_ntoa(&conn->hc->client_addr));
              httpd_send_err(conn->hc, 408, httpd_err408title, "",
                             httpd_err408form, "");
              finish_connection(conn, nowP);
            }
          break;

        case CNST_SENDING:
          if (nowP->tv_sec - conn->active_at >= CONFIG_THTTPD_IDLE_SEND_LIMIT_SEC)
            {
              ndbg("%s connection timed out sending\n", httpd_ntoa(&conn->hc->client_addr));
              clear_connection(conn, nowP);
            }
          break;
        }
    }
}

static void linger_clear_connection(ClientData client_data, struct timeval *nowP)
{
  struct connect_s *conn;

  nvdbg("Clear connection\n");
  conn = (struct connect_s *) client_data.p;
  conn->linger_timer = NULL;
  really_clear_connection(conn, nowP);
}

static void occasional(ClientData client_data, struct timeval *nowP)
{
  tmr_cleanup();
}

/* Generate debugging statistics ndbg messages for all packages */

#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
static void logstats(struct timeval *nowP)
{
  struct timeval tv;
  time_t now;
  long up_secs;
  long stats_secs;

  if (!nowP)
    {
      (void)gettimeofday(&tv, NULL);
      nowP = &tv;
    }

  now        = nowP->tv_sec;
  up_secs    = now - start_time;
  stats_secs = now - stats_time;
  if (stats_secs == 0)
    {
      stats_secs = 1;             /* fudge */
    }

  stats_time = now;
  ndbg("up %ld seconds, stats for %ld seconds\n", up_secs, stats_secs);

  thttpd_logstats(stats_secs);
  httpd_logstats(stats_secs);
  fdwatch_logstats(fw, stats_secs);
  tmr_logstats(stats_secs);
}
#endif

/* Generate debugging statistics */

#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
static void thttpd_logstats(long secs)
{
  if (secs > 0)
    {
      ndbg("thttpd - %ld connections (%g/sec), %d max simultaneous, %lld bytes (%g/sec), %d httpd_conns allocated\n",
           stats_connections, (float)stats_connections / secs,
           stats_simultaneous, (sint64) stats_bytes,
           (float)stats_bytes / secs, httpd_conn_count);
    }

  stats_connections  = 0;
  stats_bytes        = 0;
  stats_simultaneous = 0;
}
#endif

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Function: thttpd_main
 *
 * Description:
 *   This function is the entrypoint into the THTTPD server.  It does not
 *   return.  It may be called, the normal mechanism for starting the server
 *   is:
 *
 *   1) Set is g_thttpdsymtab and g_thttpdnsymbols.  The user is required
 *      to provide a symbol table to use for binding CGI programs (if CGI
 *      is enabled.  See examples/nxflat and examples/thttpd for examples of
 *      how such a symbol table may be created.
 *   2) Call task_create() to start thttpd_main()
 *
 ****************************************************************************/

int thttpd_main(int argc, char **argv)
{
  int num_ready;
  int cnum;
  FAR struct connect_s *conn;
  FAR httpd_conn *hc;
  httpd_sockaddr sa;
  struct timeval tv;
#ifdef CONFIG_THTTPD_DIR
  int ret;
#endif

  nvdbg("THTTPD started\n");

  /* Setup host address */

#ifdef  CONFIG_NET_IPv6
#  error "IPv6 support not yet implemented"
#else
  sa.sin_family      = AF_INET;
  sa.sin_port        = HTONS(CONFIG_THTTPD_PORT);
  sa.sin_addr.s_addr = HTONL(CONFIG_THTTPD_IPADDR);
#endif

  /* Initialize the fdwatch package to handle all of the configured
   * socket descriptors
   */

  fw = fdwatch_initialize(CONFIG_NSOCKET_DESCRIPTORS);
  if (!fw)
    {
      ndbg("fdwatch initialization failure\n");
      exit(1);
    }

  /* Switch directories again if requested */

#ifdef CONFIG_THTTPD_DATADIR
  if (chdir(CONFIG_THTTPD_DATADIR) < 0)
    {
      ndbg("chdir to %s: %d\n", CONFIG_THTTPD_DATADIR, errno);
      exit(1);
    }
#endif

  /* Initialize the timer package */

  tmr_init();

  /* Initialize the HTTP layer */

  nvdbg("Calling httpd_initialize()\n");
  hs = httpd_initialize(&sa);
  if (!hs)
    {
      ndbg("httpd_initialize() failed\n");
      exit(1);
    }

  /* Set up the occasional timer */

  if (tmr_create(NULL, occasional, JunkClientData, CONFIG_THTTPD_OCCASIONAL_MSEC * 1000L, 1) == NULL)
    {
      ndbg("tmr_create(occasional) failed\n");
      exit(1);
    }

  /* Set up the idle timer */

  if (tmr_create(NULL, idle, JunkClientData, 5 * 1000L, 1) == NULL)
    {
      ndbg("tmr_create(idle) failed\n");
      exit(1);

    }

#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET)
    {
      struct timeval ts;
      gettimeofday(&ts, NULL);
      start_time         = ts.tv_sec;
      stats_time         = ts.tv_sec;
      stats_connections  = 0;
      stats_bytes        = 0;
      stats_simultaneous = 0;
    }
#endif

  /* Initialize our connections table */

  connects = NEW(struct connect_s, AVAILABLE_FDS);
  if (connects == NULL)
    {
      ndbg("Out of memory allocating a struct connect_s\n");
      exit(1);
    }

  for (cnum = 0; cnum < AVAILABLE_FDS; ++cnum)
    {
      connects[cnum].conn_state        = CNST_FREE;
      connects[cnum].next_free_connect = cnum + 1;
      connects[cnum].hc                = NULL;
    }

  connects[AVAILABLE_FDS - 1].next_free_connect = -1;    /* end of link list */
  first_free_connect = 0;
  num_connects       = 0;
  httpd_conn_count   = 0;

  if (hs != NULL)
    {
      if (hs->listen_fd != -1)
        {
          fdwatch_add_fd(fw, hs->listen_fd, NULL);
        }
    }

  /* Main loop */

  nvdbg("Entering the main loop\n");
  (void)gettimeofday(&tv, NULL);
  while ((!terminate) || num_connects > 0)
    {
      /* Do the fd watch */

      num_ready = fdwatch(fw, tmr_mstimeout(&tv));
      if (num_ready < 0)
        {
          if (errno == EINTR || errno == EAGAIN)
            {
              /* Not errors... try again */

              continue;
            }

          ndbg("fdwatch failed: %d\n", errno);
          exit(1);
        }

      (void)gettimeofday(&tv, NULL);

      if (num_ready == 0)
        {
          /* No fd's are ready - run the timers */

          tmr_run(&tv);
          continue;
        }

      /* Is it a new connection? */

      if (fdwatch_check_fd(fw, hs->listen_fd))
        {
          if (!handle_newconnect(&tv, hs->listen_fd))
            {
              /* Go around the loop and do another fdwatch, rather than 
               * dropping through and processing existing connections.  New
               * connections always get priority.
               */

              continue;
            }
        }

      /* Find the connections that need servicing */

      while ((conn = (struct connect_s*)fdwatch_get_next_client_data(fw)) != (struct connect_s*)-1)
        {
          if (conn)
            {
              hc = conn->hc;
              if (!fdwatch_check_fd(fw, hc->conn_fd))
                {
                  /* Something went wrong */

                  nvdbg("Clearing connection\n");
                  clear_connection(conn, &tv);
                }
              else
                {
                  nvdbg("Handle conn_state %d\n", conn->conn_state);
                  switch (conn->conn_state)
                    {
                      case CNST_READING:
                        {
                          handle_read(conn, &tv);

                          /* If a GET request was received and a file is ready to
                           * be sent, then fall through to send the file.
                           */

                          if (conn->conn_state != CNST_SENDING)
                            { 
                              break;
                            }
                        }

                      case CNST_SENDING:
                        {
                          /* Send a file -- this really should be performed on a
                           * separate thread to keep the serve from locking up during
                           * the write.
                           */

                          handle_send(conn, &tv);
                        }
                        break;

                      case CNST_LINGERING:
                        {
                          /* Linger close the connection */

                          handle_linger(conn, &tv);
                        }
                        break;
                    }
                }
            }
        }
      tmr_run(&tv);
    }

  /* The main loop terminated */

  shut_down();
  ndbg("Exiting\n");
  exit(0);
}

#endif /* CONFIG_THTTPD */