📄 httpd.c
字号:
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
*
*/
/* This file is modified from the original version (httpd.c) as shipped with
* lwIP version 1.3.0. Changes have been made to allow support for a
* rudimentary server-side-include facility which will replace tags of the form
* <!--#tag--> in any file whose extension is .shtml, .shtm or .ssi with
* strings provided by an include handler whose pointer is provided to the
* module via function http_set_ssi_handler(). Additionally, a simple common
* gateway interface (CGI) handling mechanism has been added to allow clients
* to hook functions to particular request URIs.
*
* To enable SSI support, define label INCLUDE_HTTPD_SSI in lwipopts.h.
* To enable CGI support, define label INCLUDE_HTTPD_CGI in lwipopts.h.
*
* By default, the server assumes that HTTP headers are already present in
* each file stored in the file system. By defining DYNAMIC_HTTP_HEADERS in
* lwipopts.h, this behavior can be changed such that the server inserts the
* headers automatically based on the extension of the file being served. If
* this mode is used, be careful to ensure that the file system image used
* does not already contain the header information.
*
* File system images without headers can be created using the makefsfile
* tool with the -h command line option.
*/
/*
* Notes about valid SSI tags
* --------------------------
*
* The following assumptions are made about tags used in SSI markers:
*
* 1. No tag may contain '-' or whitespace characters within the tag name.
* 2. Whitespace is allowed between the tag leadin "<!--#" and the start of
* the tag name and between the tag name and the leadout string "-->".
* 3. The maximum tag name length is MAX_TAG_NAME_LEN, currently 8 characters.
*
* Notes on CGI usage
* ------------------
*
* The simple CGI support offered here works with GET method requests only
* and can handle up to 16 parameters encoded into the URI. The handler
* function may not write directly to the HTTP output but must return a
* filename that the HTTP server will send to the browser as a response to
* the incoming CGI request.
*
*/
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "httpd.h"
#include "lwip/tcp.h"
#include "fs.h"
#include <string.h>
#include "ustdlib.h"
#ifdef INCLUDE_HTTPD_DEBUG
#include "hw_types.h"
#include "utils/uartstdio.h"
#define DEBUG_PRINT UARTprintf
#else
#define DEBUG_PRINT while(0)((int (*)(char *, ...))0)
#endif
#ifndef true
#define true ((u8_t)1)
#endif
#ifndef false
#define false ((u8_t)0)
#endif
typedef struct
{
const char *name;
u8_t shtml;
} default_filename;
const default_filename g_psDefaultFilenames[] = {
{"/index.shtml", true },
{"/index.ssi", true },
{"/index.shtm", true },
{"/index.html", false },
{"/index.htm", false }
};
#define NUM_DEFAULT_FILENAMES (sizeof(g_psDefaultFilenames) / \
sizeof(default_filename))
#ifdef DYNAMIC_HTTP_HEADERS
/* The number of individual strings that comprise the headers sent before each
* requested file.
*/
#define NUM_FILE_HDR_STRINGS 3
#endif
#ifdef INCLUDE_HTTPD_SSI
const char *g_pcSSIExtensions[] = {
".shtml", ".shtm", ".ssi"
};
#define NUM_SHTML_EXTENSIONS (sizeof(g_pcSSIExtensions) / sizeof(const char *))
enum tag_check_state {
TAG_NONE, /* Not processing an SSI tag */
TAG_LEADIN, /* Tag lead in "<!--#" being processed */
TAG_FOUND, /* Tag name being read, looking for lead-out start */
TAG_LEADOUT, /* Tag lead out "-->" being processed */
TAG_SENDING /* Sending tag replacement string */
};
#endif /* INCLUDE_HTTPD_SSI */
struct http_state {
struct fs_file *handle;
char *file; /* Pointer to first unsent byte in buf. */
char *buf; /* File read buffer. */
#ifdef INCLUDE_HTTPD_SSI
char *parsed; /* Pointer to the first unparsed byte in buf. */
char *tag_end; /* Pointer to char after the closing '>' of the tag. */
u32_t parse_left; /* Number of unparsed bytes in buf. */
#endif
u32_t left; /* Number of unsent bytes in buf. */
int buf_len; /* Size of file read buffer, buf. */
u8_t retries;
#ifdef INCLUDE_HTTPD_SSI
u8_t tag_check; /* true if we are processing a .shtml file else false */
u8_t tag_index; /* Counter used by tag parsing state machine */
u8_t tag_insert_len; /* Length of insert in string tag_insert */
u8_t tag_name_len; /* Length of the tag name in string tag_name */
char tag_name[MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
char tag_insert[MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
enum tag_check_state tag_state; /* State of the tag processor */
#endif
#ifdef INCLUDE_HTTPD_CGI
char *params[MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
char *param_vals[MAX_CGI_PARAMETERS]; /* Values for each extracted param */
#endif
#ifdef DYNAMIC_HTTP_HEADERS
const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
u16_t hdr_pos; /* The position of the first unsent header byte in the
current string */
u16_t hdr_index; /* The index of the hdr string currently being sent. */
#endif
};
#ifdef INCLUDE_HTTPD_SSI
/* SSI insert handler function pointer. */
tSSIHandler g_pfnSSIHandler = NULL;
int g_iNumTags = 0;
const char **g_ppcTags = NULL;
#define LEN_TAG_LEAD_IN 5
const char * const g_pcTagLeadIn = "<!--#";
#define LEN_TAG_LEAD_OUT 3
const char * const g_pcTagLeadOut = "-->";
#endif /* INCLUDE_HTTPD_SSI */
#ifdef INCLUDE_HTTPD_CGI
/* CGI handler information */
const tCGI *g_pCGIs = NULL;
int g_iNumCGIs = 0;
#endif /* INCLUDE_HTTPD_CGI */
#ifdef DYNAMIC_HTTP_HEADERS
//*****************************************************************************
//
// HTTP header strings for various filename extensions.
//
//*****************************************************************************
typedef struct
{
const char *pszExtension;
unsigned long ulHeaderIndex;
} tHTTPHeader;
const char *g_psHTTPHeaderStrings[] =
{
"Content-type: text/html\r\n\r\n",
"Content-type: text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\n" \
"Pragma: no-cache\r\n\r\n",
"Content-type: image/gif\r\n\r\n",
"Content-type: image/png\r\n\r\n",
"Content-type: image/jpeg\r\n\r\n",
"Content-type: image/bmp\r\n\r\n",
"Content-type: application/octet-stream\r\n\r\n",
"Content-type: application/x-javascript\r\n\r\n",
"Content-type: audio/x-pn-realaudio\r\n\r\n",
"Content-type: text/css\r\n\r\n",
"Content-type: text/plain\r\n\r\n",
"HTTP/1.0 200 OK\r\n",
"HTTP/1.0 404 File not found\r\n",
"Server: lwIP/1.3.0 (http://www.sics.se/~adam/lwip/)\r\n",
"\r\n<html><body><h2>404: The requested file cannot be found." \
"</h2></body></html>\r\n"
};
#define HTTP_HDR_HTML 0
#define HTTP_HDR_SSI 1
#define HTTP_HDR_GIF 2
#define HTTP_HDR_JPG 3
#define HTTP_HDR_PNG 4
#define HTTP_HDR_BMP 5
#define HTTP_HDR_APP 6
#define HTTP_HDR_JS 7
#define HTTP_HDR_RA 8
#define HTTP_HDR_CSS 9
#define HTTP_HDR_DEFAULT_TYPE 10
#define HTTP_HDR_OK 11
#define HTTP_HDR_NOT_FOUND 12
#define HTTP_HDR_SERVER 13
#define DEFAULT_404_HTML 14
tHTTPHeader g_psHTTPHeaders[] =
{
{ "html", HTTP_HDR_HTML},
{ "htm", HTTP_HDR_HTML},
{ "shtml",HTTP_HDR_SSI},
{ "shtm", HTTP_HDR_SSI},
{ "ssi", HTTP_HDR_SSI},
{ "gif", HTTP_HDR_GIF},
{ "png", HTTP_HDR_PNG},
{ "jpg", HTTP_HDR_JPG},
{ "bmp", HTTP_HDR_BMP},
{ "class",HTTP_HDR_APP},
{ "cls", HTTP_HDR_APP},
{ "js", HTTP_HDR_JS},
{ "ram", HTTP_HDR_RA},
{ "css", HTTP_HDR_CSS}
};
#define NUM_HTTP_HEADERS (sizeof(g_psHTTPHeaders) / sizeof(tHTTPHeader))
#endif
/*-----------------------------------------------------------------------------------*/
static void
conn_err(void *arg, err_t err)
{
struct http_state *hs;
LWIP_UNUSED_ARG(err);
if(arg)
{
hs = arg;
if(hs->handle) {
fs_close(hs->handle);
hs->handle = NULL;
}
if(hs->buf)
{
mem_free(hs->buf);
}
mem_free(hs);
}
}
/*-----------------------------------------------------------------------------------*/
static void
close_conn(struct tcp_pcb *pcb, struct http_state *hs)
{
err_t err;
DEBUG_PRINT("Closing connection 0x%08x\n", pcb);
tcp_arg(pcb, NULL);
tcp_sent(pcb, NULL);
tcp_recv(pcb, NULL);
if(hs->handle) {
fs_close(hs->handle);
hs->handle = NULL;
}
if(hs->buf)
{
mem_free(hs->buf);
}
mem_free(hs);
err = tcp_close(pcb);
if(err != ERR_OK)
{
DEBUG_PRINT("Error %d closing 0x%08x\n", err, pcb);
}
}
/*-----------------------------------------------------------------------------------*/
#ifdef INCLUDE_HTTPD_CGI
static int
extract_uri_parameters(struct http_state *hs, char *params)
{
char *pair;
char *equals;
int loop;
/* If we have no parameters at all, return immediately. */
if(!params || (params[0] == '\0')) {
return(0);
}
/* Get a pointer to our first parameter */
pair = params;
/*
* Parse up to MAX_CGI_PARAMETERS from the passed string and ignore the
* remainder (if any)
*/
for(loop = 0; (loop < MAX_CGI_PARAMETERS) && pair; loop++) {
/* Save the name of the parameter */
hs->params[loop] = pair;
/* Remember the start of this name=value pair */
equals = pair;
/* Find the start of the next name=value pair and replace the delimiter
* with a 0 to terminate the previous pair string.
*/
pair = strchr(pair, '&');
if(pair) {
*pair = '\0';
pair++;
} else {
/* We didn't find a new parameter so find the end of the URI and
* replace the space with a '\0'
*/
pair = strchr(equals, ' ');
if(pair) {
*pair = '\0';
}
/* Revert to NULL so that we exit the loop as expected. */
pair = NULL;
}
/* Now find the '=' in the previous pair, replace it with '\0' and save
* the parameter value string.
*/
equals = strchr(equals, '=');
if(equals) {
*equals = '\0';
hs->param_vals[loop] = equals + 1;
} else {
hs->param_vals[loop] = NULL;
}
}
return(loop);
}
#endif /* INCLUDE_HTTPD_CGI */
/*-----------------------------------------------------------------------------------*/
#ifdef INCLUDE_HTTPD_SSI
static void
get_tag_insert(struct http_state *hs)
{
int loop;
if(g_pfnSSIHandler && g_ppcTags && g_iNumTags) {
/* Find this tag in the list we have been provided. */
for(loop = 0; loop < g_iNumTags; loop++)
{
if(strcmp(hs->tag_name, g_ppcTags[loop]) == 0) {
hs->tag_insert_len = g_pfnSSIHandler(loop, hs->tag_insert,
MAX_TAG_INSERT_LEN);
return;
}
}
}
/* If we drop out, we were asked to serve a page which contains tags that
* we don't have a handler for. Merely echo back the tags with an error
* marker.
*/
usnprintf(hs->tag_insert, MAX_TAG_INSERT_LEN + 1,
"<b>***UNKNOWN TAG %s***</b>", hs->tag_name);
hs->tag_insert_len = strlen(hs->tag_insert);
}
#endif /* INCLUDE_HTTPD_SSI */
#ifdef DYNAMIC_HTTP_HEADERS
//*****************************************************************************
//
// Generate the relevant HTTP headers for the given filename and write
// them into the supplied buffer. Returns true on success or false on failure.
//
//*****************************************************************************
static void
get_http_headers(struct http_state *pState, char *pszURI)
{
int iLoop;
char *pszWork;
char *pszExt;
char *pszVars;
//
// Ensure that we initialize the loop counter.
//
iLoop = 0;
//
//
// In all cases, the second header we send is the server identification
// so set it here.
//
pState->hdrs[1] = g_psHTTPHeaderStrings[HTTP_HDR_SERVER];
// Is this a normal file or the special case we use to send back the
// default "404: Page not found" response?
//
if(pszURI == NULL)
{
pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
pState->hdrs[2] = g_psHTTPHeaderStrings[DEFAULT_404_HTML];
//
// Set up to send the first header string.
//
pState->hdr_index = 0;
pState->hdr_pos = 0;
return;
}
else
{
//
// We are dealing with a particular filename. Look for one other
// special case. We assume that any filename with "404" in it must be
// indicative of a 404 server error whereas all other files require
// the 200 OK header.
//
if(ustrstr(pszURI, "404") || pszURI == NULL)
{
pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_NOT_FOUND];
}
else
{
pState->hdrs[0] = g_psHTTPHeaderStrings[HTTP_HDR_OK];
}
//
// Determine if the URI has any variables and, if so, temporarily remove
// them.
//
pszVars = strchr(pszURI, '?');
if(pszVars)
{
*pszVars = '\0';
}
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -