(third-party) upgrade shttpd from 1.39 to the latest, 1.41

This commit is contained in:
Charles Kerr 2008-07-16 23:55:49 +00:00
parent f89a5e23d5
commit 2281c39c1f
15 changed files with 1004 additions and 812 deletions

View File

@ -328,7 +328,9 @@ startServer( tr_rpc_server * server )
if( !server->ctx )
{
char ports[128];
int i;
int argc = 0;
char * argv[100];
char passwd[MAX_PATH_LENGTH];
const char * clutchDir = tr_getClutchDir( server->session );
struct timeval tv = tr_timevalMsec( INACTIVE_INTERVAL_MSEC );
@ -339,36 +341,41 @@ startServer( tr_rpc_server * server )
else
edit_passwords( passwd, MY_REALM, server->username, server->password );
server->ctx = shttpd_init( );
tr_snprintf( ports, sizeof( ports ), "%d", server->port );
argv[argc++] = tr_strdup( "appname-unused" );
argv[argc++] = tr_strdup( "-ports" );
argv[argc++] = tr_strdup_printf( "%d", server->port );
argv[argc++] = tr_strdup( "-dir_list" );
argv[argc++] = tr_strdup( "0" );
argv[argc++] = tr_strdup( "-auth_realm" );
argv[argc++] = tr_strdup( MY_REALM );
if( server->acl )
{
argv[argc++] = tr_strdup( "-acl" );
argv[argc++] = tr_strdup( server->acl );
}
if( server->isPasswordEnabled )
{
argv[argc++] = tr_strdup( "-protect" );
argv[argc++] = tr_strdup_printf( "/transmission=%s", passwd );
}
if( clutchDir && *clutchDir )
{
tr_inf( _( "Serving the web interface files from \"%s\"" ), clutchDir );
argv[argc++] = tr_strdup( "-aliases" );
argv[argc++] = tr_strdup_printf( "%s=%s,%s=%s",
"/transmission/clutch", clutchDir,
"/transmission/web", clutchDir );
}
server->ctx = shttpd_init( argc, argv );
shttpd_register_uri( server->ctx, "/transmission/rpc", handle_rpc, server );
shttpd_register_uri( server->ctx, "/transmission/upload", handle_upload, server );
if( clutchDir && *clutchDir ) {
char * clutchAlias = tr_strdup_printf( "%s=%s,%s=%s",
"/transmission/clutch", clutchDir,
"/transmission/web", clutchDir );
tr_inf( _( "Serving the web interface files from \"%s\"" ), clutchDir );
shttpd_set_option( server->ctx, "aliases", clutchAlias );
tr_free( clutchAlias );
}
shttpd_set_option( server->ctx, "ports", ports );
shttpd_set_option( server->ctx, "dir_list", "0" );
//shttpd_set_option( server->ctx, "root", "/dev/null" );
shttpd_set_option( server->ctx, "auth_realm", MY_REALM );
if( server->acl ) {
dbgmsg( "setting acl [%s]", server->acl );
shttpd_set_option( server->ctx, "acl", server->acl );
}
if( server->isPasswordEnabled ) {
char * buf = tr_strdup_printf( "/transmission=%s", passwd );
shttpd_set_option( server->ctx, "protect", buf );
tr_free( buf );
}
evtimer_set( &server->timer, rpcPulse, server );
evtimer_add( &server->timer, &tv );
for( i=0; i<argc; ++i )
tr_free( argv[i] );
}
}
@ -429,7 +436,7 @@ tr_rpcGetPort( const tr_rpc_server * server )
****/
/*
* DELIM_CHARS, FOR_EACH_WORD_IN_LIST, isbyte, and testACL are from, or modified from,
* FOR_EACH_WORD_IN_LIST, isbyte, and testACL are from, or modified from,
* shttpd, written by Sergey Lyubka under this license:
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
@ -437,8 +444,6 @@ tr_rpcGetPort( const tr_rpc_server * server )
* this stuff is worth it, you can buy me a beer in return.
*/
#define DELIM_CHARS "," /* Separators for lists */
#define FOR_EACH_WORD_IN_LIST(s,len) \
for (; s != NULL && (len = strcspn(s, DELIM_CHARS)) != 0; \
s += len, s+= strspn(s, DELIM_CHARS))

View File

@ -4,7 +4,7 @@ noinst_LIBRARIES = libshttpd.a
AM_CPPFLAGS = -DEMBEDDED -DNDEBUG -DNO_CGI -DNO_SSI
libshttpd_a_SOURCES = \
string.c shttpd.c log.c auth.c md5.c cgi.c config.c \
string.c shttpd.c log.c auth.c md5.c \
io_file.c io_socket.c io_ssl.c io_emb.c io_dir.c io_cgi.c \
compat_unix.c

