Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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.