Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

1152 lines (924 sloc) 22.706 kB
/*
* Showtime HTTP server
* Copyright (C) 2010 Andreas Öman
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <netinet/in.h>
#define hsprintf(fmt...) // printf(fmt)
#if ENABLE_POSIX_NETWORKING
#include <netinet/tcp.h> // for TCP_ defines
#endif
#include "http.h"
#include "http_server.h"
int http_server_port;
static LIST_HEAD(, http_path) http_paths;
LIST_HEAD(http_connection_list, http_connection);
/**
*
*/
struct http_connection {
LIST_ENTRY(http_connection) hc_link;
int hc_fd;
int hc_events;
int hc_state;
#define HCS_COMMAND 0
#define HCS_HEADERS 1
#define HCS_POSTDATA 2
struct http_header_list hc_request_headers;
struct http_header_list hc_req_args;
struct http_header_list hc_response_headers;
htsbuf_queue_t hc_input;
htsbuf_queue_t hc_output;
http_cmd_t hc_cmd;
enum {
HTTP_VERSION_1_0,
HTTP_VERSION_1_1,
} hc_version;
char *hc_url;
char *hc_url_orig;
char hc_keep_alive;
char hc_no_output;
char *hc_post_data;
size_t hc_post_len;
size_t hc_post_offset;
char hc_myaddr[32];
};
/**
*
*/
static struct strtab HTTP_cmdtab[] = {
{ "GET", HTTP_CMD_GET },
{ "HEAD", HTTP_CMD_HEAD },
{ "POST", HTTP_CMD_POST },
{ "SUBSCRIBE", HTTP_CMD_SUBSCRIBE },
{ "UNSUBSCRIBE", HTTP_CMD_UNSUBSCRIBE },
};
/**
*
*/
static struct strtab HTTP_versiontab[] = {
{ "HTTP/1.0", HTTP_VERSION_1_0 },
{ "HTTP/1.1", HTTP_VERSION_1_1 },
};
/**
*
*/
typedef struct http_path {
LIST_ENTRY(http_path) hp_link;
const char *hp_path;
void *hp_opaque;
http_callback_t *hp_callback;
int hp_len;
int hp_leaf;
} http_path_t;
/**
*
*/
typedef struct http_server {
int hs_numcon;
int hs_fd;
int hs_fds_size;
struct pollfd *hs_fds;
struct http_connection_list hs_connections;
} http_server_t;
/**
*
*/
void *
http_get_post_data(http_connection_t *hc, size_t *sizep, int steal)
{
void *r = hc->hc_post_data;
if(sizep != NULL)
*sizep = hc->hc_post_len;
if(steal)
hc->hc_post_data = NULL;
return r;
}
/**
* Add a callback for a given "virtual path" on our HTTP server
*/
void *
http_path_add(const char *path, void *opaque, http_callback_t *callback,
int leaf)
{
http_path_t *hp = malloc(sizeof(http_path_t));
hp->hp_len = strlen(path);
hp->hp_path = strdup(path);
hp->hp_opaque = opaque;
hp->hp_callback = callback;
hp->hp_leaf = leaf;
LIST_INSERT_HEAD(&http_paths, hp, hp_link);
return hp;
}
/**
*
*/
static http_path_t *
http_resolve(http_connection_t *hc, char **remainp, char **argsp)
{
http_path_t *hp;
char *v;
LIST_FOREACH(hp, &http_paths, hp_link) {
if(!strncmp(hc->hc_url, hp->hp_path, hp->hp_len)) {
if(hc->hc_url[hp->hp_len] == 0 || hc->hc_url[hp->hp_len] == '/' ||
hc->hc_url[hp->hp_len] == '?')
break;
}
}
if(hp == NULL)
return NULL;
v = hc->hc_url + hp->hp_len;
*remainp = NULL;
*argsp = NULL;
switch(*v) {
case 0:
break;
case '/':
if(v[1] == '?') {
*argsp = v + 1;
break;
}
*remainp = v + 1;
v = strchr(v + 1, '?');
if(v != NULL) {
*v = 0; /* terminate remaining url */
*argsp = v + 1;
}
break;
case '?':
*argsp = v + 1;
break;
default:
return NULL;
}
return hp;
}
/**
* HTTP status code to string
*/
static const char *
http_rc2str(int code)
{
switch(code) {
case HTTP_STATUS_OK: return "Ok";
case HTTP_STATUS_NOT_FOUND: return "Not found";
case HTTP_STATUS_UNAUTHORIZED: return "Unauthorized";
case HTTP_STATUS_BAD_REQUEST: return "Bad request";
case HTTP_STATUS_FOUND: return "Found";
case HTTP_STATUS_METHOD_NOT_ALLOWED: return "Method not allowed";
case HTTP_STATUS_PRECONDITION_FAILED: return "Precondition failed";
case HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE: return "Unsupported media type";
case HTTP_NOT_IMPLEMENTED: return "Not implemented";
case 500: return "Internal Server Error";
default:
return "Unknown returncode";
break;
}
}
/**
* Transmit a HTTP reply
*/
static void
http_send_header(http_connection_t *hc, int rc, const char *content,
int contentlen, const char *encoding, const char *location,
int maxage, const char *range)
{
htsbuf_queue_t hdrs;
time_t t;
http_header_t *hh;
char date[64];
time(&t);
htsbuf_queue_init(&hdrs, 0);
htsbuf_qprintf(&hdrs, "%s %d %s\r\n",
val2str(hc->hc_version, HTTP_versiontab),
rc, http_rc2str(rc));
htsbuf_qprintf(&hdrs, "Server: HTS/Showtime %s\r\n",
htsversion_full);
htsbuf_qprintf(&hdrs, "Date: %s\r\n", http_asctime(t, date, sizeof(date)));
if(maxage == 0) {
htsbuf_qprintf(&hdrs, "Cache-Control: no-cache\r\n");
} else {
htsbuf_qprintf(&hdrs, "Last-Modified: %s\r\n",
http_asctime(t, date, sizeof(date)));
t += maxage;
htsbuf_qprintf(&hdrs, "Expires: %s\r\n",
http_asctime(t, date, sizeof(date)));
htsbuf_qprintf(&hdrs, "Cache-Control: max-age=%d\r\n", maxage);
}
htsbuf_qprintf(&hdrs, "Connection: %s\r\n",
hc->hc_keep_alive ? "Keep-Alive" : "Close");
if(encoding != NULL)
htsbuf_qprintf(&hdrs, "Content-Encoding: %s\r\n", encoding);
if(location != NULL)
htsbuf_qprintf(&hdrs, "Location: %s\r\n", location);
if(content != NULL)
htsbuf_qprintf(&hdrs, "Content-Type: %s\r\n", content);
htsbuf_qprintf(&hdrs, "Content-Length: %d\r\n", contentlen);
LIST_FOREACH(hh, &hc->hc_response_headers, hh_link)
htsbuf_qprintf(&hdrs, "%s: %s\r\n", hh->hh_key, hh->hh_value);
htsbuf_qprintf(&hdrs, "\r\n");
htsbuf_appendq(&hc->hc_output, &hdrs);
}
/**
* Transmit a HTTP reply
*/
int
http_send_raw(http_connection_t *hc, int rc, const char *rctxt,
struct http_header_list *headers, htsbuf_queue_t *output)
{
htsbuf_queue_t hdrs;
http_header_t *hh;
#if 0
struct tm tm0, *tm;
time_t t;
#endif
htsbuf_queue_init(&hdrs, 0);
htsbuf_qprintf(&hdrs, "%s %d %s\r\n",
val2str(hc->hc_version, HTTP_versiontab),
rc, rctxt);
if(headers != NULL) {
LIST_FOREACH(hh, headers, hh_link)
htsbuf_qprintf(&hdrs, "%s: %s\r\n", hh->hh_key, hh->hh_value);
http_headers_free(headers);
}
htsbuf_qprintf(&hdrs, "\r\n");
htsbuf_appendq(&hc->hc_output, &hdrs);
if(output != NULL) {
if(hc->hc_no_output)
htsbuf_queue_flush(output);
else
htsbuf_appendq(&hc->hc_output, output);
}
return 0;
}
/**
* Transmit a HTTP reply
*/
int
http_send_reply(http_connection_t *hc, int rc, const char *content,
const char *encoding, const char *location, int maxage,
htsbuf_queue_t *output)
{
http_send_header(hc, rc ?: 200, content, output ? output->hq_size : 0,
encoding, location, maxage, 0);
if(output != NULL) {
if(hc->hc_no_output)
htsbuf_queue_flush(output);
else
htsbuf_appendq(&hc->hc_output, output);
}
return 0;
}
/**
* Send HTTP error back
*/
int
http_error(http_connection_t *hc, int error, const char *fmt, ...)
{
const char *errtxt = http_rc2str(error);
htsbuf_queue_t hq;
va_list ap;
char extra[200];
htsbuf_queue_init(&hq, 0);
if(fmt != NULL) {
va_start(ap, fmt);
vsnprintf(extra, sizeof(extra), fmt, ap);
va_end(ap);
} else {
extra[0] = 0;
}
TRACE(TRACE_ERROR, "HTTPSRV", "%d %s%s%s", error, hc->hc_url_orig,
*extra ? " -- " : "", extra),
htsbuf_qprintf(&hq,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>%d %s</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"<H1>%d %s</H1>\r\n"
"<p>%s</p>\r\n"
"</BODY></HTML>\r\n",
error, errtxt, error, errtxt, extra);
return http_send_reply(hc, error, "text/html", NULL, NULL, 0, &hq);
}
/**
* Send an HTTP REDIRECT
*/
int
http_redirect(http_connection_t *hc, const char *location)
{
htsbuf_queue_t hq;
htsbuf_queue_init(&hq, 0);
htsbuf_qprintf(&hq,
"<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
"<HTML><HEAD>\r\n"
"<TITLE>Redirect</TITLE>\r\n"
"</HEAD><BODY>\r\n"
"Please follow <a href=\"%s\">%s</a>\r\n"
"</BODY></HTML>\r\n",
location, location);
return http_send_reply(hc, HTTP_STATUS_FOUND,
"text/html", NULL, location, 0, &hq);
}
/**
*
*/
static void
http_exec(http_connection_t *hc, http_path_t *hp, char *remain,
http_cmd_t method)
{
hsprintf("%p: Dispatching [%s] on thread 0x%lx\n",
hc, hp->hp_path, pthread_self());
int err = hp->hp_callback(hc, remain, hp->hp_opaque, method);
hsprintf("%p: Returned from fn, err = %d\n", hc, err);
if(err == HTTP_STATUS_OK) {
htsbuf_queue_t out;
htsbuf_queue_init(&out, 0);
htsbuf_append(&out, "OK\n", 3);
http_send_reply(hc, 0, "text/ascii", NULL, NULL, 0, &out);
return;
} else if(err > 0)
http_error(hc, err, NULL);
else if(err == 0)
return;
else
abort();
}
/**
* De-escape HTTP URL
*/
static void
http_deescape(char *s)
{
char v, *d = s;
while(*s) {
if(*s == '+') {
*d++ = ' ';
s++;
} else if(*s == '%') {
s++;
switch(*s) {
case '0' ... '9':
v = (*s - '0') << 4;
break;
case 'a' ... 'f':
v = (*s - 'a' + 10) << 4;
break;
case 'A' ... 'F':
v = (*s - 'A' + 10) << 4;
break;
default:
*d = 0;
return;
}
s++;
switch(*s) {
case '0' ... '9':
v |= (*s - '0');
break;
case 'a' ... 'f':
v |= (*s - 'a' + 10);
break;
case 'A' ... 'F':
v |= (*s - 'A' + 10);
break;
default:
*d = 0;
return;
}
s++;
*d++ = v;
} else {
*d++ = *s++;
}
}
*d = 0;
}
/**
*
*/
const char *
http_arg_get_req(http_connection_t *hc, const char *name)
{
return http_header_get(&hc->hc_req_args, name);
}
/**
*
*/
const char *
http_arg_get_hdr(http_connection_t *hc, const char *name)
{
return http_header_get(&hc->hc_request_headers, name);
}
/**
*
*/
void
http_set_response_hdr(http_connection_t *hc, const char *name,
const char *value)
{
http_header_add(&hc->hc_response_headers, name, value, 0);
}
/**
* Split a string in components delimited by 'delimiter'
*/
static int
http_tokenize(char *buf, char **vec, int vecsize, int delimiter)
{
int n = 0;
while(1) {
while((*buf > 0 && *buf < 33) || *buf == delimiter)
buf++;
if(*buf == 0)
break;
vec[n++] = buf;
if(n == vecsize)
break;
while(*buf > 32 && *buf != delimiter)
buf++;
if(*buf == 0)
break;
*buf = 0;
buf++;
}
return n;
}
/**
* Parse arguments of a HTTP GET url, not perfect, but works for us
*/
static void
http_parse_get_args(http_connection_t *hc, char *args)
{
char *k, *v;
while(args) {
k = args;
if((args = strchr(args, '=')) == NULL)
break;
*args++ = 0;
v = args;
args = strchr(args, '&');
if(args != NULL)
*args++ = 0;
http_deescape(k);
http_deescape(v);
http_header_add(&hc->hc_req_args, k, v, 0);
}
}
/**
*
*/
static int
http_cmd_get(http_connection_t *hc, http_cmd_t method)
{
http_path_t *hp;
char *remain;
char *args;
hp = http_resolve(hc, &remain, &args);
if(hp == NULL || (hp->hp_leaf && remain != NULL)) {
http_error(hc, HTTP_STATUS_NOT_FOUND, NULL);
return 0;
}
if(args != NULL)
http_parse_get_args(hc, args);
http_exec(hc, hp, remain, method);
return 0;
}
/**
*
*/
static int
http_read_post(http_connection_t *hc)
{
http_path_t *hp;
const char *content_type;
char *v, *argv[2], *args, *remain;
int n;
size_t size = hc->hc_input.hq_size;
size_t rsize = hc->hc_post_len - hc->hc_post_offset;
if(size > rsize)
size = rsize;
n = htsbuf_read(&hc->hc_input, hc->hc_post_data + hc->hc_post_offset, size);
assert(n == size);
hc->hc_post_offset += size;
assert(hc->hc_post_offset <= hc->hc_post_len);
if(hc->hc_post_offset < hc->hc_post_len)
return 0;
hc->hc_state = HCS_COMMAND;
/* Parse content-type */
content_type = http_header_get(&hc->hc_request_headers, "Content-Type");
if(content_type != NULL) {
v = mystrdupa(content_type);
n = http_tokenize(v, argv, 2, ';');
if(n == 0) {
http_error(hc, HTTP_STATUS_BAD_REQUEST, "Content-Type malformed");
return 0;
}
if(!strcmp(argv[0], "application/x-www-form-urlencoded"))
http_parse_get_args(hc, hc->hc_post_data);
}
hp = http_resolve(hc, &remain, &args);
if(hp == NULL) {
http_error(hc, HTTP_STATUS_NOT_FOUND, NULL);
return 0;
}
http_exec(hc, hp, remain, HTTP_CMD_POST);
return 0;
}
/**
*
*/
static int
http_cmd_post(http_connection_t *hc)
{
const char *v;
v = http_header_get(&hc->hc_request_headers, "Content-Length");
if(v == NULL) {
/* No content length in POST, make us disconnect */
return 1;
}
hc->hc_post_len = atoi(v);
if(hc->hc_post_len > 16 * 1024 * 1024) {
/* Bail out if POST data > 16 Mb */
hc->hc_keep_alive = 0;
return 1;
}
/* Allocate space for data, we add a terminating null char to ease
string processing on the content */
hc->hc_post_data = malloc(hc->hc_post_len + 1);
if(hc->hc_post_data == NULL) {
hc->hc_keep_alive = 0;
return 1;
}
hc->hc_post_data[hc->hc_post_len] = 0;
hc->hc_post_offset = 0;
v = http_header_get(&hc->hc_request_headers, "Expect");
if(v != NULL) {
if(!strcasecmp(v, "100-continue")) {
htsbuf_qprintf(&hc->hc_output, "HTTP/1.1 100 Continue\r\n\r\n");
}
}
hc->hc_state = HCS_POSTDATA;
return http_read_post(hc);
}
/**
*
*/
static int
http_handle_request(http_connection_t *hc)
{
hc->hc_state = HCS_COMMAND;
/* Set keep-alive status */
const char *v = http_header_get(&hc->hc_request_headers, "connection");
switch(hc->hc_version) {
case HTTP_VERSION_1_0:
/* Keep-alive is default off, but can be enabled */
hc->hc_keep_alive = v != NULL && !strcasecmp(v, "keep-alive");
break;
case HTTP_VERSION_1_1:
/* Keep-alive is default on, but can be disabled */
hc->hc_keep_alive = !(v != NULL && !strcasecmp(v, "close"));
break;
}
hc->hc_no_output = hc->hc_cmd == HTTP_CMD_HEAD;
switch(hc->hc_cmd) {
default:
http_error(hc, HTTP_NOT_IMPLEMENTED, NULL);
return 0;
case HTTP_CMD_POST:
return http_cmd_post(hc);
case HTTP_CMD_HEAD:
hc->hc_no_output = 1;
// FALLTHRU
case HTTP_CMD_GET:
case HTTP_CMD_SUBSCRIBE:
case HTTP_CMD_UNSUBSCRIBE:
return http_cmd_get(hc, hc->hc_cmd);
}
return 1;
}
/**
*
*/
static int
http_read_line(http_connection_t *hc, char *buf, size_t bufsize)
{
int len;
len = htsbuf_find(&hc->hc_input, 0xa);
if(len == -1)
return 0;
if(len >= bufsize - 1)
return -1;
htsbuf_read(&hc->hc_input, buf, len);
buf[len] = 0;
while(len > 0 && buf[len - 1] < 32)
buf[--len] = 0;
htsbuf_drop(&hc->hc_input, 1); /* Drop the \n */
return 1;
}
/**
*
*/
static int
http_handle_input(http_connection_t *hc)
{
char buf[1024];
char *argv[3], *c;
int r, n;
while(1) {
switch(hc->hc_state) {
case HCS_COMMAND:
free(hc->hc_post_data);
hc->hc_post_data = NULL;
if((r = http_read_line(hc, buf, sizeof(buf))) == -1)
return 1;
if(r == 0)
return 0;
hsprintf("%p: %s\n", hc, buf);
if((n = http_tokenize(buf, argv, 3, -1)) != 3)
return 1;
hc->hc_cmd = str2val(argv[0], HTTP_cmdtab);
mystrset(&hc->hc_url, argv[1]);
mystrset(&hc->hc_url_orig, argv[1]);
if((hc->hc_version = str2val(argv[2], HTTP_versiontab)) == -1)
return 1;
hc->hc_state = HCS_HEADERS;
/* FALLTHRU */
http_headers_free(&hc->hc_req_args);
http_headers_free(&hc->hc_request_headers);
http_headers_free(&hc->hc_response_headers);
case HCS_HEADERS:
if((r = http_read_line(hc, buf, sizeof(buf))) == -1)
return 1;
if(r == 0)
return 0;
hsprintf("%p: %s\n", hc, buf);
if(buf[0] == 32 || buf[0] == '\t') {
// LWS
http_header_add_lws(&hc->hc_request_headers, buf+1);
} else if(buf[0] == 0) {
if(http_handle_request(hc))
return 1;
if(TAILQ_FIRST(&hc->hc_output.hq_q) == NULL && !hc->hc_keep_alive)
return 1;
} else {
if((c = strchr(buf, ':')) == NULL)
return 1;
*c++ = 0;
while(*c == 32)
c++;
http_header_add(&hc->hc_request_headers, buf, c, 0);
}
break;
case HCS_POSTDATA:
if(!http_read_post(hc))
return 0;
}
}
}
/**
*
*/
static int
http_write(http_connection_t *hc)
{
htsbuf_data_t *hd;
int l, r = 0;
htsbuf_queue_t *q = &hc->hc_output;
while((hd = TAILQ_FIRST(&q->hq_q)) != NULL) {
l = hd->hd_data_len - hd->hd_data_off;
r = write(hc->hc_fd, hd->hd_data + hd->hd_data_off, l);
if(r == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
r = 0;
if(r == -1)
return -1;
q->hq_size -= r;
if(r != l) {
// Failed to write it all
hd->hd_data_off += r;
hc->hc_events |= POLLOUT;
return 0;
}
TAILQ_REMOVE(&q->hq_q, hd, hd_link);
free(hd->hd_data);
free(hd);
}
hc->hc_events &= ~POLLOUT;
return !hc->hc_keep_alive;
}
/**
*
*/
static int
http_io(http_connection_t *hc, int revents)
{
int r;
if(revents & (POLLHUP | POLLERR))
return 1;
if(revents & POLLIN) {
char *mem = malloc(1000);
r = read(hc->hc_fd, mem, 1000);
if(r > 0) {
htsbuf_append_prealloc(&hc->hc_input, mem, r);
if(http_handle_input(hc))
return 1;
} else {
free(mem);
return 1;
}
}
r = http_write(hc);
return r;
}
/**
*
*/
static void
http_close(http_server_t *hs, http_connection_t *hc)
{
hsprintf("%p: ----------------- CLOSED CONNECTION\n", hc);
htsbuf_queue_flush(&hc->hc_input);
htsbuf_queue_flush(&hc->hc_output);
http_headers_free(&hc->hc_req_args);
http_headers_free(&hc->hc_request_headers);
http_headers_free(&hc->hc_response_headers);
LIST_REMOVE(hc, hc_link);
hs->hs_numcon--;
close(hc->hc_fd);
free(hc->hc_url);
free(hc->hc_url_orig);
free(hc->hc_post_data);
free(hc);
}
/**
*
*/
const char *
http_get_my_host(http_connection_t *hc)
{
return hc->hc_myaddr;
}
int
http_get_my_port(http_connection_t *hc)
{
return http_server_port;
}
/**
*
*/
static void
http_accept(http_server_t *hs)
{
struct sockaddr_in si;
socklen_t sl = sizeof(struct sockaddr_in);
int fd, val;
http_connection_t *hc;
socklen_t slen;
struct sockaddr_in self;
fd = accept(hs->hs_fd, (struct sockaddr *)&si, &sl);
if(fd == -1) {
TRACE(TRACE_ERROR, "HTTPSRV", "Accept error: %s", strerror(errno));
sleep(1);
return;
}
hc = calloc(1, sizeof(http_connection_t));
hc->hc_fd = fd;
hc->hc_events = POLLIN | POLLHUP | POLLERR;
LIST_INSERT_HEAD(&hs->hs_connections, hc, hc_link);
hs->hs_numcon++;
htsbuf_queue_init(&hc->hc_input, 0);
htsbuf_queue_init(&hc->hc_output, 0);
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
val = 1;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val));
#ifdef TCP_KEEPIDLE
val = 30;
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val));
#endif
#ifdef TCP_KEEPINVL
val = 15;
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val));
#endif
#ifdef TCP_KEEPCNT
val = 5;
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val));
#endif
#ifdef TCP_NODELAY
val = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
#endif
slen = sizeof(struct sockaddr_in);
if(!getsockname(fd, (struct sockaddr *)&self, &slen)) {
uint32_t ip = ntohl(self.sin_addr.s_addr);
snprintf(hc->hc_myaddr, sizeof(hc->hc_myaddr),
"%d.%d.%d.%d",
(ip >> 24) & 0xff,
(ip >> 16) & 0xff,
(ip >> 8) & 0xff,
(ip) & 0xff);
} else {
hc->hc_myaddr[0] = 0;
}
hsprintf("%p: ----------------- NEW CONNECTION\n", hc);
}
/**
*
*/
static void *
http_server(void *aux)
{
http_server_t *hs = aux;
int n, r;
http_connection_t *hc, *nxt;
while(1) {
n = hs->hs_numcon + 1;
if(hs->hs_fds_size < n) {
hs->hs_fds_size = n + 3;
hs->hs_fds = realloc(hs->hs_fds, sizeof(struct pollfd) * hs->hs_fds_size);
}
n = 0;
LIST_FOREACH(hc, &hs->hs_connections, hc_link) {
hs->hs_fds[n].fd = hc->hc_fd;
hs->hs_fds[n].events = hc->hc_events;
n++;
}
hs->hs_fds[n].fd = hs->hs_fd;
hs->hs_fds[n].events = POLLIN;
n++;
r = poll(hs->hs_fds, n, -1);
if(r == -1)
continue;
n = 0;
for(hc = LIST_FIRST(&hs->hs_connections); hc != NULL; hc = nxt) {
nxt = LIST_NEXT(hc, hc_link);
if(http_io(hc, hs->hs_fds[n].revents)) {
http_close(hs, hc);
}
n++;
}
if(hs->hs_fds[n].revents & POLLIN)
http_accept(hs);
}
return NULL;
}
/**
*
*/
void
http_server_init(void)
{
int fd;
struct sockaddr_in si = {0};
socklen_t sl = sizeof(struct sockaddr_in);
int one = 1, i;
http_server_t *hs;
if((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
return;
si.sin_family = AF_INET;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
for(i = 0; i < 100; i++) {
si.sin_port = htons(42000 + i);
if(!bind(fd, (struct sockaddr *)&si, sizeof(struct sockaddr_in)))
break;
}
if(i == 100) {
si.sin_port = 0;
if(bind(fd, (struct sockaddr *)&si, sizeof(struct sockaddr_in)) == -1) {
TRACE(TRACE_ERROR, "HTTPSRV", "Unable to bind");
close(fd);
return;
}
if(getsockname(fd, (struct sockaddr *)&si, &sl) == -1) {
TRACE(TRACE_ERROR, "HTTPSRV", "Unable to figure local port");
close(fd);
return;
}
http_server_port = ntohs(si.sin_port);
} else {
http_server_port = 42000 + i;
}
TRACE(TRACE_INFO, "HTTPSRV", "Listening on port %d", http_server_port);
listen(fd, 1);
hs = calloc(1, sizeof(http_server_t));
hs->hs_fd = fd;
hts_thread_create_detached("httpsrv", http_server, hs,
THREAD_PRIO_NORMAL);
}
Jump to Line
Something went wrong with that request. Please try again.