View File

@ -1,3 +1,3 @@
http://shttpd.sourceforge.net/
Sergey Lyubka wrote this software.
This snapshot si from shttpd-1.39
This snapshot is from shttpd-1.41

View File

@ -281,12 +281,12 @@ check_authorization(struct conn *c, const char *path)
if (!memcmp(c->uri, s, p - s)) {
n = s + len - p + 1;
n = s + len - p;
if (n > (int) sizeof(protected_path) - 1)
n = sizeof(protected_path) - 1;
my_strlcpy(protected_path, p + 1, n);
if ((fp = fopen(protected_path, "r")) == NULL)
elog(E_LOG, c, "check_auth: cannot open %s: %s",
protected_path, strerror(errno));

View File

@ -18,58 +18,6 @@ struct env_block {
int nvars; /* Number of variables */
};
/*
* UNIX socketpair() implementation. Why? Because Windows does not have it.
* Return 0 on success, -1 on error.
*/
static int
my_socketpair(struct conn *c, int sp[2])
{
struct sockaddr_in sa;
int sock, ret = -1;
socklen_t len = sizeof(sa);
(void) memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(0);
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
} else if (bind(sock, (struct sockaddr *) &sa, len) != 0) {
elog(E_LOG, c, "mysocketpair: bind(): %d", ERRNO);
(void) closesocket(sock);
} else if (listen(sock, 1) != 0) {
elog(E_LOG, c, "mysocketpair: listen(): %d", ERRNO);
(void) closesocket(sock);
} else if (getsockname(sock, (struct sockaddr *) &sa, &len) != 0) {
elog(E_LOG, c, "mysocketpair: getsockname(): %d", ERRNO);
(void) closesocket(sock);
} else if ((sp[0] = socket(AF_INET, SOCK_STREAM, 6)) == -1) {
elog(E_LOG, c, "mysocketpair: socket(): %d", ERRNO);
(void) closesocket(sock);
} else if (connect(sp[0], (struct sockaddr *) &sa, len) != 0) {
elog(E_LOG, c, "mysocketpair: connect(): %d", ERRNO);
(void) closesocket(sock);
(void) closesocket(sp[0]);
} else if ((sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) == -1) {
elog(E_LOG, c, "mysocketpair: accept(): %d", ERRNO);
(void) closesocket(sock);
(void) closesocket(sp[0]);
} else {
/* Success */
ret = 0;
(void) closesocket(sock);
}
#ifndef _WIN32
(void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
(void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
#endif /* _WIN32*/
return (ret);
}
static void
addenv(struct env_block *block, const char *fmt, ...)
{
@ -145,11 +93,16 @@ prepare_environment(const struct conn *c, const char *prog,
struct env_block *blk)
{
const struct headers *h = &c->ch;
const char *s, *root = c->ctx->options[OPT_ROOT];
const char *s, *fname, *root = c->ctx->options[OPT_ROOT];
size_t len;
blk->len = blk->nvars = 0;
/* SCRIPT_FILENAME */
fname = prog;
if ((s = strrchr(prog, '/')))
fname = s + 1;
/* Prepare the environment block */
addenv(blk, "%s", "GATEWAY_INTERFACE=CGI/1.1");
addenv(blk, "%s", "SERVER_PROTOCOL=HTTP/1.1");
@ -163,7 +116,7 @@ prepare_environment(const struct conn *c, const char *prog,
addenv(blk, "REMOTE_PORT=%hu", ntohs(c->sa.u.sin.sin_port));
addenv(blk, "REQUEST_URI=%s", c->uri);
addenv(blk, "SCRIPT_NAME=%s", prog + strlen(root));
addenv(blk, "SCRIPT_FILENAME=%s", prog); /* PHP */
addenv(blk, "SCRIPT_FILENAME=%s", fname); /* PHP */
addenv(blk, "PATH_TRANSLATED=%s", prog);
if (h->ct.v_vec.len > 0)
@ -244,7 +197,7 @@ run_cgi(struct conn *c, const char *prog)
break;
}
if (my_socketpair(c, pair) != 0) {
if (shttpd_socketpair(pair) != 0) {
ret = -1;
} else if (spawn_process(c, prog, blk.buf, blk.vars, pair[1], dir)) {
ret = -1;

View File

@ -20,14 +20,16 @@
#include <unistd.h>
#include <dirent.h>
#include <dlfcn.h>
#if !defined(NO_THREADS)
#include "pthread.h"
#define _beginthread(a, b, c) do { pthread_t tid; \
pthread_create(&tid, NULL, (void *(*)(void *))a, c); } while (0)
#endif /* !NO_THREADS */
#define SSL_LIB "libssl.so"
#define DIRSEP '/'
#define IS_DIRSEP_CHAR(c) ((c) == '/')
#define O_BINARY 0
#define closesocket(a) close(a)
#define ERRNO errno
#define NO_GUI
#define InitializeCriticalSection(x) /* FIXME UNIX version is not MT safe */
#define EnterCriticalSection(x)
#define LeaveCriticalSection(x)

View File

@ -1,309 +0,0 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*/
#include "defs.h"
static int isbyte(int n) { return (n >= 0 && n <= 255); }
static void
set_acl(struct shttpd_ctx *ctx, const char *s)
{
struct acl *acl = NULL;
char flag;
int len, a, b, c, d, n, mask;
struct llhead *lp, *tmp;
/* Delete the old ACLs if any */
LL_FOREACH_SAFE(&ctx->acl, lp, tmp)
free(LL_ENTRY(lp, struct acl, link));
FOR_EACH_WORD_IN_LIST(s, len) {
mask = 32;
if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
elog(E_FATAL, NULL, "[%s]: subnet must be "
"[+|-]x.x.x.x[/x]", s);
} else if (flag != '+' && flag != '-') {
elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
} else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
elog(E_FATAL, NULL, "bad ip address: [%s]", s);
} else if ((acl = malloc(sizeof(*acl))) == NULL) {
elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
} else if (sscanf(s + n, "/%d", &mask) == 0) {
/* Do nothing, no mask specified */
} else if (mask < 0 || mask > 32) {
elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
}
acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
acl->flag = flag;
LL_TAIL(&ctx->acl, &acl->link);
}
}
#ifndef NO_SSL
/*
* Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
*/
static void
set_ssl(struct shttpd_ctx *ctx, const char *pem)
{
SSL_CTX *CTX;
void *lib;
struct ssl_func *fp;
/* Load SSL library dynamically */
if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL)
elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB);
for (fp = ssl_sw; fp->name != NULL; fp++)
if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL)
elog(E_FATAL, NULL,"set_ssl: cannot find %s", fp->name);
/* Initialize SSL crap */
SSL_library_init();
if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
elog(E_FATAL, NULL, "SSL_CTX_new error");
else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
elog(E_FATAL, NULL, "cannot open %s", pem);
else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
elog(E_FATAL, NULL, "cannot open %s", pem);
ctx->ssl_ctx = CTX;
}
#endif /* NO_SSL */
static void
open_log_file(FILE **fpp, const char *path)
{
if (*fpp != NULL)
(void) fclose(*fpp);
if (path == NULL) {
*fpp = NULL;
} else if ((*fpp = fopen(path, "a")) == NULL) {
elog(E_FATAL, NULL, "cannot open log file %s: %s",
path, strerror(errno));
}
}
static void
set_alog(struct shttpd_ctx *ctx, const char *path)
{
open_log_file(&ctx->access_log, path);
}
static void
set_elog(struct shttpd_ctx *ctx, const char *path)
{
open_log_file(&ctx->error_log, path);
}
static void show_cfg_page(struct shttpd_arg *arg);
static void
set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
{
free_list(&ctx->registered_uris, &registered_uri_destructor);
if (uri != NULL) {
shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
}
}
static void
set_ports(struct shttpd_ctx *ctx, const char *p)
{
int len, is_ssl;
free_list(&ctx->listeners, &listener_destructor);
FOR_EACH_WORD_IN_LIST(p, len) {
is_ssl = p[len - 1] == 's' ? 1 : 0;
if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
elog(E_FATAL, NULL,
"Cannot open socket on port %d", atoi(p));
}
}
static const struct opt {
int index; /* Index in shttpd_ctx */
const char *name; /* Option name in config file */
const char *description; /* Description */
const char *default_value; /* Default option value */
void (*setter)(struct shttpd_ctx *, const char *);
} known_options[] = {
{OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
{OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
{OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
{OPT_DIR_LIST, "dir_list", "Directory listing", "1", NULL},
{OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
{OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
#ifndef NO_CGI
{OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
{OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
{OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
#endif /* NO_CGI */
{OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
#ifndef NO_AUTH
{OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
{OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
{OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
#endif /* !NO_AUTH */
{OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
{OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
{OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
#ifndef NO_SSL
{OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
#endif /* NO_SSL */
{OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
{OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
#ifdef _WIN32
#else
{OPT_INETD, "inetd", "Inetd mode", "0", NULL},
{OPT_UID, "uid", "\tRun as user", NULL, NULL},
#endif /* _WIN32 */
{-1, NULL, NULL, NULL, NULL}
};
void shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
{
const struct opt *o;
for (o = known_options; o->name != NULL; o++)
if (!strcmp(opt, o->name))
break;
if (o->name == NULL)
elog(E_FATAL, NULL, "no such option: [%s]", opt);
/* Call option setter first, so it can use both new and old values */
if (o->setter != NULL)
o->setter(ctx, val);
/* Free old value if any */
if (ctx->options[o->index] != NULL)
free(ctx->options[o->index]);
/* Set new option value */
ctx->options[o->index] = val ? my_strdup(val) : NULL;
}
static void
show_cfg_page(struct shttpd_arg *arg)
{
struct shttpd_ctx *ctx = arg->user_data;
char opt_name[20], value[BUFSIZ];
const struct opt *o;
opt_name[0] = value[0] = '\0';
if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
if (arg->flags & SHTTPD_MORE_POST_DATA)
return;
(void) shttpd_get_var("o", arg->in.buf, arg->in.len,
opt_name, sizeof(opt_name));
(void) shttpd_get_var("v", arg->in.buf, arg->in.len,
value, sizeof(value));
shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
}
shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
"<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
shttpd_printf(arg, "%s", "<table border=1"
"<tr><th>Option</th><th>Description</th>"
"<th colspan=2>Value</th></tr>");
if (opt_name[0] != '\0' && value[0] != '\0')
shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
opt_name, value[0] ? value : "NULL");
for (o = known_options; o->name != NULL; o++) {
shttpd_printf(arg,
"<form method=post><tr><td>%s</td><td>%s</td>"
"<input type=hidden name=o value='%s'>"
"<td><input type=text name=v value='%s'></td>"
"<td><input type=submit value=save></td></form></tr>",
o->name, o->description, o->name,
ctx->options[o->index] ? ctx->options[o->index] : "");
}
shttpd_printf(arg, "%s", "</table></body></html>");
arg->flags |= SHTTPD_END_OF_OUTPUT;
}
/*
* Show usage string and exit.
*/
void
usage(const char *prog)
{
const struct opt *o;
(void) fprintf(stderr,
"SHTTPD version %s (c) Sergey Lyubka\n"
"usage: %s [options] [config_file]\n", VERSION, prog);
#if !defined(NO_AUTH)
fprintf(stderr, " -A <htpasswd_file> <realm> <user> <passwd>\n");
#endif /* NO_AUTH */
for (o = known_options; o->name != NULL; o++) {
(void) fprintf(stderr, " -%s\t%s", o->name, o->description);
if (o->default_value != NULL)
fprintf(stderr, " (default: %s)", o->default_value);
fputc('\n', stderr);
}
exit(EXIT_FAILURE);
}
struct shttpd_ctx *shttpd_init(void)
{
struct shttpd_ctx *ctx;
struct tm *tm;
const struct opt *o;
if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
elog(E_FATAL, NULL, "cannot allocate shttpd context");
/* Set default values */
for (o = known_options; o->name != NULL; o++) {
ctx->options[o->index] = o->default_value == NULL ?
NULL : my_strdup(o->default_value);
}
current_time = ctx->start_time = time(NULL);
tm = localtime(&current_time);
tz_offset = 0;
#if 0
tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
#endif
InitializeCriticalSection(&ctx->mutex);
LL_INIT(&ctx->connections);
LL_INIT(&ctx->registered_uris);
LL_INIT(&ctx->error_handlers);
LL_INIT(&ctx->acl);
#if !defined(NO_SSI)
LL_INIT(&ctx->ssi_funcs);
#endif
LL_INIT(&ctx->listeners);
#ifdef _WIN32
{WSADATA data; WSAStartup(MAKEWORD(2,2), &data);}
#endif /* _WIN32 */
return (ctx);
}

View File

@ -11,8 +11,8 @@
#ifndef CONFIG_HEADER_DEFINED
#define CONFIG_HEADER_DEFINED
#undef VERSION
#define VERSION "1.39" /* Version */
#undef VERSION
#define VERSION "1.41" /* Version */
#define CONFIG_FILE "shttpd.conf" /* Configuration file */
#define HTPASSWD ".htpasswd" /* Passwords file name */
#define URI_MAX 16384 /* Default max request size */
@ -25,5 +25,6 @@
#define EXPIRE_TIME 3600 /* Expiration time, seconds */
#define ENV_MAX 4096 /* Size of environment block */
#define CGI_ENV_VARS 64 /* Maximum vars passed to CGI */
#define SERVICE_NAME "SHTTPD " VERSION /* NT service name */
#endif /* CONFIG_HEADER_DEFINED */

View File

@ -14,9 +14,9 @@
#include "std_includes.h"
#include "llist.h"
#include "io.h"
#include "shttpd.h"
#include "md5.h"
#include "config.h"
#include "shttpd.h"
#define NELEMS(ar) (sizeof(ar) / sizeof(ar[0]))
@ -26,10 +26,6 @@
#define DBG(x)
#endif /* DEBUG */
#ifdef EMBEDDED
#include "shttpd.h"
#endif /* EMBEDDED */
/*
* Darwin prior to 7.0 and Win32 do not have socklen_t
*/
@ -45,9 +41,10 @@ struct vec {
int len;
};
#if !defined(_WIN32)
#if !defined(FALSE)
enum {FALSE, TRUE};
#endif /* _WIN32 */
#endif /* !FALSE */
enum {METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD};
enum {HDR_DATE, HDR_INT, HDR_STRING}; /* HTTP header types */
enum {E_FATAL = 1, E_LOG = 2}; /* Flags for elog() function */
@ -175,7 +172,6 @@ struct stream {
union channel chan; /* Descriptor */
struct io io; /* IO buffer */
const struct io_class *io_class; /* IO class */
int nread_last; /* Bytes last read */
int headers_len;
big_int_t content_len;
unsigned int flags;
@ -186,10 +182,21 @@ struct stream {
#define FLAG_CLOSED 16
#define FLAG_DONT_CLOSE 32
#define FLAG_ALWAYS_READY 64 /* File, dir, user_func */
#define FLAG_SUSPEND 128
};
struct worker {
struct llhead link;
int num_conns; /* Num of active connections */
int exit_flag; /* Ditto - exit flag */
int ctl[2]; /* Control socket pair */
struct shttpd_ctx *ctx; /* Context reference */
struct llhead connections; /* List of connections */
};
struct conn {
struct llhead link; /* Connections chain */
struct worker *worker; /* Worker this conn belongs to */
struct shttpd_ctx *ctx; /* Context this conn belongs to */
struct usa sa; /* Remote socket address */
time_t birth_time; /* Creation time */
@ -223,7 +230,7 @@ enum {
OPT_SSI_EXTENSIONS, OPT_AUTH_REALM, OPT_AUTH_GPASSWD,
OPT_AUTH_PUT, OPT_ACCESS_LOG, OPT_ERROR_LOG, OPT_MIME_TYPES,
OPT_SSL_CERTIFICATE, OPT_ALIASES, OPT_ACL, OPT_INETD, OPT_UID,
OPT_CFG_URI, OPT_PROTECT,
OPT_CFG_URI, OPT_PROTECT, OPT_SERVICE, OPT_HIDE, OPT_THREADS,
NUM_OPTIONS
};
@ -231,35 +238,33 @@ enum {
* SHTTPD context
*/
struct shttpd_ctx {
time_t start_time; /* Start time */
int nactive; /* # of connections now */
unsigned long nrequests; /* Requests made */
uint64_t in, out; /* IN/OUT traffic counters */
SSL_CTX *ssl_ctx; /* SSL context */
struct llhead connections; /* List of connections */
struct llhead registered_uris;/* User urls */
struct llhead error_handlers; /* Embedded error handlers */
struct llhead acl; /* Access control list */
#if !defined(NO_SSI)
struct llhead ssi_funcs; /* SSI callback functions */
#endif
struct llhead listeners; /* Listening sockets */
struct llhead workers; /* Worker workers */
FILE *access_log; /* Access log stream */
FILE *error_log; /* Error log stream */
FILE *access_log; /* Access log stream */
FILE *error_log; /* Error log stream */
char *options[NUM_OPTIONS]; /* Configurable options */
#if defined(_WIN32)
CRITICAL_SECTION mutex; /* For MT case */
HANDLE ev[2]; /* For thread synchronization */
#elif defined(__rtems__)
#if defined(__rtems__)
rtems_id mutex;
#endif /* _WIN32 */
};
#define IS_TRUE(ctx, opt) ((ctx)->options[opt] && (ctx)->options[opt][0] =='1')
struct listener {
struct llhead link;
struct shttpd_ctx *ctx; /* Context that socket belongs */
int sock; /* Listening socket */
int is_ssl; /* Should be SSL-ed */
};
/* Types of messages that could be sent over the control socket */
enum {CTL_PASS_SOCKET, CTL_WAKEUP};
/*
* In SHTTPD, list of values are represented as comma or space separated
@ -300,11 +305,11 @@ extern int url_decode(const char *, int, char *dst, int);
extern void send_server_error(struct conn *, int code, const char *reason);
extern int get_headers_len(const char *buf, size_t buflen);
extern void parse_headers(const char *s, int len, struct headers *parsed);
extern void open_listening_ports(struct shttpd_ctx *ctx);
extern int is_true(const char *str);
extern int shttpd_socketpair(int pair[2]);
extern void get_mime_type(struct shttpd_ctx *, const char *, int, struct vec *);
extern void free_list(struct llhead *head, void (*)(struct llhead *));
extern void registered_uri_destructor(struct llhead *);
extern void listener_destructor(struct llhead *);
#define IS_TRUE(ctx, opt) is_true((ctx)->options[opt])
/*
* config.c
@ -342,6 +347,10 @@ extern char * my_getcwd(char *, int);
extern int spawn_process(struct conn *c, const char *prog,
char *envblk, char *envp[], int sock, const char *dir);
extern void set_nt_service(struct shttpd_ctx *, const char *);
extern void set_systray(struct shttpd_ctx *, const char *);
extern void try_to_run_as_nt_service(void);
/*
* io_*.c
*/
@ -360,10 +369,8 @@ extern void ssl_handshake(struct stream *stream);
extern void setup_embedded_stream(struct conn *, union variant, void *);
extern struct registered_uri *is_registered_uri(struct shttpd_ctx *,
const char *uri);
#if !defined(NO_SSI)
extern void do_ssi(struct conn *);
extern void ssi_func_destructor(struct llhead *lp);
#endif
/*
* auth.c

View File

@ -52,6 +52,9 @@ call_user(struct conn *c, struct shttpd_arg *arg, shttpd_callback_t func)
c->loc.flags &= ~FLAG_DONT_CLOSE;
else
c->loc.flags |= FLAG_DONT_CLOSE;
if (arg->flags & SHTTPD_SUSPEND)
c->loc.flags |= FLAG_SUSPEND;
}
static int
@ -90,14 +93,10 @@ close_embedded(struct stream *stream)
size_t
shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
{
struct conn *c = arg->priv;
struct io *io = &c->loc.io;
char *buf = arg->out.buf + arg->out.num_bytes;
int buflen = arg->out.len - arg->out.num_bytes, len = 0;
va_list ap;
assert(buf <= io->buf + io->size);
if (buflen > 0) {
va_start(ap, fmt);
len = vsnprintf(buf, buflen, fmt, ap);
@ -267,6 +266,22 @@ shttpd_handle_error(struct shttpd_ctx *ctx, int code,
}
}
void
shttpd_wakeup(const void *priv)
{
const struct conn *conn = priv;
char buf[sizeof(int) + sizeof(void *)];
int cmd = CTL_WAKEUP;
#if 0
conn->flags &= ~SHTTPD_SUSPEND;
#endif
(void) memcpy(buf, &cmd, sizeof(cmd));
(void) memcpy(buf + sizeof(cmd), conn, sizeof(conn));
(void) send(conn->worker->ctl[1], buf, sizeof(buf), 0);
}
const struct io_class io_embedded = {
"embedded",
do_embedded,

View File

@ -20,7 +20,7 @@ write_file(struct stream *stream, const void *buf, size_t len)
assert(fd != -1);
n = write(fd, buf, len);
DBG(("put_file(%p, %d): %d bytes", (void *) stream, len, n));
DBG(("put_file(%p, %d): %d bytes", (void *) stream, (int) len, n));
if (n <= 0 || (rem->io.total >= (big_int_t) rem->headers_len)) {
(void) fstat(fd, &st);

View File

@ -33,21 +33,21 @@ ssl_handshake(struct stream *stream)
{
int n;
if ((n = SSL_accept(stream->chan.ssl.ssl)) == 0) {
if ((n = SSL_accept(stream->chan.ssl.ssl)) == 1) {
DBG(("handshake: SSL accepted"));
stream->flags |= FLAG_SSL_ACCEPTED;
} else {
n = SSL_get_error(stream->chan.ssl.ssl, n);
if (n != SSL_ERROR_WANT_READ && n != SSL_ERROR_WANT_WRITE)
stream->flags |= FLAG_CLOSED;
elog(E_LOG, stream->conn, "SSL_accept error %d", n);
} else {
DBG(("handshake: SSL accepted"));
stream->flags |= FLAG_SSL_ACCEPTED;
DBG(("SSL_accept error %d", n));
}
}
static int
read_ssl(struct stream *stream, void *buf, size_t len)
{
int nread = 0;
int nread = -1;
assert(stream->chan.ssl.ssl != NULL);

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,13 @@
/*
* Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2004-2008 Sergey Lyubka <valenok@gmail.com>
* All rights reserved
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
* "THE BEER-WARE LICENSE" (Revision 42):
* Sergey Lyubka wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* $Id: shttpd.h,v 1.9 2008/02/13 20:44:32 drozd Exp $
* $Id: shttpd.h,v 1.16 2008/05/31 09:02:02 drozd Exp $
*/
#ifndef SHTTPD_HEADER_INCLUDED
@ -41,54 +29,62 @@ struct ubuf {
struct shttpd_arg {
void *priv; /* Private! Do not touch! */
void *state; /* User state */
void *user_data; /* User-defined data */
void *user_data; /* Data from register_uri() */
struct ubuf in; /* Input is here, POST data */
struct ubuf out; /* Output goes here */
unsigned int flags;
#define SHTTPD_END_OF_OUTPUT 1
#define SHTTPD_CONNECTION_ERROR 2
#define SHTTPD_MORE_POST_DATA 4
#define SHTTPD_POST_BUFFER_FULL 8
#define SHTTPD_SSI_EVAL_TRUE 16
#define SHTTPD_END_OF_OUTPUT 1 /* No more data do send */
#define SHTTPD_CONNECTION_ERROR 2 /* Server closed the connection */
#define SHTTPD_MORE_POST_DATA 4 /* arg->in has incomplete data */
#define SHTTPD_POST_BUFFER_FULL 8 /* arg->in has max data */
#define SHTTPD_SSI_EVAL_TRUE 16 /* SSI eval callback must set it*/
#define SHTTPD_SUSPEND 32 /* User wants to suspend output */
};
/*
* User callback function. Called when certain registered URLs have been
* requested. These are the requirements to the callback function:
*
* 1. it must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
* 1. It must copy data into 'out.buf' buffer, not more than 'out.len' bytes,
* and record how many bytes are copied, into 'out.num_bytes'
* 2. it must not block the execution
* 3. it must set SHTTPD_END_OF_OUTPUT flag when finished
* 4. for POST requests, it must process the incoming data (in.buf) of length
* 2. It must not call any blocking functions
* 3. It must set SHTTPD_END_OF_OUTPUT flag when there is no more data to send
* 4. For POST requests, it must process the incoming data (in.buf) of length
* 'in.len', and set 'in.num_bytes', which is how many bytes of POST
* data is read and can be discarded by SHTTPD.
* data was processed and can be discarded by SHTTPD.
* 5. If callback allocates arg->state, to keep state, it must deallocate it
* at the end of coonection SHTTPD_CONNECTION_ERROR or SHTTPD_END_OF_OUTPUT
* 6. If callback function wants to suspend until some event, it must store
* arg->priv pointer elsewhere, set SHTTPD_SUSPEND flag and return. When
* the event happens, user code should call shttpd_wakeup(priv).
* It is safe to call shttpd_wakeup() from any thread. User code must
* not call shttpd_wakeup once the connection is closed.
*/
typedef void (*shttpd_callback_t)(struct shttpd_arg *);
/*
* shttpd_init Initialize shttpd context.
* shttpd_set_option Set new value for option.
* shttpd_fini Dealocate the context
* shttpd_register_uri Setup the callback function for specified URL.
* shtppd_listen Setup a listening socket in the SHTTPD context
* shttpd_init Initialize shttpd context
* shttpd_fini Dealocate the context, close all connections
* shttpd_set_option Set new value for option
* shttpd_register_uri Setup the callback function for specified URL
* shttpd_poll Do connections processing
* shttpd_version return string with SHTTPD version
* shttpd_get_var Fetch POST/GET variable value by name. Return value len
* shttpd_get_header return value of the specified HTTP header
* shttpd_get_env return string values for the following
* pseudo-variables: "REQUEST_METHOD", "REQUEST_URI",
* "REMOTE_USER" and "REMOTE_ADDR".
* shttpd_get_env return values for the following pseudo-variables:
"REQUEST_METHOD", "REQUEST_URI",
* "REMOTE_USER" and "REMOTE_ADDR"
* shttpd_printf helper function to output data
* shttpd_handle_error register custom HTTP error handler
* shttpd_wakeup clear SHTTPD_SUSPEND state for the connection
*/
struct shttpd_ctx;
struct shttpd_ctx *shttpd_init(void);
struct shttpd_ctx *shttpd_init(int argc, char *argv[]);
void shttpd_set_option(struct shttpd_ctx *, const char *opt, const char *val);
void shttpd_fini(struct shttpd_ctx *);
int shttpd_listen(struct shttpd_ctx *ctx, int port, int is_ssl);
void shttpd_register_uri(struct shttpd_ctx *ctx, const char *uri,
shttpd_callback_t callback, void *const user_data);
void shttpd_poll(struct shttpd_ctx *, int milliseconds);
@ -104,19 +100,7 @@ void shttpd_handle_error(struct shttpd_ctx *ctx, int status,
shttpd_callback_t func, void *const data);
void shttpd_register_ssi_func(struct shttpd_ctx *ctx, const char *name,
shttpd_callback_t func, void *const user_data);
/*
* The following three functions are for applications that need to
* load-balance the connections on their own. Many threads may be spawned
* with one SHTTPD context per thread. Boss thread may only wait for
* new connections by means of shttpd_accept(). Then it may scan thread
* pool for the idle thread by means of shttpd_active(), and add new
* connection to the context by means of shttpd_add().
*/
void shttpd_add_socket(struct shttpd_ctx *, int sock, int is_ssl);
int shttpd_accept(int lsn_sock, int milliseconds);
int shttpd_active(struct shttpd_ctx *);
void shttpd_wakeup(const void *priv);
#ifdef __cplusplus
}

View File

@ -10,7 +10,7 @@
#include "defs.h"
static int exit_flag;
static int exit_flag; /* Program termination flag */
static void
signal_handler(int sig_num)
@ -27,70 +27,6 @@ signal_handler(int sig_num)
}
}
void
process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
{
const char *config_file = CONFIG_FILE;
char line[BUFSIZ], opt[BUFSIZ],
val[BUFSIZ], path[FILENAME_MAX], *p;
FILE *fp;
size_t i, line_no = 0;
/* First find out, which config file to open */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
if (argv[i + 1] == NULL)
usage(argv[0]);
if (argv[i] != NULL && argv[i + 1] != NULL) {
/* More than one non-option arguments are given w*/
usage(argv[0]);
} else if (argv[i] != NULL) {
/* Just one non-option argument is given, this is config file */
config_file = argv[i];
} else {
/* No config file specified. Look for one where shttpd lives */
if ((p = strrchr(argv[0], DIRSEP)) != 0) {
my_snprintf(path, sizeof(path), "%.*s%s",
p - argv[0] + 1, argv[0], config_file);
config_file = path;
}
}
fp = fopen(config_file, "r");
/* If config file was set in command line and open failed, exit */
if (fp == NULL && argv[i] != NULL)
elog(E_FATAL, NULL, "cannot open config file %s: %s",
config_file, strerror(errno));
if (fp != NULL) {
elog(E_LOG, NULL, "Loading config file %s", config_file);
/* Loop over the lines in config file */
while (fgets(line, sizeof(line), fp) != NULL) {
line_no++;
/* Ignore empty lines and comments */
if (line[0] == '#' || line[0] == '\n')
continue;
if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
elog(E_FATAL, NULL, "line %d in %s is invalid",
line_no, config_file);
shttpd_set_option(ctx, opt, val);
}
(void) fclose(fp);
}
/* Now pass through the command line options */
for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
shttpd_set_option(ctx, &argv[i][1], argv[i + 1]);
}
int
main(int argc, char *argv[])
{
@ -107,24 +43,11 @@ main(int argc, char *argv[])
if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))
usage(argv[0]);
ctx = shttpd_init();
process_command_line_arguments(ctx, argv);
#if defined(_WIN32)
try_to_run_as_nt_service();
#endif /* _WIN32 */
#ifndef _WIN32
/* Switch to alternate UID, it is safe now, after shttpd_listen() */
if (ctx->options[OPT_UID] != NULL) {
struct passwd *pw;
if ((pw = getpwnam(ctx->options[OPT_UID])) == NULL)
elog(E_FATAL, 0, "main: unknown user [%s]",
ctx->options[OPT_UID]);
else if (setgid(pw->pw_gid) == -1)
elog(E_FATAL, NULL, "main: setgid(%s): %s",
ctx->options[OPT_UID], strerror(errno));
else if (setuid(pw->pw_uid) == -1)
elog(E_FATAL, NULL, "main: setuid(%s): %s",
ctx->options[OPT_UID], strerror(errno));
}
(void) signal(SIGCHLD, signal_handler);
(void) signal(SIGPIPE, SIG_IGN);
#endif /* _WIN32 */
@ -132,22 +55,15 @@ main(int argc, char *argv[])
(void) signal(SIGTERM, signal_handler);
(void) signal(SIGINT, signal_handler);
if (IS_TRUE(ctx, OPT_INETD)) {
shttpd_set_option(ctx, "ports", NULL);
(void) freopen("/dev/null", "a", stderr);
shttpd_add_socket(ctx, fileno(stdin), 0);
}
ctx = shttpd_init(argc, argv);
elog(E_LOG, NULL, "shttpd %s started on port(s) %s, serving %s",
VERSION, ctx->options[OPT_PORTS], ctx->options[OPT_ROOT]);
while (exit_flag == 0)
shttpd_poll(ctx, 5000);
elog(E_LOG, NULL, "%d requests %.2lf Mb in %.2lf Mb out. "
"Exit on signal %d", ctx->nrequests, (double) (ctx->in / 1048576),
(double) ctx->out / 1048576, exit_flag);
shttpd_poll(ctx, 10 * 1000);
elog(E_LOG, NULL, "Exit on signal %d", exit_flag);
shttpd_fini(ctx);
return (EXIT_SUCCESS);