⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 httpd.c

📁 picoos源码。The RTOS and the TCP/IP stack will be built automatically.
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 *  Copyright (c) 2004, Dennis Kuschel.
 *  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.
 *   3. The name of the author may not be used to endorse or promote
 *      products derived from this software without specific prior written
 *      permission. 
 *
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
 *
 */

/**
 * @file    httpd.c
 * @author  Dennis Kuschel
 * @brief   HTTP demon for embedded devices
 *
 * This software is from http://mycpu.mikrocontroller.net.
 * Please send questions and bug reports to dennis_k@freenet.de.
 */

#include "httpd.h"

#ifndef NO_EFS
#include "filesys.h"
#endif


/* MS Windows definitions for file access */
#ifdef NO_EFS
#include <sys/stat.h>
#include <fcntl.h>
#include <io.h>
#define  fsys_stat          _stat
#define  fsys_open          _open
#define  fsys_close         _close
#define  fsys_read          _read
#define  FSO_RDONLY         _O_RDONLY
#define  FSO_BINARY         _O_BINARY
#define  FS_SUBDIRECTORIES  0
#define  FS_MAXFNAMELEN     256
#define  DOS_PATH
#endif



/*---------------------------------------------------------------------------
 *  DEFINES
 *-------------------------------------------------------------------------*/

#define HTTP_PORT           80
#define HTTP_FNAMESIZE      FS_MAXFNAMELEN
#define HTTP_RXBUFSIZE      1024 /* should be big enough to hold a complete header */
#define HTTP_TXBUFSIZE      1024
#define HTTP_MAXSTRLEN      ((HTTP_FNAMESIZE < 512) ? 512 : HTTP_FNAMESIZE)
#define HTTP_OPTSTRSIZE     512
#define HTTP_TEMPBUFSIZE    (2*HTTP_MAXSTRLEN)
#define HTTP_MAXCONN        (FD_SETSIZE-(FD_SETSIZE/4))
#define HTTP_TIMEOUT_TRANS  300  /* seconds */
#define HTTP_TIMEOUT_IDLE   30   /* seconds */
#define HTTP_SERVLETMINBUF  1024
#define HTTP_SESSIONIDNAME  "sid"
#define HTTP_MAXSIDLENGTH   (3 + 1 + 8)



/*---------------------------------------------------------------------------
 *  MACROS
 *-------------------------------------------------------------------------*/

#define ALIGN32(val)      (((val) + sizeof(u32_t)-1) & ~(sizeof(u32_t)-1))



/*---------------------------------------------------------------------------
 *  TYPEDEFS
 *-------------------------------------------------------------------------*/

/* List link header.
 */
typedef struct lelem_s  lelem_t;
struct lelem_s
{
  lelem_t   *prev;
  lelem_t   *next;
};

#define INIT_LIST_HEAD(head) \
  do { (head)->prev = head; (head)->next = head; } while(0)
#define END_OF_LIST(head, elem)     ((void*)(head) == (void*)(elem))
#define IS_LIST_EMPTY(head)         ((head)->next == (head))
#define IS_ON_LIST(listelem)        ((listelem)->next != (listelem))
#define NEXT_LIST_ELEM(elem, type)  \
          (type)(void*)((lelem_t*)(void*)(elem))->next
#define PREV_LIST_ELEM(elem, type)  \
          (type)(void*)((lelem_t*)(void*)(elem))->prev
#define FIRST_LIST_ELEM(head, type) NEXT_LIST_ELEM(head, type)
#define LAST_LIST_ELEM(head, type)  PREV_LIST_ELEM(head, type)
#define GET_TRUE_ELEM(elem, type, link) \
        ((type)(void*)((char*)(elem)-(char*)(&((type)NULL)->link)))


typedef struct {
  const char    *extension;
  sint_t        contenttype;
} HTTPTYPEDEC_t;

#define STATE_INIT              0
#define STATE_UPSTREAM          1
#define STATE_DOWNSTREAM        2

#define KEEPALIVE_PERDEFAULT    1
#define KEEPALIVE_UPONREQUEST   2

#if HTTP_SERVLETS

typedef struct {
  lelem_t       list;      /* must be the first element */
  SERVLETFUNC_t func;
  char          filename[ALIGN32(HTTP_FNAMESIZE+1)];
} SLINFO_t;

typedef struct SLBUF {
  struct SLBUF  *nextbuf;
  char          buf[HTTP_SERVLETMINBUF];
} SLBUF_t;

typedef struct SESSION {
  lelem_t       list;      /* must be the first element */
  COUNTERTYPE   lastUsed;
  u32_t         timeout;
  u32_t         id;
  u32_t         userMemSize;
  u32_t         userData[1]; /* placeholder, needed for alignment */
  /* user data follows here */
} SESSION_t;

#endif


