parent
3aa5d2aeed
commit
2ab4ebc2f0
|
@ -27,8 +27,8 @@
|
||||||
typedef void (*action_cb_t)(struct uh_client *cl);
|
typedef void (*action_cb_t)(struct uh_client *cl);
|
||||||
|
|
||||||
struct uh_action {
|
struct uh_action {
|
||||||
struct avl_node avl;
|
struct avl_node avl;
|
||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
action_cb_t cb;
|
action_cb_t cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
54
src/client.c
54
src/client.c
|
@ -22,15 +22,15 @@
|
||||||
#include "uh_ssl.h"
|
#include "uh_ssl.h"
|
||||||
|
|
||||||
const char *const http_versions[] = {
|
const char *const http_versions[] = {
|
||||||
[UH_HTTP_VER_0_9] = "HTTP/0.9",
|
[UH_HTTP_VER_0_9] = "HTTP/0.9",
|
||||||
[UH_HTTP_VER_1_0] = "HTTP/1.0",
|
[UH_HTTP_VER_1_0] = "HTTP/1.0",
|
||||||
[UH_HTTP_VER_1_1] = "HTTP/1.1"
|
[UH_HTTP_VER_1_1] = "HTTP/1.1"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *const http_methods[] = {
|
const char *const http_methods[] = {
|
||||||
[UH_HTTP_MSG_GET] = "GET",
|
[UH_HTTP_MSG_GET] = "GET",
|
||||||
[UH_HTTP_MSG_POST] = "POST",
|
[UH_HTTP_MSG_POST] = "POST",
|
||||||
[UH_HTTP_MSG_HEAD] = "HEAD"
|
[UH_HTTP_MSG_HEAD] = "HEAD"
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void client_send(struct uh_client *cl, const void *data, int len)
|
static inline void client_send(struct uh_client *cl, const void *data, int len)
|
||||||
|
@ -120,34 +120,34 @@ static void uh_handle_request(struct uh_client *cl)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (handle_file_request(cl, path))
|
if (handle_file_request(cl, path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (cl->srv->error404_cb) {
|
if (cl->srv->error404_cb) {
|
||||||
cl->srv->error404_cb(cl);
|
cl->srv->error404_cb(cl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cl->send_error(cl, 404, "Not Found", "The requested PATH %s was not found on this server.", path);
|
cl->send_error(cl, 404, "Not Found", "The requested PATH %s was not found on this server.", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void connection_close(struct uh_client *cl)
|
static inline void connection_close(struct uh_client *cl)
|
||||||
{
|
{
|
||||||
cl->us->eof = true;
|
cl->us->eof = true;
|
||||||
cl->state = CLIENT_STATE_CLOSE;
|
cl->state = CLIENT_STATE_CLOSE;
|
||||||
ustream_state_change(cl->us);
|
ustream_state_change(cl->us);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void keepalive_cb(struct uloop_timeout *timeout)
|
static inline void keepalive_cb(struct uloop_timeout *timeout)
|
||||||
{
|
{
|
||||||
struct uh_client *cl = container_of(timeout, struct uh_client, timeout);
|
struct uh_client *cl = container_of(timeout, struct uh_client, timeout);
|
||||||
|
|
||||||
connection_close(cl);
|
connection_close(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dispatch_done(struct uh_client *cl)
|
static void dispatch_done(struct uh_client *cl)
|
||||||
{
|
{
|
||||||
if (cl->dispatch.free)
|
if (cl->dispatch.free)
|
||||||
cl->dispatch.free(cl);
|
cl->dispatch.free(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int hdr_get_len(struct kvlist *kv, const void *data)
|
static inline int hdr_get_len(struct kvlist *kv, const void *data)
|
||||||
|
@ -157,13 +157,13 @@ static inline int hdr_get_len(struct kvlist *kv, const void *data)
|
||||||
|
|
||||||
static void client_request_done(struct uh_client *cl)
|
static void client_request_done(struct uh_client *cl)
|
||||||
{
|
{
|
||||||
if (cl->response_length < 0)
|
if (cl->response_length < 0)
|
||||||
cl->printf(cl, "0\r\n\r\n");
|
cl->printf(cl, "0\r\n\r\n");
|
||||||
|
|
||||||
dispatch_done(cl);
|
dispatch_done(cl);
|
||||||
|
|
||||||
if (cl->connection_close) {
|
if (cl->connection_close) {
|
||||||
connection_close(cl);
|
connection_close(cl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,7 +250,7 @@ static bool client_init_cb(struct uh_client *cl, char *buf, int len)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
*newline = 0;
|
*newline = 0;
|
||||||
|
|
||||||
cl->state = client_parse_request(cl, buf);
|
cl->state = client_parse_request(cl, buf);
|
||||||
ustream_consume(cl->us, newline + 2 - buf);
|
ustream_consume(cl->us, newline + 2 - buf);
|
||||||
|
@ -349,7 +349,7 @@ static void client_parse_header(struct uh_client *cl, char *data)
|
||||||
|
|
||||||
static bool client_header_cb(struct uh_client *cl, char *buf, int len)
|
static bool client_header_cb(struct uh_client *cl, char *buf, int len)
|
||||||
{
|
{
|
||||||
char *newline;
|
char *newline;
|
||||||
int line_len;
|
int line_len;
|
||||||
|
|
||||||
newline = strstr(buf, "\r\n");
|
newline = strstr(buf, "\r\n");
|
||||||
|
@ -368,9 +368,9 @@ static bool client_header_cb(struct uh_client *cl, char *buf, int len)
|
||||||
|
|
||||||
typedef bool (*read_cb_t)(struct uh_client *cl, char *buf, int len);
|
typedef bool (*read_cb_t)(struct uh_client *cl, char *buf, int len);
|
||||||
static read_cb_t read_cbs[] = {
|
static read_cb_t read_cbs[] = {
|
||||||
[CLIENT_STATE_INIT] = client_init_cb,
|
[CLIENT_STATE_INIT] = client_init_cb,
|
||||||
[CLIENT_STATE_HEADER] = client_header_cb,
|
[CLIENT_STATE_HEADER] = client_header_cb,
|
||||||
[CLIENT_STATE_DATA] = client_data_cb,
|
[CLIENT_STATE_DATA] = client_data_cb,
|
||||||
};
|
};
|
||||||
|
|
||||||
void uh_client_read_cb(struct uh_client *cl)
|
void uh_client_read_cb(struct uh_client *cl)
|
||||||
|
@ -426,7 +426,7 @@ void uh_client_notify_state(struct uh_client *cl)
|
||||||
|
|
||||||
static void client_notify_state(struct ustream *s)
|
static void client_notify_state(struct ustream *s)
|
||||||
{
|
{
|
||||||
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
|
struct uh_client *cl = container_of(s, struct uh_client, sfd.stream);
|
||||||
|
|
||||||
uh_client_notify_state(cl);
|
uh_client_notify_state(cl);
|
||||||
}
|
}
|
||||||
|
@ -434,15 +434,15 @@ static void client_notify_state(struct ustream *s)
|
||||||
void uh_accept_client(struct uh_server *srv, bool ssl)
|
void uh_accept_client(struct uh_server *srv, bool ssl)
|
||||||
{
|
{
|
||||||
struct uh_client *cl;
|
struct uh_client *cl;
|
||||||
unsigned int sl;
|
unsigned int sl;
|
||||||
int sfd;
|
int sfd;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
|
|
||||||
sl = sizeof(addr);
|
sl = sizeof(addr);
|
||||||
sfd = accept(srv->fd.fd, (struct sockaddr *)&addr, &sl);
|
sfd = accept(srv->fd.fd, (struct sockaddr *)&addr, &sl);
|
||||||
if (sfd < 0) {
|
if (sfd < 0) {
|
||||||
uh_log_err("accept");
|
uh_log_err("accept");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cl = calloc(1, sizeof(struct uh_client));
|
cl = calloc(1, sizeof(struct uh_client));
|
||||||
|
|
32
src/client.h
32
src/client.h
|
@ -27,22 +27,22 @@
|
||||||
#define UHTTPD_CONNECTION_TIMEOUT 30
|
#define UHTTPD_CONNECTION_TIMEOUT 30
|
||||||
|
|
||||||
enum http_method {
|
enum http_method {
|
||||||
UH_HTTP_MSG_GET,
|
UH_HTTP_MSG_GET,
|
||||||
UH_HTTP_MSG_POST,
|
UH_HTTP_MSG_POST,
|
||||||
UH_HTTP_MSG_HEAD
|
UH_HTTP_MSG_HEAD
|
||||||
};
|
};
|
||||||
|
|
||||||
enum http_version {
|
enum http_version {
|
||||||
UH_HTTP_VER_0_9,
|
UH_HTTP_VER_0_9,
|
||||||
UH_HTTP_VER_1_0,
|
UH_HTTP_VER_1_0,
|
||||||
UH_HTTP_VER_1_1
|
UH_HTTP_VER_1_1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum client_state {
|
enum client_state {
|
||||||
CLIENT_STATE_INIT,
|
CLIENT_STATE_INIT,
|
||||||
CLIENT_STATE_HEADER,
|
CLIENT_STATE_HEADER,
|
||||||
CLIENT_STATE_DATA,
|
CLIENT_STATE_DATA,
|
||||||
CLIENT_STATE_DONE,
|
CLIENT_STATE_DONE,
|
||||||
CLIENT_STATE_CLOSE
|
CLIENT_STATE_CLOSE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,16 +74,16 @@ struct dispatch {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uh_client {
|
struct uh_client {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct uh_server *srv;
|
struct uh_server *srv;
|
||||||
struct ustream *us;
|
struct ustream *us;
|
||||||
struct ustream_fd sfd;
|
struct ustream_fd sfd;
|
||||||
#if (UHTTPD_SSL_SUPPORT)
|
#if (UHTTPD_SSL_SUPPORT)
|
||||||
struct ustream_ssl ssl;
|
struct ustream_ssl ssl;
|
||||||
#endif
|
#endif
|
||||||
struct uloop_timeout timeout;
|
struct uloop_timeout timeout;
|
||||||
enum client_state state;
|
enum client_state state;
|
||||||
struct http_request request;
|
struct http_request request;
|
||||||
struct sockaddr_in peer_addr;
|
struct sockaddr_in peer_addr;
|
||||||
struct dispatch dispatch;
|
struct dispatch dispatch;
|
||||||
bool connection_close;
|
bool connection_close;
|
||||||
|
|
510
src/file.c
510
src/file.c
|
@ -22,117 +22,117 @@
|
||||||
#include "uhttpd.h"
|
#include "uhttpd.h"
|
||||||
|
|
||||||
static const struct mimetype uh_mime_types[] = {
|
static const struct mimetype uh_mime_types[] = {
|
||||||
{ "txt", "text/plain" },
|
{ "txt", "text/plain" },
|
||||||
{ "log", "text/plain" },
|
{ "log", "text/plain" },
|
||||||
{ "js", "text/javascript" },
|
{ "js", "text/javascript" },
|
||||||
{ "css", "text/css" },
|
{ "css", "text/css" },
|
||||||
{ "htm", "text/html" },
|
{ "htm", "text/html" },
|
||||||
{ "html", "text/html" },
|
{ "html", "text/html" },
|
||||||
{ "diff", "text/x-patch" },
|
{ "diff", "text/x-patch" },
|
||||||
{ "patch", "text/x-patch" },
|
{ "patch", "text/x-patch" },
|
||||||
{ "c", "text/x-csrc" },
|
{ "c", "text/x-csrc" },
|
||||||
{ "h", "text/x-chdr" },
|
{ "h", "text/x-chdr" },
|
||||||
{ "o", "text/x-object" },
|
{ "o", "text/x-object" },
|
||||||
{ "ko", "text/x-object" },
|
{ "ko", "text/x-object" },
|
||||||
|
|
||||||
{ "bmp", "image/bmp" },
|
{ "bmp", "image/bmp" },
|
||||||
{ "gif", "image/gif" },
|
{ "gif", "image/gif" },
|
||||||
{ "png", "image/png" },
|
{ "png", "image/png" },
|
||||||
{ "jpg", "image/jpeg" },
|
{ "jpg", "image/jpeg" },
|
||||||
{ "jpeg", "image/jpeg" },
|
{ "jpeg", "image/jpeg" },
|
||||||
{ "svg", "image/svg+xml" },
|
{ "svg", "image/svg+xml" },
|
||||||
|
|
||||||
{ "json", "application/json" },
|
{ "json", "application/json" },
|
||||||
{ "jsonp", "application/javascript" },
|
{ "jsonp", "application/javascript" },
|
||||||
{ "zip", "application/zip" },
|
{ "zip", "application/zip" },
|
||||||
{ "pdf", "application/pdf" },
|
{ "pdf", "application/pdf" },
|
||||||
{ "xml", "application/xml" },
|
{ "xml", "application/xml" },
|
||||||
{ "xsl", "application/xml" },
|
{ "xsl", "application/xml" },
|
||||||
{ "doc", "application/msword" },
|
{ "doc", "application/msword" },
|
||||||
{ "ppt", "application/vnd.ms-powerpoint" },
|
{ "ppt", "application/vnd.ms-powerpoint" },
|
||||||
{ "xls", "application/vnd.ms-excel" },
|
{ "xls", "application/vnd.ms-excel" },
|
||||||
{ "odt", "application/vnd.oasis.opendocument.text" },
|
{ "odt", "application/vnd.oasis.opendocument.text" },
|
||||||
{ "odp", "application/vnd.oasis.opendocument.presentation" },
|
{ "odp", "application/vnd.oasis.opendocument.presentation" },
|
||||||
{ "pl", "application/x-perl" },
|
{ "pl", "application/x-perl" },
|
||||||
{ "sh", "application/x-shellscript" },
|
{ "sh", "application/x-shellscript" },
|
||||||
{ "php", "application/x-php" },
|
{ "php", "application/x-php" },
|
||||||
{ "deb", "application/x-deb" },
|
{ "deb", "application/x-deb" },
|
||||||
{ "iso", "application/x-cd-image" },
|
{ "iso", "application/x-cd-image" },
|
||||||
{ "tar.gz", "application/x-compressed-tar" },
|
{ "tar.gz", "application/x-compressed-tar" },
|
||||||
{ "tgz", "application/x-compressed-tar" },
|
{ "tgz", "application/x-compressed-tar" },
|
||||||
{ "gz", "application/x-gzip" },
|
{ "gz", "application/x-gzip" },
|
||||||
{ "tar.bz2", "application/x-bzip-compressed-tar" },
|
{ "tar.bz2", "application/x-bzip-compressed-tar" },
|
||||||
{ "tbz", "application/x-bzip-compressed-tar" },
|
{ "tbz", "application/x-bzip-compressed-tar" },
|
||||||
{ "bz2", "application/x-bzip" },
|
{ "bz2", "application/x-bzip" },
|
||||||
{ "tar", "application/x-tar" },
|
{ "tar", "application/x-tar" },
|
||||||
{ "rar", "application/x-rar-compressed" },
|
{ "rar", "application/x-rar-compressed" },
|
||||||
|
|
||||||
{ "mp3", "audio/mpeg" },
|
{ "mp3", "audio/mpeg" },
|
||||||
{ "ogg", "audio/x-vorbis+ogg" },
|
{ "ogg", "audio/x-vorbis+ogg" },
|
||||||
{ "wav", "audio/x-wav" },
|
{ "wav", "audio/x-wav" },
|
||||||
|
|
||||||
{ "mpg", "video/mpeg" },
|
{ "mpg", "video/mpeg" },
|
||||||
{ "mpeg", "video/mpeg" },
|
{ "mpeg", "video/mpeg" },
|
||||||
{ "avi", "video/x-msvideo" },
|
{ "avi", "video/x-msvideo" },
|
||||||
|
|
||||||
{ "README", "text/plain" },
|
{ "README", "text/plain" },
|
||||||
{ "log", "text/plain" },
|
{ "log", "text/plain" },
|
||||||
{ "cfg", "text/plain" },
|
{ "cfg", "text/plain" },
|
||||||
{ "conf", "text/plain" },
|
{ "conf", "text/plain" },
|
||||||
|
|
||||||
{ "pac", "application/x-ns-proxy-autoconfig" },
|
{ "pac", "application/x-ns-proxy-autoconfig" },
|
||||||
{ "wpad.dat", "application/x-ns-proxy-autoconfig" },
|
{ "wpad.dat", "application/x-ns-proxy-autoconfig" },
|
||||||
|
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *canonpath(const char *path, char *path_resolved)
|
static char *canonpath(const char *path, char *path_resolved)
|
||||||
{
|
{
|
||||||
const char *path_cpy = path;
|
const char *path_cpy = path;
|
||||||
char *path_res = path_resolved;
|
char *path_res = path_resolved;
|
||||||
|
|
||||||
/* normalize */
|
/* normalize */
|
||||||
while ((*path_cpy != '\0') && (path_cpy < (path + PATH_MAX - 2))) {
|
while ((*path_cpy != '\0') && (path_cpy < (path + PATH_MAX - 2))) {
|
||||||
if (*path_cpy != '/')
|
if (*path_cpy != '/')
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
/* skip repeating / */
|
/* skip repeating / */
|
||||||
if (path_cpy[1] == '/') {
|
if (path_cpy[1] == '/') {
|
||||||
path_cpy++;
|
path_cpy++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* /./ or /../ */
|
/* /./ or /../ */
|
||||||
if (path_cpy[1] == '.') {
|
if (path_cpy[1] == '.') {
|
||||||
/* skip /./ */
|
/* skip /./ */
|
||||||
if ((path_cpy[2] == '/') || (path_cpy[2] == '\0')) {
|
if ((path_cpy[2] == '/') || (path_cpy[2] == '\0')) {
|
||||||
path_cpy += 2;
|
path_cpy += 2;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* collapse /x/../ */
|
/* collapse /x/../ */
|
||||||
if ((path_cpy[2] == '.') &&
|
if ((path_cpy[2] == '.') &&
|
||||||
((path_cpy[3] == '/') || (path_cpy[3] == '\0'))) {
|
((path_cpy[3] == '/') || (path_cpy[3] == '\0'))) {
|
||||||
while ((path_res > path_resolved) && (*--path_res != '/'));
|
while ((path_res > path_resolved) && (*--path_res != '/'));
|
||||||
|
|
||||||
path_cpy += 3;
|
path_cpy += 3;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
next:
|
next:
|
||||||
*path_res++ = *path_cpy++;
|
*path_res++ = *path_cpy++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove trailing slash if not root / */
|
/* remove trailing slash if not root / */
|
||||||
if ((path_res > (path_resolved+1)) && (path_res[-1] == '/'))
|
if ((path_res > (path_resolved+1)) && (path_res[-1] == '/'))
|
||||||
path_res--;
|
path_res--;
|
||||||
else if (path_res == path_resolved)
|
else if (path_res == path_resolved)
|
||||||
*path_res++ = '/';
|
*path_res++ = '/';
|
||||||
|
|
||||||
*path_res = '\0';
|
*path_res = '\0';
|
||||||
|
|
||||||
return path_resolved;
|
return path_resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns NULL on error.
|
/* Returns NULL on error.
|
||||||
|
@ -140,266 +140,266 @@ next:
|
||||||
** NULL here causes 404 [Not Found], but that's not too unreasonable. */
|
** NULL here causes 404 [Not Found], but that's not too unreasonable. */
|
||||||
struct path_info *uh_path_lookup(struct uh_client *cl, const char *url)
|
struct path_info *uh_path_lookup(struct uh_client *cl, const char *url)
|
||||||
{
|
{
|
||||||
static char buf[PATH_MAX];
|
static char buf[PATH_MAX];
|
||||||
static char path_phys[PATH_MAX];
|
static char path_phys[PATH_MAX];
|
||||||
static char path_info[PATH_MAX];
|
static char path_info[PATH_MAX];
|
||||||
static struct path_info p;
|
static struct path_info p;
|
||||||
const char *path = cl->get_path(cl);
|
const char *path = cl->get_path(cl);
|
||||||
const char *query = cl->get_query(cl);
|
const char *query = cl->get_query(cl);
|
||||||
|
|
||||||
const char *docroot = cl->srv->docroot;
|
const char *docroot = cl->srv->docroot;
|
||||||
int docroot_len = strlen(docroot);
|
int docroot_len = strlen(docroot);
|
||||||
char *pathptr = NULL;
|
char *pathptr = NULL;
|
||||||
bool slash;
|
bool slash;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
/* back out early if url is undefined */
|
/* back out early if url is undefined */
|
||||||
if (url == NULL)
|
if (url == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
memset(&p, 0, sizeof(p));
|
memset(&p, 0, sizeof(p));
|
||||||
path_phys[0] = 0;
|
path_phys[0] = 0;
|
||||||
path_info[0] = 0;
|
path_info[0] = 0;
|
||||||
|
|
||||||
strcpy(buf, docroot);
|
strcpy(buf, docroot);
|
||||||
strcat(buf, path);
|
strcat(buf, path);
|
||||||
|
|
||||||
/* create canon path */
|
/* create canon path */
|
||||||
len = strlen(buf);
|
len = strlen(buf);
|
||||||
slash = len && buf[len - 1] == '/';
|
slash = len && buf[len - 1] == '/';
|
||||||
len = min(len, sizeof(path_phys) - 1);
|
len = min(len, sizeof(path_phys) - 1);
|
||||||
|
|
||||||
for (i = len; i >= 0; i--) {
|
for (i = len; i >= 0; i--) {
|
||||||
char ch = buf[i];
|
char ch = buf[i];
|
||||||
bool exists;
|
bool exists;
|
||||||
|
|
||||||
if (ch != 0 && ch != '/')
|
if (ch != 0 && ch != '/')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
buf[i] = 0;
|
buf[i] = 0;
|
||||||
exists = !!canonpath(buf, path_phys);
|
exists = !!canonpath(buf, path_phys);
|
||||||
buf[i] = ch;
|
buf[i] = ch;
|
||||||
|
|
||||||
if (!exists)
|
if (!exists)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* test current path */
|
/* test current path */
|
||||||
if (stat(path_phys, &p.stat))
|
if (stat(path_phys, &p.stat))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
snprintf(path_info, sizeof(path_info), "%s", buf + i);
|
snprintf(path_info, sizeof(path_info), "%s", buf + i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check whether found path is within docroot */
|
/* check whether found path is within docroot */
|
||||||
if (strncmp(path_phys, docroot, docroot_len) != 0 ||
|
if (strncmp(path_phys, docroot, docroot_len) != 0 ||
|
||||||
(path_phys[docroot_len] != 0 &&
|
(path_phys[docroot_len] != 0 &&
|
||||||
path_phys[docroot_len] != '/'))
|
path_phys[docroot_len] != '/'))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* is a regular file */
|
/* is a regular file */
|
||||||
if (p.stat.st_mode & S_IFREG) {
|
if (p.stat.st_mode & S_IFREG) {
|
||||||
p.root = docroot;
|
p.root = docroot;
|
||||||
p.phys = path_phys;
|
p.phys = path_phys;
|
||||||
p.name = &path_phys[docroot_len];
|
p.name = &path_phys[docroot_len];
|
||||||
p.info = path_info[0] ? path_info : NULL;
|
p.info = path_info[0] ? path_info : NULL;
|
||||||
return &p;
|
return &p;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(p.stat.st_mode & S_IFDIR))
|
if (!(p.stat.st_mode & S_IFDIR))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (path_info[0])
|
if (path_info[0])
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
pathptr = path_phys + strlen(path_phys);
|
pathptr = path_phys + strlen(path_phys);
|
||||||
|
|
||||||
/* ensure trailing slash */
|
/* ensure trailing slash */
|
||||||
if (pathptr[-1] != '/') {
|
if (pathptr[-1] != '/') {
|
||||||
pathptr[0] = '/';
|
pathptr[0] = '/';
|
||||||
pathptr[1] = 0;
|
pathptr[1] = 0;
|
||||||
pathptr++;
|
pathptr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if requested url resolves to a directory and a trailing slash
|
/* if requested url resolves to a directory and a trailing slash
|
||||||
is missing in the request url, redirect the client to the same
|
is missing in the request url, redirect the client to the same
|
||||||
url with trailing slash appended */
|
url with trailing slash appended */
|
||||||
if (!slash) {
|
if (!slash) {
|
||||||
cl->send_header(cl, 302, "Found", 0);
|
cl->send_header(cl, 302, "Found", 0);
|
||||||
cl->printf(cl, "Location: %s%s%s\r\n\r\n", &path_phys[docroot_len],
|
cl->printf(cl, "Location: %s%s%s\r\n\r\n", &path_phys[docroot_len],
|
||||||
query ? "?" : "", query ? query : "");
|
query ? "?" : "", query ? query : "");
|
||||||
cl->request_done(cl);
|
cl->request_done(cl);
|
||||||
p.redirected = 1;
|
p.redirected = 1;
|
||||||
return &p;
|
return &p;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* try to locate index file */
|
/* try to locate index file */
|
||||||
len = path_phys + sizeof(path_phys) - pathptr - 1;
|
len = path_phys + sizeof(path_phys) - pathptr - 1;
|
||||||
strcpy(pathptr, cl->srv->index_file);
|
strcpy(pathptr, cl->srv->index_file);
|
||||||
|
|
||||||
if (stat(path_phys, &p.stat) < 0)
|
if (stat(path_phys, &p.stat) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
p.root = docroot;
|
p.root = docroot;
|
||||||
p.phys = path_phys;
|
p.phys = path_phys;
|
||||||
p.name = &path_phys[docroot_len];
|
p.name = &path_phys[docroot_len];
|
||||||
|
|
||||||
return p.phys ? &p : NULL;
|
return p.phys ? &p : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *file_unix2date(time_t ts, char *buf, int len)
|
static char *file_unix2date(time_t ts, char *buf, int len)
|
||||||
{
|
{
|
||||||
struct tm *t = gmtime(&ts);
|
struct tm *t = gmtime(&ts);
|
||||||
|
|
||||||
strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", t);
|
strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", t);
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * uh_file_mime_lookup(const char *path)
|
static const char * uh_file_mime_lookup(const char *path)
|
||||||
{
|
{
|
||||||
const struct mimetype *m = &uh_mime_types[0];
|
const struct mimetype *m = &uh_mime_types[0];
|
||||||
const char *e;
|
const char *e;
|
||||||
|
|
||||||
while (m->extn) {
|
while (m->extn) {
|
||||||
e = &path[strlen(path)-1];
|
e = &path[strlen(path)-1];
|
||||||
|
|
||||||
while (e >= path) {
|
while (e >= path) {
|
||||||
if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn))
|
if ((*e == '.' || *e == '/') && !strcasecmp(&e[1], m->extn))
|
||||||
return m->mime;
|
return m->mime;
|
||||||
e--;
|
e--;
|
||||||
}
|
}
|
||||||
m++;
|
m++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "application/octet-stream";
|
return "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_file_response_ok_hdrs(struct uh_client *cl, struct stat *s)
|
static void uh_file_response_ok_hdrs(struct uh_client *cl, struct stat *s)
|
||||||
{
|
{
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
||||||
cl->printf(cl, "Last-Modified: %s\r\n", file_unix2date(s->st_mtime, buf, sizeof(buf)));
|
cl->printf(cl, "Last-Modified: %s\r\n", file_unix2date(s->st_mtime, buf, sizeof(buf)));
|
||||||
cl->printf(cl, "Date: %s\r\n", file_unix2date(time(NULL), buf, sizeof(buf)));
|
cl->printf(cl, "Date: %s\r\n", file_unix2date(time(NULL), buf, sizeof(buf)));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_file_response_304(struct uh_client *cl, struct stat *s)
|
static void uh_file_response_304(struct uh_client *cl, struct stat *s)
|
||||||
{
|
{
|
||||||
cl->send_header(cl, 304, "Not Modified", 0);
|
cl->send_header(cl, 304, "Not Modified", 0);
|
||||||
uh_file_response_ok_hdrs(cl, s);
|
uh_file_response_ok_hdrs(cl, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_file_response_200(struct uh_client *cl, struct stat *s)
|
static void uh_file_response_200(struct uh_client *cl, struct stat *s)
|
||||||
{
|
{
|
||||||
cl->send_header(cl, 200, "OK", s->st_size);
|
cl->send_header(cl, 200, "OK", s->st_size);
|
||||||
uh_file_response_ok_hdrs(cl, s);
|
uh_file_response_ok_hdrs(cl, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uh_file_if_modified_since(struct uh_client *cl, struct stat *s)
|
static int uh_file_if_modified_since(struct uh_client *cl, struct stat *s)
|
||||||
{
|
{
|
||||||
const char *date = kvlist_get(&cl->request.hdr, "if-modified-since");
|
const char *date = kvlist_get(&cl->request.hdr, "if-modified-since");
|
||||||
struct tm t;
|
struct tm t;
|
||||||
|
|
||||||
if (!date)
|
if (!date)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
memset(&t, 0, sizeof(t));
|
memset(&t, 0, sizeof(t));
|
||||||
|
|
||||||
if ((strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) ? timegm(&t) : 0) >= s->st_mtime) {
|
if ((strptime(date, "%a, %d %b %Y %H:%M:%S %Z", &t) ? timegm(&t) : 0) >= s->st_mtime) {
|
||||||
uh_file_response_304(cl, s);
|
uh_file_response_304(cl, s);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void file_write_cb(struct uh_client *cl)
|
static void file_write_cb(struct uh_client *cl)
|
||||||
{
|
{
|
||||||
static char buf[4096];
|
static char buf[4096];
|
||||||
int fd = cl->dispatch.file.fd;
|
int fd = cl->dispatch.file.fd;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
while (cl->us->w.data_bytes < 256) {
|
while (cl->us->w.data_bytes < 256) {
|
||||||
r = read(fd, buf, sizeof(buf));
|
r = read(fd, buf, sizeof(buf));
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
uh_log_err("read");
|
uh_log_err("read");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r <= 0) {
|
if (r <= 0) {
|
||||||
cl->request_done(cl);
|
cl->request_done(cl);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cl->send(cl, buf, r);
|
cl->send(cl, buf, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_file_free(struct uh_client *cl)
|
static void uh_file_free(struct uh_client *cl)
|
||||||
{
|
{
|
||||||
close(cl->dispatch.file.fd);
|
close(cl->dispatch.file.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_file_data(struct uh_client *cl, struct path_info *pi, int fd)
|
static void uh_file_data(struct uh_client *cl, struct path_info *pi, int fd)
|
||||||
{
|
{
|
||||||
/* test preconditions */
|
/* test preconditions */
|
||||||
if ((!uh_file_if_modified_since(cl, &pi->stat))) {
|
if ((!uh_file_if_modified_since(cl, &pi->stat))) {
|
||||||
cl->printf(cl, "\r\n");
|
cl->printf(cl, "\r\n");
|
||||||
cl->request_done(cl);
|
cl->request_done(cl);
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write status */
|
/* write status */
|
||||||
uh_file_response_200(cl, &pi->stat);
|
uh_file_response_200(cl, &pi->stat);
|
||||||
|
|
||||||
cl->printf(cl, "Content-Type: %s\r\n\r\n", uh_file_mime_lookup(pi->name));
|
cl->printf(cl, "Content-Type: %s\r\n\r\n", uh_file_mime_lookup(pi->name));
|
||||||
|
|
||||||
/* send header */
|
/* send header */
|
||||||
if (cl->request.method == UH_HTTP_MSG_HEAD) {
|
if (cl->request.method == UH_HTTP_MSG_HEAD) {
|
||||||
cl->request_done(cl);
|
cl->request_done(cl);
|
||||||
close(fd);
|
close(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cl->state = CLIENT_STATE_DONE;
|
cl->state = CLIENT_STATE_DONE;
|
||||||
|
|
||||||
cl->dispatch.file.fd = fd;
|
cl->dispatch.file.fd = fd;
|
||||||
cl->dispatch.write_cb = file_write_cb;
|
cl->dispatch.write_cb = file_write_cb;
|
||||||
cl->dispatch.free = uh_file_free;
|
cl->dispatch.free = uh_file_free;
|
||||||
file_write_cb(cl);
|
file_write_cb(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uh_file_request(struct uh_client *cl, const char *path, struct path_info *pi)
|
static void uh_file_request(struct uh_client *cl, const char *path, struct path_info *pi)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
if (!(pi->stat.st_mode & S_IROTH))
|
if (!(pi->stat.st_mode & S_IROTH))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
if (pi->stat.st_mode & S_IFREG) {
|
if (pi->stat.st_mode & S_IFREG) {
|
||||||
fd = open(pi->phys, O_RDONLY);
|
fd = open(pi->phys, O_RDONLY);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
uh_file_data(cl, pi, fd);
|
uh_file_data(cl, pi, fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
error:
|
error:
|
||||||
cl->send_error(cl, 403, "Forbidden", "You don't have permission to access %s on this server.", path);
|
cl->send_error(cl, 403, "Forbidden", "You don't have permission to access %s on this server.", path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handle_file_request(struct uh_client *cl, const char *path)
|
bool handle_file_request(struct uh_client *cl, const char *path)
|
||||||
{
|
{
|
||||||
struct path_info *pi;
|
struct path_info *pi;
|
||||||
|
|
||||||
pi = uh_path_lookup(cl, path);
|
pi = uh_path_lookup(cl, path);
|
||||||
if (!pi)
|
if (!pi)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uh_log_debug("pi->root: %s", pi->root);
|
uh_log_debug("pi->root: %s", pi->root);
|
||||||
uh_log_debug("pi->phys: %s", pi->phys);
|
uh_log_debug("pi->phys: %s", pi->phys);
|
||||||
|
@ -407,11 +407,11 @@ bool handle_file_request(struct uh_client *cl, const char *path)
|
||||||
uh_log_debug("pi->info: %s", pi->info);
|
uh_log_debug("pi->info: %s", pi->info);
|
||||||
uh_log_debug("pi->redirected: %d", pi->redirected);
|
uh_log_debug("pi->redirected: %d", pi->redirected);
|
||||||
|
|
||||||
if (pi->redirected)
|
if (pi->redirected)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
uh_file_request(cl, path, pi);
|
uh_file_request(cl, path, pi);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
src/file.h
16
src/file.h
|
@ -21,17 +21,17 @@
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
|
||||||
struct path_info {
|
struct path_info {
|
||||||
const char *root;
|
const char *root;
|
||||||
const char *phys;
|
const char *phys;
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *info;
|
const char *info;
|
||||||
bool redirected;
|
bool redirected;
|
||||||
struct stat stat;
|
struct stat stat;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mimetype {
|
struct mimetype {
|
||||||
const char *extn;
|
const char *extn;
|
||||||
const char *mime;
|
const char *mime;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool handle_file_request(struct uh_client *cl, const char *path);
|
bool handle_file_request(struct uh_client *cl, const char *path);
|
||||||
|
|
84
src/uh_ssl.c
84
src/uh_ssl.c
|
@ -27,79 +27,79 @@ static void *ctx;
|
||||||
|
|
||||||
int uh_ssl_init(struct uh_server *srv, const char *key, const char *crt)
|
int uh_ssl_init(struct uh_server *srv, const char *key, const char *crt)
|
||||||
{
|
{
|
||||||
srv->ssl = true;
|
srv->ssl = true;
|
||||||
|
|
||||||
if (_init)
|
if (_init)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
_init = true;
|
_init = true;
|
||||||
dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL);
|
dlh = dlopen("libustream-ssl.so", RTLD_LAZY | RTLD_LOCAL);
|
||||||
if (!dlh) {
|
if (!dlh) {
|
||||||
uh_log_err("Failed to load ustream-ssl library: %s", dlerror());
|
uh_log_err("Failed to load ustream-ssl library: %s", dlerror());
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ops = dlsym(dlh, "ustream_ssl_ops");
|
ops = dlsym(dlh, "ustream_ssl_ops");
|
||||||
if (!ops) {
|
if (!ops) {
|
||||||
uh_log_err("Could not find required symbol 'ustream_ssl_ops' in ustream-ssl library");
|
uh_log_err("Could not find required symbol 'ustream_ssl_ops' in ustream-ssl library");
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = ops->context_new(true);
|
ctx = ops->context_new(true);
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
uh_log_err("Failed to initialize ustream-ssl");
|
uh_log_err("Failed to initialize ustream-ssl");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ops->context_set_crt_file(ctx, crt) ||
|
if (ops->context_set_crt_file(ctx, crt) ||
|
||||||
ops->context_set_key_file(ctx, key)) {
|
ops->context_set_key_file(ctx, key)) {
|
||||||
uh_log_err("Failed to load certificate/key files");
|
uh_log_err("Failed to load certificate/key files");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_ssl_free()
|
void uh_ssl_free()
|
||||||
{
|
{
|
||||||
if (_init) {
|
if (_init) {
|
||||||
_init = false;
|
_init = false;
|
||||||
ops->context_free(ctx);
|
ops->context_free(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssl_ustream_read_cb(struct ustream *s, int bytes)
|
static void ssl_ustream_read_cb(struct ustream *s, int bytes)
|
||||||
{
|
{
|
||||||
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
|
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
|
||||||
|
|
||||||
uh_client_read_cb(cl);
|
uh_client_read_cb(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssl_ustream_write_cb(struct ustream *s, int bytes)
|
static void ssl_ustream_write_cb(struct ustream *s, int bytes)
|
||||||
{
|
{
|
||||||
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
|
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
|
||||||
|
|
||||||
if (cl->dispatch.write_cb)
|
if (cl->dispatch.write_cb)
|
||||||
cl->dispatch.write_cb(cl);
|
cl->dispatch.write_cb(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ssl_notify_state(struct ustream *s)
|
static void ssl_notify_state(struct ustream *s)
|
||||||
{
|
{
|
||||||
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
|
struct uh_client *cl = container_of(s, struct uh_client, ssl.stream);
|
||||||
|
|
||||||
uh_client_notify_state(cl);
|
uh_client_notify_state(cl);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_ssl_client_attach(struct uh_client *cl)
|
void uh_ssl_client_attach(struct uh_client *cl)
|
||||||
{
|
{
|
||||||
cl->us = &cl->ssl.stream;
|
cl->us = &cl->ssl.stream;
|
||||||
ops->init(&cl->ssl, &cl->sfd.stream, ctx, true);
|
ops->init(&cl->ssl, &cl->sfd.stream, ctx, true);
|
||||||
cl->us->notify_read = ssl_ustream_read_cb;
|
cl->us->notify_read = ssl_ustream_read_cb;
|
||||||
cl->us->notify_write = ssl_ustream_write_cb;
|
cl->us->notify_write = ssl_ustream_write_cb;
|
||||||
cl->us->notify_state = ssl_notify_state;
|
cl->us->notify_state = ssl_notify_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_ssl_client_detach(struct uh_client *cl)
|
void uh_ssl_client_detach(struct uh_client *cl)
|
||||||
{
|
{
|
||||||
ustream_free(&cl->ssl.stream);
|
ustream_free(&cl->ssl.stream);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ void uh_ssl_client_detach(struct uh_client *cl);
|
||||||
|
|
||||||
static inline int uh_ssl_init(const char *key, const char *crt)
|
static inline int uh_ssl_init(const char *key, const char *crt)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void uh_ssl_free()
|
static inline void uh_ssl_free()
|
||||||
|
|
|
@ -23,14 +23,14 @@
|
||||||
|
|
||||||
struct uh_server {
|
struct uh_server {
|
||||||
bool ssl;
|
bool ssl;
|
||||||
struct uloop_fd fd;
|
struct uloop_fd fd;
|
||||||
char *docroot;
|
char *docroot;
|
||||||
char *index_file;
|
char *index_file;
|
||||||
int nclients;
|
int nclients;
|
||||||
struct avl_tree actions;
|
struct avl_tree actions;
|
||||||
struct list_head clients;
|
struct list_head clients;
|
||||||
|
|
||||||
void (*free)(struct uh_server *srv);
|
void (*free)(struct uh_server *srv);
|
||||||
void (*set_docroot)(struct uh_server *srv, const char *docroot);
|
void (*set_docroot)(struct uh_server *srv, const char *docroot);
|
||||||
void (*set_index_file)(struct uh_server *srv, const char *index_file);
|
void (*set_index_file)(struct uh_server *srv, const char *index_file);
|
||||||
void (*error404_cb)(struct uh_client *cl);
|
void (*error404_cb)(struct uh_client *cl);
|
||||||
|
|
150
src/utils.c
150
src/utils.c
|
@ -19,12 +19,12 @@
|
||||||
|
|
||||||
void uh_printf(struct uh_client *cl, const char *format, ...)
|
void uh_printf(struct uh_client *cl, const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list arg;
|
va_list arg;
|
||||||
|
|
||||||
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
|
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
|
||||||
va_start(arg, format);
|
va_start(arg, format);
|
||||||
ustream_vprintf(cl->us, format, arg);
|
ustream_vprintf(cl->us, format, arg);
|
||||||
va_end(arg);
|
va_end(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_vprintf(struct uh_client *cl, const char *format, va_list arg)
|
void uh_vprintf(struct uh_client *cl, const char *format, va_list arg)
|
||||||
|
@ -35,59 +35,59 @@ void uh_vprintf(struct uh_client *cl, const char *format, va_list arg)
|
||||||
|
|
||||||
void uh_chunk_send(struct uh_client *cl, const void *data, int len)
|
void uh_chunk_send(struct uh_client *cl, const void *data, int len)
|
||||||
{
|
{
|
||||||
struct ustream *us = cl->us;
|
struct ustream *us = cl->us;
|
||||||
|
|
||||||
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
|
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
|
||||||
ustream_printf(us, "%X\r\n", len);
|
ustream_printf(us, "%X\r\n", len);
|
||||||
ustream_write(us, data, len, true);
|
ustream_write(us, data, len, true);
|
||||||
ustream_printf(us, "\r\n", len);
|
ustream_printf(us, "\r\n", len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_chunk_printf(struct uh_client *cl, const char *format, ...)
|
void uh_chunk_printf(struct uh_client *cl, const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list arg;
|
va_list arg;
|
||||||
|
|
||||||
va_start(arg, format);
|
va_start(arg, format);
|
||||||
uh_chunk_vprintf(cl, format, arg);
|
uh_chunk_vprintf(cl, format, arg);
|
||||||
va_end(arg);
|
va_end(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void uh_chunk_vprintf(struct uh_client *cl, const char *format, va_list arg)
|
void uh_chunk_vprintf(struct uh_client *cl, const char *format, va_list arg)
|
||||||
{
|
{
|
||||||
struct ustream *us = cl->us;
|
struct ustream *us = cl->us;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
va_list arg2;
|
va_list arg2;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
|
uloop_timeout_set(&cl->timeout, UHTTPD_CONNECTION_TIMEOUT * 1000);
|
||||||
|
|
||||||
va_copy(arg2, arg);
|
va_copy(arg2, arg);
|
||||||
len = vsnprintf(buf, sizeof(buf), format, arg2);
|
len = vsnprintf(buf, sizeof(buf), format, arg2);
|
||||||
va_end(arg2);
|
va_end(arg2);
|
||||||
|
|
||||||
ustream_printf(us, "%X\r\n", len);
|
ustream_printf(us, "%X\r\n", len);
|
||||||
if (len < sizeof(buf))
|
if (len < sizeof(buf))
|
||||||
ustream_write(cl->us, buf, len, true);
|
ustream_write(cl->us, buf, len, true);
|
||||||
else
|
else
|
||||||
ustream_vprintf(cl->us, format, arg);
|
ustream_vprintf(cl->us, format, arg);
|
||||||
ustream_printf(us, "\r\n", len);
|
ustream_printf(us, "\r\n", len);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *uh_split_header(char *str)
|
char *uh_split_header(char *str)
|
||||||
{
|
{
|
||||||
char *val;
|
char *val;
|
||||||
|
|
||||||
val = strchr(str, ':');
|
val = strchr(str, ':');
|
||||||
if (!val)
|
if (!val)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
*val = 0;
|
*val = 0;
|
||||||
val++;
|
val++;
|
||||||
|
|
||||||
while (isspace(*val))
|
while (isspace(*val))
|
||||||
val++;
|
val++;
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* blen is the size of buf; slen is the length of src. The input-string need
|
/* blen is the size of buf; slen is the length of src. The input-string need
|
||||||
|
@ -95,30 +95,30 @@ char *uh_split_header(char *str)
|
||||||
** length of the decoded string, -1 on buffer overflow, -2 on malformed string. */
|
** length of the decoded string, -1 on buffer overflow, -2 on malformed string. */
|
||||||
int uh_urldecode(char *buf, int blen, const char *src, int slen)
|
int uh_urldecode(char *buf, int blen, const char *src, int slen)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
|
||||||
#define hex(x) \
|
#define hex(x) \
|
||||||
(((x) <= '9') ? ((x) - '0') : \
|
(((x) <= '9') ? ((x) - '0') : \
|
||||||
(((x) <= 'F') ? ((x) - 'A' + 10) : \
|
(((x) <= 'F') ? ((x) - 'A' + 10) : \
|
||||||
((x) - 'a' + 10)))
|
((x) - 'a' + 10)))
|
||||||
|
|
||||||
for (i = 0; (i < slen) && (len < blen); i++)
|
for (i = 0; (i < slen) && (len < blen); i++)
|
||||||
{
|
{
|
||||||
if (src[i] != '%') {
|
if (src[i] != '%') {
|
||||||
buf[len++] = src[i];
|
buf[len++] = src[i];
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i + 2 >= slen || !isxdigit(src[i + 1]) || !isxdigit(src[i + 2]))
|
if (i + 2 >= slen || !isxdigit(src[i + 1]) || !isxdigit(src[i + 2]))
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
|
buf[len++] = (char)(16 * hex(src[i+1]) + hex(src[i+2]));
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
buf[len] = 0;
|
buf[len] = 0;
|
||||||
|
|
||||||
return (i == slen) ? len : -1;
|
return (i == slen) ? len : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* blen is the size of buf; slen is the length of src. The input-string need
|
/* blen is the size of buf; slen is the length of src. The input-string need
|
||||||
|
@ -126,31 +126,31 @@ int uh_urldecode(char *buf, int blen, const char *src, int slen)
|
||||||
** length of the encoded string, or -1 on error (buffer overflow) */
|
** length of the encoded string, or -1 on error (buffer overflow) */
|
||||||
int uh_urlencode(char *buf, int blen, const char *src, int slen)
|
int uh_urlencode(char *buf, int blen, const char *src, int slen)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
static const char hex[] = "0123456789abcdef";
|
static const char hex[] = "0123456789abcdef";
|
||||||
|
|
||||||
for (i = 0; (i < slen) && (len < blen); i++)
|
for (i = 0; (i < slen) && (len < blen); i++)
|
||||||
{
|
{
|
||||||
if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
|
if( isalnum(src[i]) || (src[i] == '-') || (src[i] == '_') ||
|
||||||
(src[i] == '.') || (src[i] == '~') )
|
(src[i] == '.') || (src[i] == '~') )
|
||||||
{
|
{
|
||||||
buf[len++] = src[i];
|
buf[len++] = src[i];
|
||||||
}
|
}
|
||||||
else if ((len+3) <= blen)
|
else if ((len+3) <= blen)
|
||||||
{
|
{
|
||||||
buf[len++] = '%';
|
buf[len++] = '%';
|
||||||
buf[len++] = hex[(src[i] >> 4) & 15];
|
buf[len++] = hex[(src[i] >> 4) & 15];
|
||||||
buf[len++] = hex[ src[i] & 15];
|
buf[len++] = hex[ src[i] & 15];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
len = -1;
|
len = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (i == slen) ? len : -1;
|
return (i == slen) ? len : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int find_idx(const char *const *list, int max, const char *str)
|
int find_idx(const char *const *list, int max, const char *str)
|
||||||
|
|
Loading…
Reference in New Issue