From 489c0b453b4e44764516409b62cc73e2c6149b75 Mon Sep 17 00:00:00 2001 From: patacongo Date: Sat, 11 Jul 2009 23:40:17 +0000 Subject: Add framework for thttpd git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@1975 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/Makefile | 5 +- nuttx/netutils/Makefile | 14 +- nuttx/netutils/thttpd/Makefile | 84 ++++ nuttx/netutils/thttpd/fdwatch.h | 86 ++++ nuttx/netutils/thttpd/libhttpd.h | 339 +++++++++++++ nuttx/netutils/thttpd/mime_types.h | 275 +++++++++++ nuttx/netutils/thttpd/tdate_parse.h | 41 ++ nuttx/netutils/thttpd/thttpd.c | 947 ++++++++++++++++++++++++++++++++++++ nuttx/netutils/thttpd/timers.h | 144 ++++++ nuttx/netutils/thttpd/version.h | 50 ++ 10 files changed, 1982 insertions(+), 3 deletions(-) create mode 100644 nuttx/netutils/thttpd/Makefile create mode 100644 nuttx/netutils/thttpd/fdwatch.h create mode 100644 nuttx/netutils/thttpd/libhttpd.h create mode 100644 nuttx/netutils/thttpd/mime_types.h create mode 100644 nuttx/netutils/thttpd/tdate_parse.h create mode 100644 nuttx/netutils/thttpd/thttpd.c create mode 100644 nuttx/netutils/thttpd/timers.h create mode 100644 nuttx/netutils/thttpd/version.h (limited to 'nuttx') diff --git a/nuttx/Makefile b/nuttx/Makefile index fe917e2bc..c0b73406f 100644 --- a/nuttx/Makefile +++ b/nuttx/Makefile @@ -116,7 +116,7 @@ endif # Add libraries for network support ifeq ($(CONFIG_NET),y) -LINKLIBS += net/libnet$(LIBEXT) netutils/libnetutils$(LIBEXT) +LINKLIBS += net/libnet$(LIBEXT) netutils/libnetutils$(LIBEXT) netutils/libthttpd$(LIBEXT) endif # Add libraries for file system support @@ -226,6 +226,9 @@ net/libnet$(LIBEXT): context netutils/libnetutils$(LIBEXT): context @$(MAKE) -C netutils TOPDIR="$(TOPDIR)" libnetutils$(LIBEXT) +netutils/libthttpd$(LIBEXT): context + @$(MAKE) -C netutils TOPDIR="$(TOPDIR)" libthttpd$(LIBEXT) + fs/libfs$(LIBEXT): context @$(MAKE) -C fs TOPDIR="$(TOPDIR)" libfs$(LIBEXT) diff --git a/nuttx/netutils/Makefile b/nuttx/netutils/Makefile index 15344a454..a7a9ec2ac 100644 --- a/nuttx/netutils/Makefile +++ b/nuttx/netutils/Makefile @@ -72,7 +72,7 @@ BIN = libnetutils$(LIBEXT) VPATH = uiplib:dhcpc:dhcpd:resolv:smtp:telnetd:webclient:webserver:tftpc -all: $(BIN) +all: $(BIN) libthttpd$(LIBEXT) $(AOBJS): %$(OBJEXT): %.S $(call ASSEMBLE, $<, $@) @@ -85,6 +85,12 @@ $(BIN): $(OBJS) $(call ARCHIVE, $@, $${obj}); \ done ; ) +thttpd/libthttpd$(LIBEXT): + @$(MAKE) -C thttpd libthttpd$(LIBEXT) TOPDIR="$(TOPDIR)" + +libthttpd$(LIBEXT): thttpd/libthttpd$(LIBEXT) + @cp -a thttpd/libthttpd$(LIBEXT) . + .depend: Makefile $(SRCS) ifeq ($(CONFIG_NET),y) @$(MKDEP) --dep-path . --dep-path uiplib --dep-path dhcpc --dep-path dhcpd \ @@ -95,15 +101,19 @@ endif @touch $@ depend: .depend + @$(MAKE) -C thttpd depend TOPDIR="$(TOPDIR)" clean: - @rm -f $(BIN) *~ .*.swp + @rm -f $(BIN) libthttpd$(LIBEXT) *~ .*.swp $(call CLEAN) @( for dir in $(SUBDIRS); do \ rm -f $${dir}/*~ $${dir}/.*.swp; \ done ; ) + @$(MAKE) -C thttpd clean TOPDIR="$(TOPDIR)" distclean: clean @rm -f Make.dep .depend + @$(MAKE) -C thttpd distclean TOPDIR="$(TOPDIR)" -include Make.dep + diff --git a/nuttx/netutils/thttpd/Makefile b/nuttx/netutils/thttpd/Makefile new file mode 100644 index 000000000..37825c2cc --- /dev/null +++ b/nuttx/netutils/thttpd/Makefile @@ -0,0 +1,84 @@ +############################################################################# +# netutils/thttpd/Makefile +# +# Copyright (C) 2009 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt +# +# 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. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 +# COPYRIGHT OWNER 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. +# +############################################################################# + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs + +CGIBINDIR = $(TOPDIR)/netutils/thttpd/cgi-bin +SUBDIRS = cgi-src + +ASRCS = +AOBJS = $(ASRCS:.S=$(OBJEXT)) + +CSRCS = thttpd.c +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +BIN = libthttpd$(LIBEXT) + +all: $(BIN) + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +$(BIN): $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $@, $${obj}); \ + done ; ) + +.depend: Makefile $(SRCS) + @$(MKDEP) --dep-path . $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f $(BIN) *~ .*.swp + $(call CLEAN) + @( for dir in $(SUBDIRS); do \ + rm -f $${dir}/*~ $${dir}/.*.swp; \ + done ; ) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep + + diff --git a/nuttx/netutils/thttpd/fdwatch.h b/nuttx/netutils/thttpd/fdwatch.h new file mode 100644 index 000000000..76c8c02ae --- /dev/null +++ b/nuttx/netutils/thttpd/fdwatch.h @@ -0,0 +1,86 @@ +/**************************************************************************** + * netutils/thttpd/fdwatch.h + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file of the same name in THTTPD: + * + * Copyright © 1999 by Jef Poskanzer . + * 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 + ****************************************************************************/ + +#ifndef __NETUTILS_THTTPD_FDWATCH_H +#define __NETUTILS_THTTPD_FDWATCH_H + +#define FDW_READ 0 +#define FDW_WRITE 1 + +#ifndef INFTIM +# define INFTIM -1 +#endif + +/* Figure out how many file descriptors the system allows, and + * initialize the fdwatch data structures. Returns -1 on failure. + */ + +extern int fdwatch_get_nfiles(void); + +/* Add a descriptor to the watch list. rw is either FDW_READ or FDW_WRITE. */ + +extern void fdwatch_add_fd(int fd, void *client_data, int rw); + +/* Delete a descriptor from the watch list. */ + +extern void fdwatch_del_fd(int fd); + +/* Do the watch. Return value is the number of descriptors that are ready, + * or 0 if the timeout expired, or -1 on errors. A timeout of INFTIM means + * wait indefinitely. + */ + +extern int fdwatch(long timeout_msecs); + +/* Check if a descriptor was ready. */ + +extern int fdwatch_check_fd(int fd); + +/* Get the client data for the next returned event. Returns -1 when there + * are no more events. + */ + +extern void *fdwatch_get_next_client_data(void); + +/* Generate debugging statistics syslog message. */ + +extern void fdwatch_logstats(long secs); + +#endif /* __NETUTILS_THTTPD_FDWATCH_H */ + diff --git a/nuttx/netutils/thttpd/libhttpd.h b/nuttx/netutils/thttpd/libhttpd.h new file mode 100644 index 000000000..ddd1e2d69 --- /dev/null +++ b/nuttx/netutils/thttpd/libhttpd.h @@ -0,0 +1,339 @@ +/**************************************************************************** + * netutils/thttpd/libhttpd.h + * HTTP Protocol Library Definitions + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file of the same name in the original THTTPD package: + * + * Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . + * 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. + * + ****************************************************************************/ + +#ifndef __NETUTILS_THTTPD_LIBHTTPD_H +#define __NETUTILS_THTTPD_LIBHTTPD_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_THTTPD_PORT +# define CONFIG_THTTPD_PORT 80 +#endif + +#ifndef CONFIG_THTTPD_CHARSET +# define CONFIG_THTTPD_CHARSET "iso-8859-1" +#endif + +#ifndef CONFIG_THTTPD_IOBUFFERSIZE +# define CONFIG_THTTPD_IOBUFFERSIZE 256 +#endif + +#if CONFIG_THTTPD_IOBUFFERSIZE > 65535 +# error "Can't use uint16 for buffer" +#endif + +/* A few convenient defines. */ + +#ifndef MAX +# define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#ifndef MIN +# define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define NEW(t,n) ((t*) malloc( sizeof(t) * (n) )) +#define RENEW(o,t,n) ((t*) realloc( (void*) o, sizeof(t) * (n) )) + +/* Methods. */ + +#define METHOD_UNKNOWN 0 +#define METHOD_GET 1 +#define METHOD_HEAD 2 +#define METHOD_POST 3 + +/* States for checked_state. */ + +#define CHST_FIRSTWORD 0 +#define CHST_FIRSTWS 1 +#define CHST_SECONDWORD 2 +#define CHST_SECONDWS 3 +#define CHST_THIRDWORD 4 +#define CHST_THIRDWS 5 +#define CHST_LINE 6 +#define CHST_LF 7 +#define CHST_CR 8 +#define CHST_CRLF 9 +#define CHST_CRLFCR 10 +#define CHST_BOGUS 11 + +#define GC_FAIL 0 +#define GC_OK 1 +#define GC_NO_MORE 2 + +#define GR_NO_REQUEST 0 +#define GR_GOT_REQUEST 1 +#define GR_BAD_REQUEST 2 + +/**************************************************************************** + * Public Type Definitions + ****************************************************************************/ + +/* A multi-family sockaddr. */ + +typedef union +{ + struct sockaddr sa; +#ifdef CONFIG_NET_IPv6 + struct sockaddr_in6 sa_in6; +#else + struct sockaddr_in sa_in; +#endif /* CONFIG_NET_IPv6 */ +} httpd_sockaddr; + +/* A server. */ + +typedef struct +{ + char *binding_hostname; + char *server_hostname; + int cgi_count; + char *cwd; + int listen_fd; +} httpd_server; + +/* A connection. */ + +typedef struct +{ + int initialized; + httpd_server *hs; + httpd_sockaddr client_addr; + char *read_buf; + size_t read_size, read_idx, checked_idx; + int checked_state; + int method; + int status; + off_t bytes_to_send; + off_t bytes_sent; + char *encodedurl; + char *decodedurl; + char *protocol; + char *origfilename; + char *expnfilename; + char *encodings; + char *pathinfo; + char *query; + char *referer; + char *useragent; + char *accept; + char *accepte; + char *acceptl; + char *cookie; + char *contenttype; + char *reqhost; + char *hdrhost; + char *hostdir; + char *authorization; + char *remoteuser; + size_t maxdecodedurl, maxorigfilename, maxexpnfilename, maxencodings, + maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost, maxhostdir, + maxremoteuser, maxresponse; +#ifdef TILDE_MAP_2 + char *altdir; + size_t maxaltdir; +#endif /* TILDE_MAP_2 */ + time_t if_modified_since, range_if; + size_t contentlength; + char *type; /* not malloc()ed */ +#ifdef CONFIG_THTTPD_VHOST + char *vhostname; /* not malloc()ed */ +#endif + boolean mime_flag; + boolean one_one; /* HTTP/1.1 or better */ + boolean got_range; + boolean tildemapped; /* this connection got tilde-mapped */ + boolean keep_alive; + boolean should_linger; + int conn_fd; /* Connection to the client */ + int file_fd; /* Descriptor for open, outgoing file */ + off_t range_start; /* File range start from Range= */ + off_t range_end; /* File range end from Range= */ + struct stat sb; + + /* This is the I/O buffer that is used to buffer portions of outgoing files */ + + uint16 buflen; /* Index to first valid data in buffer */ + ubyte buffer[CONFIG_THTTPD_IOBUFFERSIZE]; +} httpd_conn; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initializes. Does the socket(), bind(), and listen(). Returns an + * httpd_server* which includes a socket fd that you can select() on. + * Return (httpd_server*) 0 on error. + */ + +#ifdef CONFIG_NET_IPv6 +extern httpd_server *httpd_initialize(struct sockaddr_in6 *sa, char *cwd); +#else +extern httpd_server *httpd_initialize(struct sockaddr_in *sa, char *cwd); +#endif + +/* Call to unlisten/close socket(s) listening for new connections. */ + +extern void httpd_unlisten(httpd_server * hs); + +/* Call to shut down. */ + +extern void httpd_terminate(httpd_server * hs); + +/* When a listen fd is ready to read, call this. It does the accept() and + * returns an httpd_conn* which includes the fd to read the request from and + * write the response to. Returns an indication of whether the accept() + * failed, succeeded, or if there were no more connections to accept. + * + * In order to minimize malloc()s, the caller passes in the httpd_conn. + * The caller is also responsible for setting initialized to zero before the + * first call using each different httpd_conn. + */ + +extern int httpd_get_conn(httpd_server * hs, int listen_fd, httpd_conn * hc); + +/* Checks whether the data in hc->read_buf constitutes a complete request + * yet. The caller reads data into hc->read_buf[hc->read_idx] and advances + * hc->read_idx. This routine checks what has been read so far, using + * hc->checked_idx and hc->checked_state to keep track, and returns an + * indication of whether there is no complete request yet, there is a + * complete request, or there won't be a valid request due to a syntax error. + */ + +extern int httpd_got_request(httpd_conn * hc); + +/* Parses the request in hc->read_buf. Fills in lots of fields in hc, + * like the URL and the various headers. + * + * Returns -1 on error. + */ + +extern int httpd_parse_request(httpd_conn * hc); + +/* Starts sending data back to the client. In some cases (directories, + * CGI programs), finishes sending by itself - in those cases, hc->file_fd + * is negative. If there is more data to be sent, then hc->file_fd is a file + * stream for the file to send. If you don't have a current timeval + * handy just pass in 0. + * + * Returns -1 on error. + */ + +extern int httpd_start_request(httpd_conn * hc, struct timeval *nowP); + +/* Actually sends any buffered response text. */ + +extern void httpd_write_response(httpd_conn * hc); + +/* Call this to close down a connection and free the data. A fine point, + * if you fork() with a connection open you should still call this in the + * parent process - the connection will stay open in the child. + * If you don't have a current timeval handy just pass in 0. + */ + +extern void httpd_close_conn(httpd_conn * hc, struct timeval *nowP); + +/* Call this to de-initialize a connection struct and *really* free the + * mallocced strings. + */ + +extern void httpd_destroy_conn(httpd_conn * hc); + +/* Send an error message back to the client. */ + +extern void httpd_send_err(httpd_conn * hc, int status, char *title, + char *extraheads, char *form, char *arg); + +/* Some error messages. */ + +extern char *httpd_err400title; +extern char *httpd_err400form; +extern char *httpd_err408title; +extern char *httpd_err408form; +extern char *httpd_err503title; +extern char *httpd_err503form; + +/* Generate a string representation of a method number. */ + +extern char *httpd_method_str(int method); + +/* Reallocate a string. */ + +extern void httpd_realloc_str(char **strP, size_t * maxsizeP, size_t size); + +/* Format a network socket to a string representation. */ + +extern char *httpd_ntoa(httpd_sockaddr * saP); + +/* Set NDELAY mode on a socket. */ + +extern void httpd_set_ndelay(int fd); + +/* Clear NDELAY mode on a socket. */ + +extern void httpd_clear_ndelay(int fd); + +/* Generate debugging statistics syslog message. */ + +#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) +extern void httpd_logstats(long secs); +#else +# define httpd_logstats(secs) +#endif + +/* Read to requested buffer, accounting for interruptions and EOF */ + +extern int httpd_read(int fd, const void *buf, size_t nbytes); + +/* Write the buffer completely, accounting for interruptions */ + +extern int httpd_write(int fd, const void *buf, size_t nbytes); + +#endif /* __NETUTILS_THTTPD_LIBHTTPD_H */ + diff --git a/nuttx/netutils/thttpd/mime_types.h b/nuttx/netutils/thttpd/mime_types.h new file mode 100644 index 000000000..ef2106af9 --- /dev/null +++ b/nuttx/netutils/thttpd/mime_types.h @@ -0,0 +1,275 @@ +/**************************************************************************** + * netutils/thttpd/mime_types.h + * Provides mappings between filename extensions and MIME types and encodings. + * + * Based on mime_encodings.txt and mime_types.txt by Jef Poskanser which + * contained no copyright information. + * + * Copyright (C) 2007-2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +#ifndef __NETUTILS_THTTPD_MIME_TYPES_H +#define __NETUTILS_THTTPD_MIME_TYPES_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct mime_entry +{ + char *ext; + size_t ext_len; + char *val; + size_t val_len; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* A list of file extensions followed by the corresponding MIME encoding. + * Extensions not found in the table proceed to the mime_types table. + */ + +static struct mime_entry enc_tab[] = +{ + { "Z", 0, "compress", 0 }, + { "gz", 0, "gzip", 0 }, + { "uu", 0, "x-uuencode", 0 }, +}; +static const int n_enc_tab = sizeof(enc_tab) / sizeof(*enc_tab); + +/* A list of file extensions followed by the corresponding MIME type. + * Extensions not found in the table are returned as text/plain. + */ + +static struct mime_entry typ_tab[] = +{ + { "a", 0, "application/octet-stream", 0 }, + { "aab", 0, "application/x-authorware-bin", 0 }, + { "aam", 0, "application/x-authorware-map", 0 }, + { "aas", 0, "application/x-authorware-seg", 0 }, + { "ai", 0, "application/postscript", 0 }, + { "aif", 0, "audio/x-aiff", 0 }, + { "aifc", 0, "audio/x-aiff", 0 }, + { "aiff", 0, "audio/x-aiff", 0 }, + { "asc", 0, "text/plain", 0 }, + { "asf", 0, "video/x-ms-asf", 0 }, + { "asx", 0, "video/x-ms-asf", 0 }, + { "au", 0, "audio/basic", 0 }, + { "avi", 0, "video/x-msvideo", 0 }, + { "bcpio", 0, "application/x-bcpio", 0 }, + { "bin", 0, "application/octet-stream", 0 }, + { "bmp", 0, "image/bmp", 0 }, + { "cdf", 0, "application/x-netcdf", 0 }, + { "class", 0, "application/x-java-vm", 0 }, + { "cpio", 0, "application/x-cpio", 0 }, + { "cpt", 0, "application/mac-compactpro", 0 }, + { "crl", 0, "application/x-pkcs7-crl", 0 }, + { "crt", 0, "application/x-x509-ca-cert", 0 }, + { "csh", 0, "application/x-csh", 0 }, + { "css", 0, "text/css", 0 }, + { "dcr", 0, "application/x-director", 0 }, + { "dir", 0, "application/x-director", 0 }, + { "djv", 0, "image/vnd.djvu", 0 }, + { "djvu", 0, "image/vnd.djvu", 0 }, + { "dll", 0, "application/octet-stream", 0 }, + { "dms", 0, "application/octet-stream", 0 }, + { "doc", 0, "application/msword", 0 }, + { "dtd", 0, "text/xml", 0 }, + { "dump", 0, "application/octet-stream", 0 }, + { "dvi", 0, "application/x-dvi", 0 }, + { "dxr", 0, "application/x-director", 0 }, + { "eps", 0, "application/postscript", 0 }, + { "etx", 0, "text/x-setext", 0 }, + { "exe", 0, "application/octet-stream", 0 }, + { "ez", 0, "application/andrew-inset", 0 }, + { "fgd", 0, "application/x-director", 0 }, + { "fh", 0, "image/x-freehand", 0 }, + { "fh4", 0, "image/x-freehand", 0 }, + { "fh5", 0, "image/x-freehand", 0 }, + { "fh7", 0, "image/x-freehand", 0 }, + { "fhc", 0, "image/x-freehand", 0 }, + { "gif", 0, "image/gif", 0 }, + { "gtar", 0, "application/x-gtar", 0 }, + { "hdf", 0, "application/x-hdf", 0 }, + { "hqx", 0, "application/mac-binhex40", 0 }, + { "htm", 0, "text/html; charset=%s", 0 }, + { "html", 0, "text/html; charset=%s", 0 }, + { "ice", 0, "x-conference/x-cooltalk", 0 }, + { "ief", 0, "image/ief", 0 }, + { "iges", 0, "model/iges", 0 }, + { "igs", 0, "model/iges", 0 }, + { "iv", 0, "application/x-inventor", 0 }, + { "jar", 0, "application/x-java-archive", 0 }, + { "jfif", 0, "image/jpeg", 0 }, + { "jpe", 0, "image/jpeg", 0 }, + { "jpeg", 0, "image/jpeg", 0 }, + { "jpg", 0, "image/jpeg", 0 }, + { "js", 0, "application/x-javascript", 0 }, + { "kar", 0, "audio/midi", 0 }, + { "latex", 0, "application/x-latex", 0 }, + { "lha", 0, "application/octet-stream", 0 }, + { "lzh", 0, "application/octet-stream", 0 }, + { "m3u", 0, "audio/x-mpegurl", 0 }, + { "man", 0, "application/x-troff-man", 0 }, + { "mathml", 0, "application/mathml+xml", 0 }, + { "me", 0, "application/x-troff-me", 0 }, + { "mesh", 0, "model/mesh", 0 }, + { "mid", 0, "audio/midi", 0 }, + { "midi", 0, "audio/midi", 0 }, + { "mif", 0, "application/vnd.mif", 0 }, + { "mime", 0, "message/rfc822", 0 }, + { "mml", 0, "application/mathml+xml", 0 }, + { "mov", 0, "video/quicktime", 0 }, + { "movie", 0, "video/x-sgi-movie", 0 }, + { "mp2", 0, "audio/mpeg", 0 }, + { "mp3", 0, "audio/mpeg", 0 }, + { "mp4", 0, "video/mp4", 0 }, + { "mpe", 0, "video/mpeg", 0 }, + { "mpeg", 0, "video/mpeg", 0 }, + { "mpg", 0, "video/mpeg", 0 }, + { "mpga", 0, "audio/mpeg", 0 }, + { "ms", 0, "application/x-troff-ms", 0 }, + { "msh", 0, "model/mesh", 0 }, + { "mv", 0, "video/x-sgi-movie", 0 }, + { "mxu", 0, "video/vnd.mpegurl", 0 }, + { "nc", 0, "application/x-netcdf", 0 }, + { "o", 0, "application/octet-stream", 0 }, + { "oda", 0, "application/oda", 0 }, + { "ogg", 0, "application/x-ogg", 0 }, + { "pac", 0, "application/x-ns-proxy-autoconfig", 0 }, + { "pbm", 0, "image/x-portable-bitmap", 0 }, + { "pdb", 0, "chemical/x-pdb", 0 }, + { "pdf", 0, "application/pdf", 0 }, + { "pgm", 0, "image/x-portable-graymap", 0 }, + { "pgn", 0, "application/x-chess-pgn", 0 }, + { "png", 0, "image/png", 0 }, + { "pnm", 0, "image/x-portable-anymap", 0 }, + { "ppm", 0, "image/x-portable-pixmap", 0 }, + { "ppt", 0, "application/vnd.ms-powerpoint", 0 }, + { "ps", 0, "application/postscript", 0 }, + { "qt", 0, "video/quicktime", 0 }, + { "ra", 0, "audio/x-realaudio", 0 }, + { "ram", 0, "audio/x-pn-realaudio", 0 }, + { "ras", 0, "image/x-cmu-raster", 0 }, + { "rdf", 0, "application/rdf+xml", 0 }, + { "rgb", 0, "image/x-rgb", 0 }, + { "rm", 0, "audio/x-pn-realaudio", 0 }, + { "roff", 0, "application/x-troff", 0 }, + { "rpm", 0, "audio/x-pn-realaudio-plugin", 0 }, + { "rss", 0, "application/rss+xml", 0 }, + { "rtf", 0, "text/rtf", 0 }, + { "rtx", 0, "text/richtext", 0 }, + { "sgm", 0, "text/sgml", 0 }, + { "sgml", 0, "text/sgml", 0 }, + { "sh", 0, "application/x-sh", 0 }, + { "shar", 0, "application/x-shar", 0 }, + { "silo", 0, "model/mesh", 0 }, + { "sit", 0, "application/x-stuffit", 0 }, + { "skd", 0, "application/x-koan", 0 }, + { "skm", 0, "application/x-koan", 0 }, + { "skp", 0, "application/x-koan", 0 }, + { "skt", 0, "application/x-koan", 0 }, + { "smi", 0, "application/smil", 0 }, + { "smil", 0, "application/smil", 0 }, + { "snd", 0, "audio/basic", 0 }, + { "so", 0, "application/octet-stream", 0 }, + { "spl", 0, "application/x-futuresplash", 0 }, + { "src", 0, "application/x-wais-source", 0 }, + { "stc", 0, "application/vnd.sun.xml.calc.template", 0 }, + { "std", 0, "application/vnd.sun.xml.draw.template", 0 }, + { "sti", 0, "application/vnd.sun.xml.impress.template", 0 }, + { "stw", 0, "application/vnd.sun.xml.writer.template", 0 }, + { "sv4cpio", 0, "application/x-sv4cpio", 0 }, + { "sv4crc", 0, "application/x-sv4crc", 0 }, + { "svg", 0, "image/svg+xml", 0 }, + { "svgz", 0, "image/svg+xml", 0 }, + { "swf", 0, "application/x-shockwave-flash", 0 }, + { "sxc", 0, "application/vnd.sun.xml.calc", 0 }, + { "sxd", 0, "application/vnd.sun.xml.draw", 0 }, + { "sxg", 0, "application/vnd.sun.xml.writer.global", 0 }, + { "sxi", 0, "application/vnd.sun.xml.impress", 0 }, + { "sxm", 0, "application/vnd.sun.xml.math", 0 }, + { "sxw", 0, "application/vnd.sun.xml.writer", 0 }, + { "t", 0, "application/x-troff", 0 }, + { "tar", 0, "application/x-tar", 0 }, + { "tcl", 0, "application/x-tcl", 0 }, + { "tex", 0, "application/x-tex", 0 }, + { "texi", 0, "application/x-texinfo", 0 }, + { "texinfo", 0, "application/x-texinfo", 0 }, + { "tif", 0, "image/tiff", 0 }, + { "tiff", 0, "image/tiff", 0 }, + { "tr", 0, "application/x-troff", 0 }, + { "tsp", 0, "application/dsptype", 0 }, + { "tsv", 0, "text/tab-separated-values", 0 }, + { "txt", 0, "text/plain; charset=%s", 0 }, + { "ustar", 0, "application/x-ustar", 0 }, + { "vcd", 0, "application/x-cdlink", 0 }, + { "vrml", 0, "model/vrml", 0 }, + { "vx", 0, "video/x-rad-screenplay", 0 }, + { "wav", 0, "audio/x-wav", 0 }, + { "wax", 0, "audio/x-ms-wax", 0 }, + { "wbmp", 0, "image/vnd.wap.wbmp", 0 }, + { "wbxml", 0, "application/vnd.wap.wbxml", 0 }, + { "wm", 0, "video/x-ms-wm", 0 }, + { "wma", 0, "audio/x-ms-wma", 0 }, + { "wmd", 0, "application/x-ms-wmd", 0 }, + { "wml", 0, "text/vnd.wap.wml", 0 }, + { "wmlc", 0, "application/vnd.wap.wmlc", 0 }, + { "wmls", 0, "text/vnd.wap.wmlscript", 0 }, + { "wmlsc", 0, "application/vnd.wap.wmlscriptc", 0 }, + { "wmv", 0, "video/x-ms-wmv", 0 }, + { "wmx", 0, "video/x-ms-wmx", 0 }, + { "wmz", 0, "application/x-ms-wmz", 0 }, + { "wrl", 0, "model/vrml", 0 }, + { "wsrc", 0, "application/x-wais-source", 0 }, + { "wvx", 0, "video/x-ms-wvx", 0 }, + { "xbm", 0, "image/x-xbitmap", 0 }, + { "xht", 0, "application/xhtml+xml", 0 }, + { "xhtml", 0, "application/xhtml+xml", 0 }, + { "xls", 0, "application/vnd.ms-excel", 0 }, + { "xml", 0, "text/xml", 0 }, + { "xpm", 0, "image/x-xpixmap", 0 }, + { "xsl", 0, "text/xml", 0 }, + { "xwd", 0, "image/x-xwindowdump", 0 }, + { "xyz", 0, "chemical/x-xyz", 0 }, + { "zip", 0, "application/zip", 0 }, +}; +static const int n_typ_tab = sizeof(typ_tab) / sizeof(*typ_tab); + +#endif /* __NETUTILS_THTTPD_MIME_TYPES_H */ + diff --git a/nuttx/netutils/thttpd/tdate_parse.h b/nuttx/netutils/thttpd/tdate_parse.h new file mode 100644 index 000000000..301d5467a --- /dev/null +++ b/nuttx/netutils/thttpd/tdate_parse.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * netutils/thttpd/fdwatch.h + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file of the same name in THTTPD: + * + * Copyright © 1995 by Jef Poskanzer . + * 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. + * + ****************************************************************************/ + +#ifndef __NETUTILS_TDATE_PARSE_H +#define __NETUTILS_TDATE_PARSE_H + +extern time_t tdate_parse(char *str); + +#endif /* __NETUTILS_TDATE_PARSE_H */ diff --git a/nuttx/netutils/thttpd/thttpd.c b/nuttx/netutils/thttpd/thttpd.c new file mode 100644 index 000000000..2d45b611a --- /dev/null +++ b/nuttx/netutils/thttpd/thttpd.c @@ -0,0 +1,947 @@ +/**************************************************************************** + * netutils/thttpd/thttpd.c + * Tiny HTTP Server + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file of the same name in the original THTTPD package: + * + * Copyright © 1995,1998,1999,2000,2001 by Jef Poskanzer . + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "version.h" +#include "fdwatch.h" +#include "libhttpd.h" +#include "timers.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef CONFIG_THTTPD_IPADDR +# warning "CONFIG_THTTPD_IPADDR not defined" +# define CONFIG_THTTPD_IPADDR (10<<24|0<<16|0<<8|2) +#endif + +#ifndef CONFIG_THTTPD_LINGER_MSEC +# define CONFIG_THTTPD_LINGER_MSEC 5000 +#endif + +#ifndef CONFIG_THTTPD_OCCASIONAL_MSEC +# define CONFIG_THTTPD_OCCASIONAL_MSEC 2000 +#endif + +#ifndef CONFIG_THTTPD_IDLE_READ_LIMIT_SEC +# define CONFIG_THTTPD_IDLE_READ_LIMIT_SEC 5 +#endif + +#ifndef CONFIG_THTTPD_IDLE_SEND_LIMIT_SEC +# define CONFIG_THTTPD_IDLE_SEND_LIMIT_SEC 5 +#endif + +#ifndef MAXPATHLEN +# define MAXPATHLEN 64 +#endif + +/* The connection states */ + +#define CNST_FREE 0 +#define CNST_READING 1 +#define CNST_SENDING 2 +#define CNST_PAUSING 3 +#define CNST_LINGERING 4 + +/**************************************************************************** + * 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 max_connects; +static int first_free_connect; +static int httpd_conn_count; + +/**************************************************************************** + * 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, (struct timezone *)0); + logstats(&tv); + for (cnum = 0; cnum < max_connects; ++cnum) + { + if (connects[cnum].conn_state != CNST_FREE) + { + httpd_close_conn(connects[cnum].hc, &tv); + } + + if (connects[cnum].hc != (httpd_conn *) 0) + { + httpd_destroy_conn(connects[cnum].hc); + free((void *)connects[cnum].hc); + --httpd_conn_count; + connects[cnum].hc = (httpd_conn *) 0; + } + } + + if (hs != (httpd_server *) 0) + { + httpd_server *ths = hs; + hs = (httpd_server *) 0; + if (ths->listen_fd != -1) + { + fdwatch_del_fd(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. + */ + + for (;;) + { + /* Is there room in the connection table? */ + if (num_connects >= max_connects) + { + /* 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 0; + } + + /* Get the first free connection entry off the free list */ + + if (first_free_connect == -1 || + connects[first_free_connect].conn_state != CNST_FREE) + { + ndbg("the connects free list is messed up\n"); + exit(1); + } + + conn = &connects[first_free_connect]; + + /* Make the httpd_conn if necessary */ + + if (conn->hc == (httpd_conn *) 0) + { + conn->hc = NEW(httpd_conn, 1); + if (conn->hc == (httpd_conn *) 0) + { + 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 0; + + /* No more connections to accept for now */ + + case GC_NO_MORE: + return 1; + } + + 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 = (Timer *) 0; + conn->linger_timer = (Timer *) 0; + conn->offset = 0; + + /* Set the connection file descriptor to no-delay mode */ + + httpd_set_ndelay(conn->hc->conn_fd); + + fdwatch_add_fd(conn->hc->conn_fd, conn, FDW_READ); + +#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 > 5000) + { + goto errout_with_400; + } + httpd_realloc_str(&hc->read_buf, &hc->read_size, hc->read_size + 1000); + } + + /* Read some more bytes */ + + sz = read(hc->conn_fd, &(hc->read_buf[hc->read_idx]), hc->read_size - hc->read_idx); + if (sz == 0) + { + goto errout_with_400; + } + + if (sz < 0) + { + /* Ignore EINTR and EAGAIN. Also ignore EWOULDBLOCK. At first glanc + * 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; + } + 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: + 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); + 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(hc->conn_fd); + fdwatch_add_fd(hc->conn_fd, conn, FDW_WRITE); + return; + +errout_with_400: + 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; + + /* 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; + } + + /* Send the buffer */ + + if (hc->buflen > 0) + { + 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 += nread; + conn->hc->bytes_sent += nread; + } + + /* Are we done? */ + + if (conn->offset >= conn->end_offset) + { + /* This connection is finished! */ + + finish_connection(conn, tv); + return; + } + +errout_clear_connection: + 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 != (Timer *) 0) + { + 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 = (Timer *) 0; + conn->hc->should_linger = FALSE; + } + + if (conn->hc->should_linger) + { + if (conn->conn_state != CNST_PAUSING) + { + fdwatch_del_fd(conn->hc->conn_fd); + } + + conn->conn_state = CNST_LINGERING; + close(conn->hc->conn_fd); + fdwatch_add_fd(conn->hc->conn_fd, conn, FDW_READ); + client_data.p = conn; + + if (conn->linger_timer != (Timer *) 0) + { + ndbg("replacing non-null linger_timer!\n"); + } + + conn->linger_timer = + tmr_create(tv, linger_clear_connection, client_data, CONFIG_THTTPD_LINGER_MSEC, 0); + if (conn->linger_timer == (Timer *) 0) + { + ndbg("tmr_create(linger_clear_connection) failed\n"); + exit(1); + } + } + else + { + 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 + if (conn->conn_state != CNST_PAUSING) + { + fdwatch_del_fd(conn->hc->conn_fd); + } + + httpd_close_conn(conn->hc, tv); + if (conn->linger_timer != (Timer *) 0) + { + 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 < max_connects; ++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: + case CNST_PAUSING: + 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; + + conn = (struct connect_s *) client_data.p; + conn->linger_timer = (Timer *) 0; + 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 == (struct timeval *)0) + { + (void)gettimeofday(&tv, (struct timezone *)0); + 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(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 + ****************************************************************************/ + +int thttpd_main(int argc, char **argv) +{ + char cwd[MAXPATHLEN + 1]; + int num_ready; + int cnum; + struct connect_s *conn; + httpd_conn *hc; +#ifdef CONFIG_NET_IPv6 + struct sockaddr_in6 sa; +#else + struct sockaddr_in sa; +#endif + struct timeval tv; + + /* 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 + + /* Switch directories if requested */ + +#ifdef CONFIG_THTTPD_DIR + if (chdir(CONFIG_THTTPD_DIR) < 0) + { + ndbg("chdir: %d\n", errno); + exit(1); + } +#endif + + /* Get current directory */ + + (void)getcwd(cwd, sizeof(cwd) - 1); + if (cwd[strlen(cwd) - 1] != '/') + { + (void)strcat(cwd, "/"); + } + + /* Initialize the fdwatch package */ + + max_connects = fdwatch_get_nfiles(); + if (max_connects < 0) + { + 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 */ + + hs = httpd_initialize(&sa, cwd); + if (!hs) + { + exit(1); + } + + /* Set up the occasional timer */ + + if (tmr_create + ((struct timeval *)0, occasional, JunkClientData, CONFIG_THTTPD_OCCASIONAL_MSEC * 1000L, + 1) == (Timer *) 0) + { + ndbg("tmr_create(occasional) failed\n"); + exit(1); + } + + /* Set up the idle timer */ + + if (tmr_create((struct timeval *)0, idle, JunkClientData, 5 * 1000L, 1) == + (Timer *) 0) + { + 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, max_connects); + if (connects == (struct connect_s *) 0) + { + ndbg("out of memory allocating a struct connect_s\n"); + exit(1); + } + + for (cnum = 0; cnum < max_connects; ++cnum) + { + connects[cnum].conn_state = CNST_FREE; + connects[cnum].next_free_connect = cnum + 1; + connects[cnum].hc = (httpd_conn *) 0; + } + + connects[max_connects - 1].next_free_connect = -1; /* end of link list */ + first_free_connect = 0; + num_connects = 0; + httpd_conn_count = 0; + + if (hs != (httpd_server *) 0) + { + if (hs->listen_fd != -1) + fdwatch_add_fd(hs->listen_fd, (void *)0, FDW_READ); + } + + /* Main loop */ + + (void)gettimeofday(&tv, (struct timezone *)0); + while ((!terminate) || num_connects > 0) + { + /* Do the fd watch */ + + num_ready = fdwatch(tmr_mstimeout(&tv)); + if (num_ready < 0) + { + if (errno == EINTR || errno == EAGAIN) + continue; /* try again */ + ndbg("fdwatch: %d\n", errno); + exit(1); + } + + (void)gettimeofday(&tv, (struct timezone *)0); + + if (num_ready == 0) + { + /* No fd's are ready - run the timers */ + + tmr_run(&tv); + continue; + } + + /* Is it a new connection? */ + + if (hs != (httpd_server *) 0 && hs->listen_fd != -1 && + fdwatch_check_fd(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()) != + (struct connect_s *) - 1) + { + if (conn == (struct connect_s *) 0) + continue; + + hc = conn->hc; + if (!fdwatch_check_fd(hc->conn_fd)) + { + /* Something went wrong */ + + clear_connection(conn, &tv); + } + else + { + switch (conn->conn_state) + { + case CNST_READING: + handle_read(conn, &tv); + break; + case CNST_SENDING: + handle_send(conn, &tv); + break; + case CNST_LINGERING: + handle_linger(conn, &tv); + break; + } + } + } + tmr_run(&tv); + } + + /* The main loop terminated */ + + shut_down(); + ndbg("Exiting\n"); + exit(0); +} + + diff --git a/nuttx/netutils/thttpd/timers.h b/nuttx/netutils/thttpd/timers.h new file mode 100644 index 000000000..e2f3ab57a --- /dev/null +++ b/nuttx/netutils/thttpd/timers.h @@ -0,0 +1,144 @@ +/**************************************************************************** + * netutils/thttpd/timers.h + * Header file for THTTPD timers package + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Derived from the file of the same name in THTTPD: + * + * Copyright © 1995,1998,1999,2000 by Jef Poskanzer . + * 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. + * + ****************************************************************************/ + +#ifndef __NETUTILS_THTTPD_TIMERS_H +#define __NETUTILS_THTTPD_TIMERS_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#ifndef INFTIM +# define INFTIM -1 +#endif + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* ClientData is a random value that tags along with a timer. The client + * can use it for whatever, and it gets passed to the callback when the + * timer triggers. + */ + +typedef union +{ + void *p; + int i; + long l; +} ClientData; + +/* The TimerProc gets called when the timer expires. It gets passed + * the ClientData associated with the timer, and a timeval in case + * it wants to schedule another timer. + */ + +typedef void TimerProc(ClientData client_data, struct timeval *nowP); + +/* The Timer struct. */ + +typedef struct TimerStruct +{ + TimerProc *timer_proc; + ClientData client_data; + long msecs; + int periodic; + struct timeval time; + struct TimerStruct *prev; + struct TimerStruct *next; + int hash; +} Timer; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern ClientData JunkClientData; /* For use when you don't care */ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Initialize the timer package. */ + +extern void tmr_init(void); + +/* Set up a timer, either periodic or one-shot. Returns (Timer*) 0 on errors. */ + +extern Timer *tmr_create(struct timeval *nowP, TimerProc * timer_proc, + ClientData client_data, long msecs, int periodic); + +/* Returns a timeout in milliseconds indicating how long until the next timer + * triggers. You can just put the call to this routine right in your poll(). + * Returns INFTIM (-1) if no timers are pending. + */ + +extern long tmr_mstimeout(struct timeval *nowP); + +/* Run the list of timers. Your main program needs to call this every so often. */ + +extern void tmr_run(struct timeval *nowP); + +/* Deschedule a timer. Note that non-periodic timers are automatically + * descheduled when they run, so you don't have to call this on them. + */ + +extern void tmr_cancel(Timer *timer); + +/* Clean up the timers package, freeing any unused storage. */ + +extern void tmr_cleanup(void); + +/* Cancel all timers and free storage, usually in preparation for exitting. */ + +extern void tmr_destroy(void); + +/* Generate debugging statistics syslog message. */ + +#if defined(CONFIG_DEBUG) && defined(CONFIG_DEBUG_NET) +extern void tmr_logstats(long secs); +#else +# define tmr_logstats(secs) +#endif + +#endif /* __NETUTILS_THTTPD_TIMERS_H */ + diff --git a/nuttx/netutils/thttpd/version.h b/nuttx/netutils/thttpd/version.h new file mode 100644 index 000000000..d3e56eaeb --- /dev/null +++ b/nuttx/netutils/thttpd/version.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * netutils/thttpd/version.h + * Version definitions for THTTPD + * + * Based on version.h by Jef Poskanser which contained no copyright information. + * + * Copyright (C) 2009 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + * + ****************************************************************************/ + +#ifndef __NETUTILS_THTTPD_VERSION_H +#define __NETUTILS_THTTPD_VERSION_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SERVER_SOFTWARE "thttpd/2.25b 29dec2003-NuttX" +#define SERVER_ADDRESS "http://www.nuttx.org" + +#endif /* __NETUTILS_THTTPD_VERSION_H */ + -- cgit v1.2.3