typedef struct {
  lelem_t       list;      /* must be the first element */
#if HTTP_SERVLETS
  lelem_t       mwlist;    /* link to memory-waiting-list */
#endif
  sint_t        http11;    /* HTTP version: 0 = 1.0 or below / 1 = 1.1 or above */
  sint_t        keepalive; /* HTTP/1.1: connection keep alive requested if nonzero */
  sint_t        method;    /* type of method, see method_xxx defines */
  sint_t        cont100;   /* server must send "100 continue" */
  sint_t        contenttype; /* type of content, see content_xxx defines */
  sint_t        state;     /* connection state, see STATE_xxx defines */
  sint_t        mustseek;  /* 1: must set file pointer before next send-call */
  sint_t        dataavail; /* 1: select returned with 'socket readable' */
  sint_t        wrDisabled;/* 1: socket write disabled because it would block */
  int           socket;
  sint_t        filehandle;
  u32_t         filesize;  /* size of the file for entity-header "Content-Length" */
  u32_t         transfered; /* count of transfered bytes */
  u32_t         upStreamLen;  /* strem length for PUT method */
  uint_t        txbufptr;
  uint_t        txbuflen;
  uint_t        rxbuflen;
  uint_t        rxbufptr;
  uint_t        rxlinelen;
  uint_t        emptyline;
  uint_t        havebackchar;
#if HTTP_SERVLETS
  SLINFO_t      *servlet;    /* if not NULL, reference to servlet */
  SESSION_t     *session;    /* if not NULL, pointer to associated session */
  SLBUF_t       *stdoutbuf;  /* buffer for std output */
  SLBUF_t       *curoutbuf;  /* current output buffer */
  SLBUF_t       *freeSlBufs; /* list of free buffers */
  uint_t        outbufsize;  /* size of allocated output buffer */
  uint_t        outbufptr;   /* ptr into a output buffer */
  uint_t        stdoutlen;   /* length of last out buffer */
  uint_t        neededBufs;  /* count of sl-buffers needed */
  u32_t         linkflags;   /* options for a new created link */
  char          optstring[ALIGN32(HTTP_OPTSTRSIZE+2)];
  char          linkbuf[ALIGN32(HTTP_MAXSTRLEN+1)];
#endif
  char          filename[ALIGN32(HTTP_FNAMESIZE+1)];
  u8_t          txbuffer[ALIGN32(HTTP_TXBUFSIZE)];
  u8_t          rxbuffer[ALIGN32(HTTP_RXBUFSIZE)];
  char          rxlinebuf[ALIGN32(HTTP_MAXSTRLEN)];
  u8_t          backchar;
  COUNTERTYPE   lasttransfer;
} HTTPCONN_t;


typedef struct {
  const char    *text;
  sint_t        sendhtml;   /* send status also as short html file */
  sint_t        closeconn;  /* nonzero when connection shall be closed */
} HTTPSTATUS_t;



/*---------------------------------------------------------------------------
 *  FUNCTION PROTOTYPES
 *-------------------------------------------------------------------------*/

static void     ht_listAddTail(lelem_t *headelem, lelem_t *elem);
static void     ht_listAddHead(lelem_t *headelem, lelem_t *elem);
static void     ht_listDel(lelem_t *elem);
static HTTPCONN_t*  ht_newConnection(int socket);
static void     ht_destroyConnection(HTTPCONN_t* conn);
static int      ht_newServerSocket(void);
static sint_t   ht_addRdSocket(const int sock);
static sint_t   ht_addWrSocket(const int sock);
static void     ht_delSocket(const int sock);
static void     ht_acceptClient(void);
static sint_t   ht_readByte(HTTPCONN_t *conn, u8_t *data);
static char*    ht_readLine(HTTPCONN_t *conn);
static char*    ht_movePtrToParam(char *s);
static sint_t   ht_readURI(HTTPCONN_t *conn, char *str);
static char*    ht_skipWord(char *m);
static sint_t   ht_fillInputBuffer(HTTPCONN_t *conn);
static sint_t   ht_sendStatus(HTTPCONN_t *conn, sint_t status);
static void     ht_initConnection(HTTPCONN_t* conn);
static void     ht_nextTransfer(HTTPCONN_t *conn);
static void     ht_handleDownStream(HTTPCONN_t *conn);
static void     ht_handleUpStream(HTTPCONN_t *conn);
static void     ht_setContentType(HTTPCONN_t *conn);
static char*    ht_buildFilename(const char* uriOrFile);
static sint_t   ht_openFile(HTTPCONN_t *conn, sint_t mode, u32_t *size);
static void     ht_closeFile(HTTPCONN_t *conn);
static sint_t   ht_readFile(HTTPCONN_t *conn, char *buf, u32_t count);
static void     ht_timeout(void);
static void     ht_disableWrite(HTTPCONN_t *conn);

