📄 httpd.c
字号:
/*
* 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 + -