#if HTTP_SERVLETS
sint_t          hsl_create(SERVLETFUNC_t func, const char *filename);
sint_t          hsl_destroy(SERVLETFUNC_t func);
char*           hsl_getParameterByName(SERVLET_t *slobj, const char *name);
static SLBUF_t  *ht_allocServletBuffer(void);
static void     ht_freeServletBuffer(SLBUF_t *buf);
static void     ht_freeServletBufferChain(SLBUF_t *firstbuf);
static sint_t   ht_tryAllocateBuffers(HTTPCONN_t *conn);
static void     ht_handleServlet(HTTPCONN_t *conn);
static u32_t    ht_random(sint_t init);
static u32_t    ht_newSessionId(void);
static void     ht_destroySession(SESSION_t *ss);
static sint_t   ht_assignSession(HTTPCONN_t *conn);
static sint_t   ht_isReservedChar(char c);
static void     ht_doEscape(const char *inbuf, char *outbuf, sint_t bufsize);
#endif



/*---------------------------------------------------------------------------
 *  GLOBAL VARIABLES
 *-------------------------------------------------------------------------*/

static volatile sint_t   httpd_running_g = 0;
static volatile sint_t   httpd_terminate_request_g;

static lelem_t  unusedConnList_g;
static lelem_t  activeConnList_g;
static int      serverSocket_g;
static fd_set   globalRdSocketSet;
static fd_set   globalWrSocketSet;
static uint_t   connections_g;
static u8_t     tempbuf1_g[HTTP_TEMPBUFSIZE];
static u8_t     tempbuf2_g[HTTP_TEMPBUFSIZE];
static char     httpRootDir_g[ALIGN32(HTTP_FNAMESIZE+1)];
static sint_t   blockedConns_g;

#if HTTP_SERVLETS
static lelem_t  memWaitingList_g;
static lelem_t  servletList_g;
static lelem_t  sessionListHead_g; 
static u32_t    maxServletMem_g;
static u32_t    maxSessionMem_g;
static u32_t    allocatedServletMem_g;
static u32_t    allocatedSessionMem_g;
static SLBUF_t  *freeServletBufList_g;
SYS_MTASK_DECLARELOCK(servletLock_g)
#endif

enum {
  method_get = 0,
  method_head,
  method_post,
  method_put,
#if 0
  method_delete,
  method_options,
  method_trace,
  method_connect,
#endif
  method_unknown,
  method_none
};

static char *methodList_g[] = {
  "GET", "HEAD", "POST", "PUT",
#if 0 
  "DELETE", "OPTIONS", "TRACE", "CONNECT",
#endif
  "\0"
};

enum {
  status_100 = 0,   /* Continue */
  status_101,       /* Switching Protocols */
  status_200,       /* OK */
#if HTTP_SERVLETS
  status_busy,      /* server is busy */
  status_idtimeout, /* session id timed out */
#endif
  status_201,       /* Created */
  status_202,       /* Accepted */
  status_204,       /* No Content */
  status_206,       /* Partial Content */
  status_400,       /* Bad Request */
  status_403,       /* Forbidden */
  status_404,       /* Not Found */
  status_405,       /* Method Not Allowed */
  status_413,       /* Request Entity Too Large */
  status_414,       /* Request-URI Too Large */
  status_417,       /* Expectation Failed */
  status_500,       /* Internal Server Error */
  status_501,       /* Not Implemented */
  status_503        /* Service Unavailable */
#if 0
  status_203,       /* Non-Authoritative Information */
  status_205,       /* Reset Content */
  status_300,       /* Multiple Choices */
  status_301,       /* Moved Permanently */
  status_302,       /* Found */
  status_303,       /* See Other */
  status_304,       /* Not Modified */
  status_305,       /* Use Proxy */
  status_307,       /* Temporary Redirect */
  status_401,       /* Unauthorized */
  status_406,       /* Not Acceptable */
  status_407,       /* Proxy Authentication Required */
  status_408,       /* Request Time-out */
  status_409,       /* Conflict */
  status_410,       /* Gone */
  status_411,       /* Length Required */
  status_412,       /* Precondition Failed */
  status_415,       /* Unsupported Media Type */
  status_416,       /* Requested range not satisfiable */
  status_502,       /* Bad Gateway */
  status_504,       /* Gateway Time-out */
  status_505,       /* HTTP Version not supported */
#endif
};

HTTPSTATUS_t  statustab_g[] =
{
  { "100 Continue"                       , 0 , 0 },
  { "101 Switching Protocols"            , 0 , 0 },
  { "200 OK"                             , 0 , 0 },
#if HTTP_SERVLETS
  { "200 OK (server busy)"               , 1 , 0 },
  { "200 OK (session ID timed out)"      , 1 , 0 },
#endif
  { "201 Created"                        , 0 , 0 },
  { "202 Accepted"                       , 0 , 0 },
  { "204 No Content"                     , 0 , 0 },
  { "206 Partial Content"                , 0 , 0 },
  { "400 Bad Request"                    , 1 , 1 },
  { "403 Forbidden"                      , 1 , 1 },
  { "404 File Not Found"                 , 1 , 0 },
  { "405 Method Not Allowed"             , 1 , 1 },
  { "413 Request Entity Too Large"       , 1 , 1 },
  { "414 Request-URI Too Large"          , 1 , 1 },
  { "417 Expectation Failed"             , 0 , 1 },
  { "500 Internal Server Error"          , 1 , 1 },
  { "501 Not Implemented"                , 1 , 1 },
  { "503 Service Unavailable"            , 1 , 1 }
#if 0
  { "203 Non-Authoritative Information"  , 0 , 0 },
  { "205 Reset Content"                  , 0 , 0 },
  { "300 Multiple Choices"               , 0 , 0 },
  { "301 Moved Permanently"              , 0 , 0 },
  { "302 Found"                          , 0 , 0 },
  { "303 See Other"                      , 0 , 0 },
  { "304 Not Modified"                   , 0 , 0 },
  { "305 Use Proxy"                      , 0 , 0 },
  { "307 Temporary Redirect"             , 0 , 0 },
  { "401 Unauthorized"                   , 1 , 1 },
  { "406 Not Acceptable"                 , 1 , 1 },
  { "407 Proxy Authentication Required"  , 1 , 1 },
  { "408 Request Time-out"               , 1 , 1 },
  { "409 Conflict"                       , 1 , 1 },
  { "410 Gone"                           , 1 , 1 },
  { "411 Length Required"                , 0 , 1 },
  { "412 Precondition Failed"            , 0 , 1 },
  { "415 Unsupported Media Type"         , 1 , 1 },
  { "416 Requested range not satisfiable", 0 , 1 },
  { "502 Bad Gateway"                    , 1 , 1 },
  { "504 Gateway Time-out"               , 1 , 1 },
  { "505 HTTP Version not supported"     , 1 , 1 },
#endif
};

enum {
  content_text = 0,
  content_html,
  content_binary,
  content_pdf,
  content_zip,
  content_jpg,
  content_gif,
  content_tif,
  content_png,
  content_unknown
};

static const char *contentTypeStrings_g[] =
{
  "text/plain",
  "text/html",
  "application/octet-stream",
  "application/pdf",
  "application/zip",
  "image/jpeg",
  "image/gif",
  "image/tiff",
  "image/png"
};

HTTPTYPEDEC_t contentDecodeTable[] =
{
  { "txt" , content_text },
  { "htm" , content_html },
  { "html", content_html },
  { "pdf" , content_pdf  },
  { "zip" , content_zip  },
  { "gz"  , content_zip  },
  { "bz2" , content_zip  },
  { "rar" , content_zip  },
  { "arj" , content_zip  },
  { "lha" , content_zip  },
  { "jpg" , content_jpg  },
  { "jpeg", content_jpg  },
  { "gif" , content_gif  },
  { "tif" , content_tif  },
  { "tiff", content_tif  },
  { "png" , content_png  },
  { "\0"  , 0 }
};



/*---------------------------------------------------------------------------
 *  FUNCTION IMPLEMENTATION
 *-------------------------------------------------------------------------*/

#if HTTP_SERVLETS


/* returns nonzero if the the character is in the reserved set
 */
static sint_t ht_isReservedChar(char c)
{
  const char *res = " ;/?:@&=+$,<>#%\"{}|\\^[]`\x7f";
  sint_t i;

  if ((((unsigned char) c) < 0x20) || (((unsigned char) c) >= 0x80))
    return 1;

  for (i = 0; res[i] != 0; i++)
  {
    if (c == res[i])
      return 1;
  }

  return 0;
}


/* escapes a string
 */
static void ht_doEscape(const char *inbuf, char *outbuf, sint_t bufsize)
{
  sint_t s, d;
  char c;

  s = 0;
  d = 0;
  do
  {
    c = inbuf[s];
    if (c == 0)
      break;
    if (ht_isReservedChar(c))
    {
      if ((d + 3) >= bufsize)
        break;
      sprintf(outbuf + d, "%%%02X", ((uint_t)c) & 0xFF);
      d += 3;
    }
    else
    {
      outbuf[d++] = c;
    }
    s++;
  }
  while (d < bufsize);
  outbuf[d] = 0;
}


/* insert the current session ID to a web formular
 */
sint_t hsl_formAddSessionId(SERVLET_t *slobj)
{
  HTTPCONN_t *conn = (HTTPCONN_t*) slobj;

  if (conn == NULL)
    return -1;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -