patch: added HTTP parser
This commit is contained in:
parent
6848aa73e6
commit
dbf659576f
8 changed files with 1358 additions and 2 deletions
2
Makefile
2
Makefile
|
@ -18,7 +18,7 @@ include $(MK)
|
|||
|
||||
# List of modules
|
||||
MODULES += sip sipevent sipreg sipsess
|
||||
MODULES += uri httpauth
|
||||
MODULES += uri http httpauth
|
||||
MODULES += stun turn ice
|
||||
MODULES += natbd
|
||||
MODULES += rtp sdp jbuf telev
|
||||
|
|
|
@ -25,6 +25,7 @@ extern "C" {
|
|||
#include "re_crc32.h"
|
||||
#include "re_dns.h"
|
||||
#include "re_hash.h"
|
||||
#include "re_http.h"
|
||||
#include "re_httpauth.h"
|
||||
#include "re_ice.h"
|
||||
#include "re_jbuf.h"
|
||||
|
|
149
include/re_http.h
Normal file
149
include/re_http.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* @file re_http.h Hypertext Transfer Protocol
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
|
||||
|
||||
/** HTTP Header ID (perfect hash value) */
|
||||
enum http_hdrid {
|
||||
HTTP_HDR_ACCEPT = 3186,
|
||||
HTTP_HDR_ACCEPT_CHARSET = 24,
|
||||
HTTP_HDR_ACCEPT_ENCODING = 708,
|
||||
HTTP_HDR_ACCEPT_LANGUAGE = 2867,
|
||||
HTTP_HDR_ACCEPT_RANGES = 3027,
|
||||
HTTP_HDR_AGE = 742,
|
||||
HTTP_HDR_ALLOW = 2429,
|
||||
HTTP_HDR_AUTHORIZATION = 2503,
|
||||
HTTP_HDR_CACHE_CONTROL = 2530,
|
||||
HTTP_HDR_CONNECTION = 865,
|
||||
HTTP_HDR_CONTENT_ENCODING = 580,
|
||||
HTTP_HDR_CONTENT_LANGUAGE = 3371,
|
||||
HTTP_HDR_CONTENT_LENGTH = 3861,
|
||||
HTTP_HDR_CONTENT_LOCATION = 3927,
|
||||
HTTP_HDR_CONTENT_MD5 = 406,
|
||||
HTTP_HDR_CONTENT_RANGE = 2846,
|
||||
HTTP_HDR_CONTENT_TYPE = 809,
|
||||
HTTP_HDR_DATE = 1027,
|
||||
HTTP_HDR_ETAG = 2392,
|
||||
HTTP_HDR_EXPECT = 1550,
|
||||
HTTP_HDR_EXPIRES = 1983,
|
||||
HTTP_HDR_FROM = 1963,
|
||||
HTTP_HDR_HOST = 3191,
|
||||
HTTP_HDR_IF_MATCH = 2684,
|
||||
HTTP_HDR_IF_MODIFIED_SINCE = 2187,
|
||||
HTTP_HDR_IF_NONE_MATCH = 4030,
|
||||
HTTP_HDR_IF_RANGE = 2220,
|
||||
HTTP_HDR_IF_UNMODIFIED_SINCE = 962,
|
||||
HTTP_HDR_LAST_MODIFIED = 2946,
|
||||
HTTP_HDR_LOCATION = 2514,
|
||||
HTTP_HDR_MAX_FORWARDS = 3549,
|
||||
HTTP_HDR_PRAGMA = 1673,
|
||||
HTTP_HDR_PROXY_AUTHENTICATE = 116,
|
||||
HTTP_HDR_PROXY_AUTHORIZATION = 2363,
|
||||
HTTP_HDR_RANGE = 4004,
|
||||
HTTP_HDR_REFERER = 2991,
|
||||
HTTP_HDR_RETRY_AFTER = 409,
|
||||
HTTP_HDR_SEC_WEBSOCKET_ACCEPT = 2959,
|
||||
HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS = 2937,
|
||||
HTTP_HDR_SEC_WEBSOCKET_KEY = 746,
|
||||
HTTP_HDR_SEC_WEBSOCKET_PROTOCOL = 2076,
|
||||
HTTP_HDR_SEC_WEBSOCKET_VERSION = 3158,
|
||||
HTTP_HDR_SERVER = 973,
|
||||
HTTP_HDR_TE = 2035,
|
||||
HTTP_HDR_TRAILER = 2577,
|
||||
HTTP_HDR_TRANSFER_ENCODING = 2115,
|
||||
HTTP_HDR_UPGRADE = 717,
|
||||
HTTP_HDR_USER_AGENT = 4064,
|
||||
HTTP_HDR_VARY = 3076,
|
||||
HTTP_HDR_VIA = 3961,
|
||||
HTTP_HDR_WARNING = 2108,
|
||||
HTTP_HDR_WWW_AUTHENTICATE = 2763,
|
||||
|
||||
HTTP_HDR_NONE = -1
|
||||
};
|
||||
|
||||
|
||||
/** HTTP Header */
|
||||
struct http_hdr {
|
||||
struct le le;
|
||||
struct pl name;
|
||||
struct pl val;
|
||||
enum http_hdrid id;
|
||||
};
|
||||
|
||||
/** HTTP Message */
|
||||
struct http_msg {
|
||||
struct pl ver;
|
||||
struct pl met;
|
||||
struct pl path;
|
||||
struct pl prm;
|
||||
uint16_t scode;
|
||||
struct pl reason;
|
||||
struct list hdrl;
|
||||
struct pl ctype;
|
||||
struct mbuf *mb;
|
||||
uint32_t clen;
|
||||
};
|
||||
|
||||
typedef bool(http_hdr_h)(const struct http_hdr *hdr, void *arg);
|
||||
|
||||
int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req);
|
||||
|
||||
|
||||
const struct http_hdr *http_msg_hdr(const struct http_msg *msg,
|
||||
enum http_hdrid id);
|
||||
const struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg,
|
||||
bool fwd, enum http_hdrid id,
|
||||
http_hdr_h *h, void *arg);
|
||||
const struct http_hdr *http_msg_xhdr(const struct http_msg *msg,
|
||||
const char *name);
|
||||
const struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg,
|
||||
bool fwd, const char *name,
|
||||
http_hdr_h *h, void *arg);
|
||||
uint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id);
|
||||
uint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name);
|
||||
bool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id,
|
||||
const char *value);
|
||||
bool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name,
|
||||
const char *value);
|
||||
int http_msg_print(struct re_printf *pf, const struct http_msg *msg);
|
||||
|
||||
|
||||
/* Server */
|
||||
struct http_sock;
|
||||
struct http_conn;
|
||||
|
||||
typedef void (http_req_h)(struct http_conn *conn, const struct http_msg *msg,
|
||||
void *arg);
|
||||
|
||||
int http_listen(struct http_sock **sockp, const struct sa *laddr,
|
||||
http_req_h *reqh, void *arg);
|
||||
int https_listen(struct http_sock **sockp, const struct sa *laddr,
|
||||
const char *cert, http_req_h *reqh, void *arg);
|
||||
const struct sa *http_conn_peer(const struct http_conn *conn);
|
||||
struct tcp_conn *http_conn_tcp(struct http_conn *conn);
|
||||
struct tls_conn *http_conn_tls(struct http_conn *conn);
|
||||
void http_conn_close(struct http_conn *conn);
|
||||
int http_reply(struct http_conn *conn, uint16_t scode, const char *reason,
|
||||
const char *fmt, ...);
|
||||
int http_creply(struct http_conn *conn, uint16_t scode, const char *reason,
|
||||
const char *ctype, const char *fmt, ...);
|
||||
int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason);
|
||||
|
||||
|
||||
/* Authentication */
|
||||
struct http_auth {
|
||||
const char *realm;
|
||||
bool stale;
|
||||
};
|
||||
|
||||
typedef int (http_auth_h)(const struct pl *username, uint8_t *ha1, void *arg);
|
||||
|
||||
int http_auth_print_challenge(struct re_printf *pf,
|
||||
const struct http_auth *auth);
|
||||
bool http_auth_check(const struct pl *hval, const struct pl *method,
|
||||
struct http_auth *auth, http_auth_h *authh, void *arg);
|
||||
bool http_auth_check_request(const struct http_msg *msg,
|
||||
struct http_auth *auth,
|
||||
http_auth_h *authh, void *arg);
|
166
src/http/auth.c
Normal file
166
src/http/auth.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/**
|
||||
* @file http/auth.c HTTP Authentication
|
||||
*
|
||||
* Copyright (C) 2011 Creytiv.com
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <re_types.h>
|
||||
#include <re_sys.h>
|
||||
#include <re_md5.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_list.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_httpauth.h>
|
||||
#include <re_http.h>
|
||||
|
||||
|
||||
enum {
|
||||
NONCE_EXPIRES = 300,
|
||||
NONCE_MIN_SIZE = 33,
|
||||
};
|
||||
|
||||
|
||||
static uint64_t secret;
|
||||
static bool secret_set;
|
||||
|
||||
|
||||
/**
|
||||
* Print HTTP digest authentication challenge
|
||||
*
|
||||
* @param pf Print function for output
|
||||
* @param auth Authentication parameteres
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int http_auth_print_challenge(struct re_printf *pf,
|
||||
const struct http_auth *auth)
|
||||
{
|
||||
uint8_t key[MD5_SIZE];
|
||||
uint64_t nv[2];
|
||||
|
||||
if (!auth)
|
||||
return 0;
|
||||
|
||||
if (!secret_set) {
|
||||
secret = rand_u64();
|
||||
secret_set = true;
|
||||
}
|
||||
|
||||
nv[0] = time(NULL);
|
||||
nv[1] = secret;
|
||||
|
||||
md5((uint8_t *)nv, sizeof(nv), key);
|
||||
|
||||
return re_hprintf(pf,
|
||||
"Digest realm=\"%s\", nonce=\"%w%llx\", "
|
||||
"qop=\"auth\"%s",
|
||||
auth->realm,
|
||||
key, sizeof(key), nv[0],
|
||||
auth->stale ? ", stale=true" : "");
|
||||
}
|
||||
|
||||
|
||||
static int chk_nonce(const struct pl *nonce, uint32_t expires)
|
||||
{
|
||||
uint8_t nkey[MD5_SIZE], ckey[MD5_SIZE];
|
||||
uint64_t nv[2];
|
||||
struct pl pl;
|
||||
int64_t age;
|
||||
unsigned i;
|
||||
|
||||
if (!nonce || !nonce->p || nonce->l < NONCE_MIN_SIZE)
|
||||
return EINVAL;
|
||||
|
||||
pl = *nonce;
|
||||
|
||||
for (i=0; i<sizeof(nkey); i++) {
|
||||
nkey[i] = ch_hex(*pl.p++) << 4;
|
||||
nkey[i] += ch_hex(*pl.p++);
|
||||
pl.l -= 2;
|
||||
}
|
||||
|
||||
nv[0] = pl_x64(&pl);
|
||||
nv[1] = secret;
|
||||
|
||||
md5((uint8_t *)nv, sizeof(nv), ckey);
|
||||
|
||||
if (memcmp(nkey, ckey, MD5_SIZE))
|
||||
return EAUTH;
|
||||
|
||||
age = time(NULL) - nv[0];
|
||||
|
||||
if (age < 0 || age > expires)
|
||||
return ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check HTTP digest authorization
|
||||
*
|
||||
* @param hval Authorization header value
|
||||
* @param method Request method
|
||||
* @param auth Authentication parameteres
|
||||
* @param authh Authentication handler
|
||||
* @param arg Authentication handler argument
|
||||
*
|
||||
* @return true if check is passed, otherwise false
|
||||
*/
|
||||
bool http_auth_check(const struct pl *hval, const struct pl *method,
|
||||
struct http_auth *auth, http_auth_h *authh, void *arg)
|
||||
{
|
||||
struct httpauth_digest_resp resp;
|
||||
uint8_t ha1[MD5_SIZE];
|
||||
|
||||
if (!hval || !method || !auth || !authh)
|
||||
return false;
|
||||
|
||||
if (httpauth_digest_response_decode(&resp, hval))
|
||||
return false;
|
||||
|
||||
if (pl_strcasecmp(&resp.realm, auth->realm))
|
||||
return false;
|
||||
|
||||
if (chk_nonce(&resp.nonce, NONCE_EXPIRES)) {
|
||||
auth->stale = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (authh(&resp.username, ha1, arg))
|
||||
return false;
|
||||
|
||||
if (httpauth_digest_response_auth(&resp, method, ha1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check HTTP digest authorization of an HTTP request
|
||||
*
|
||||
* @param msg HTTP message
|
||||
* @param auth Authentication parameteres
|
||||
* @param authh Authentication handler
|
||||
* @param arg Authentication handler argument
|
||||
*
|
||||
* @return true if check is passed, otherwise false
|
||||
*/
|
||||
bool http_auth_check_request(const struct http_msg *msg,
|
||||
struct http_auth *auth,
|
||||
http_auth_h *authh, void *arg)
|
||||
{
|
||||
const struct http_hdr *hdr;
|
||||
|
||||
if (!msg)
|
||||
return false;
|
||||
|
||||
hdr = http_msg_hdr(msg, HTTP_HDR_AUTHORIZATION);
|
||||
if (!hdr)
|
||||
return false;
|
||||
|
||||
return http_auth_check(&hdr->val, &msg->met, auth, authh, arg);
|
||||
}
|
9
src/http/mod.mk
Normal file
9
src/http/mod.mk
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# mod.mk
|
||||
#
|
||||
# Copyright (C) 2010 Creytiv.com
|
||||
#
|
||||
|
||||
SRCS += http/auth.c
|
||||
SRCS += http/msg.c
|
||||
SRCS += http/server.c
|
527
src/http/msg.c
Normal file
527
src/http/msg.c
Normal file
|
@ -0,0 +1,527 @@
|
|||
/**
|
||||
* @file http/msg.c HTTP Message decode
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
#include <re_types.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_list.h>
|
||||
#include <re_hash.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_http.h>
|
||||
|
||||
|
||||
enum {
|
||||
STARTLINE_MAX = 8192,
|
||||
};
|
||||
|
||||
|
||||
static void hdr_destructor(void *arg)
|
||||
{
|
||||
struct http_hdr *hdr = arg;
|
||||
|
||||
list_unlink(&hdr->le);
|
||||
}
|
||||
|
||||
|
||||
static void destructor(void *arg)
|
||||
{
|
||||
struct http_msg *msg = arg;
|
||||
|
||||
list_flush(&msg->hdrl);
|
||||
mem_deref(msg->mb);
|
||||
}
|
||||
|
||||
|
||||
static enum http_hdrid hdr_hash(const struct pl *name)
|
||||
{
|
||||
if (!name->l)
|
||||
return HTTP_HDR_NONE;
|
||||
|
||||
switch (name->p[0]) {
|
||||
|
||||
case 'x':
|
||||
case 'X':
|
||||
if (name->l > 1 && name->p[1] == '-')
|
||||
return HTTP_HDR_NONE;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return (enum http_hdrid)(hash_joaat_ci(name->p, name->l) & 0xfff);
|
||||
}
|
||||
|
||||
|
||||
static inline bool hdr_comma_separated(enum http_hdrid id)
|
||||
{
|
||||
switch (id) {
|
||||
|
||||
case HTTP_HDR_ACCEPT:
|
||||
case HTTP_HDR_ACCEPT_CHARSET:
|
||||
case HTTP_HDR_ACCEPT_ENCODING:
|
||||
case HTTP_HDR_ACCEPT_LANGUAGE:
|
||||
case HTTP_HDR_ACCEPT_RANGES:
|
||||
case HTTP_HDR_ALLOW:
|
||||
case HTTP_HDR_CACHE_CONTROL:
|
||||
case HTTP_HDR_CONNECTION:
|
||||
case HTTP_HDR_CONTENT_ENCODING:
|
||||
case HTTP_HDR_CONTENT_LANGUAGE:
|
||||
case HTTP_HDR_EXPECT:
|
||||
case HTTP_HDR_IF_MATCH:
|
||||
case HTTP_HDR_IF_NONE_MATCH:
|
||||
case HTTP_HDR_PRAGMA:
|
||||
case HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS:
|
||||
case HTTP_HDR_SEC_WEBSOCKET_PROTOCOL:
|
||||
case HTTP_HDR_SEC_WEBSOCKET_VERSION:
|
||||
case HTTP_HDR_TE:
|
||||
case HTTP_HDR_TRAILER:
|
||||
case HTTP_HDR_TRANSFER_ENCODING:
|
||||
case HTTP_HDR_UPGRADE:
|
||||
case HTTP_HDR_VARY:
|
||||
case HTTP_HDR_VIA:
|
||||
case HTTP_HDR_WARNING:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline int hdr_add(struct http_msg *msg, const struct pl *name,
|
||||
enum http_hdrid id, const char *p, ssize_t l)
|
||||
{
|
||||
struct http_hdr *hdr;
|
||||
|
||||
hdr = mem_zalloc(sizeof(*hdr), hdr_destructor);
|
||||
if (!hdr)
|
||||
return ENOMEM;
|
||||
|
||||
hdr->name = *name;
|
||||
hdr->val.p = p;
|
||||
hdr->val.l = MAX(l, 0);
|
||||
hdr->id = id;
|
||||
|
||||
list_append(&msg->hdrl, &hdr->le, hdr);
|
||||
|
||||
/* parse common headers */
|
||||
switch (id) {
|
||||
|
||||
case HTTP_HDR_CONTENT_TYPE:
|
||||
msg->ctype = hdr->val;
|
||||
break;
|
||||
|
||||
case HTTP_HDR_CONTENT_LENGTH:
|
||||
msg->clen = pl_u32(&hdr->val);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode a HTTP message
|
||||
*
|
||||
* @param msgp Pointer to allocated HTTP Message
|
||||
* @param mb Buffer containing HTTP Message
|
||||
* @param req True for request, false for response
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req)
|
||||
{
|
||||
struct pl b, s, e, name, scode;
|
||||
const char *p, *cv;
|
||||
struct http_msg *msg;
|
||||
bool comsep, quote;
|
||||
enum http_hdrid id = HTTP_HDR_NONE;
|
||||
uint32_t ws, lf;
|
||||
size_t l;
|
||||
int err;
|
||||
|
||||
if (!msgp || !mb)
|
||||
return EINVAL;
|
||||
|
||||
p = (const char *)mbuf_buf(mb);
|
||||
l = mbuf_get_left(mb);
|
||||
|
||||
if (re_regex(p, l, "[\r\n]*[^\r\n]+[\r]*[\n]1", &b, &s, NULL, &e))
|
||||
return (l > STARTLINE_MAX) ? EBADMSG : ENODATA;
|
||||
|
||||
msg = mem_zalloc(sizeof(*msg), destructor);
|
||||
if (!msg)
|
||||
return ENOMEM;
|
||||
|
||||
msg->mb = mem_ref(mb);
|
||||
|
||||
if (req) {
|
||||
if (re_regex(s.p, s.l, "[a-z]+ [^? ]+[^ ]* HTTP/[0-9.]+",
|
||||
&msg->met, &msg->path, &msg->prm, &msg->ver) ||
|
||||
msg->met.p != s.p) {
|
||||
err = EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (re_regex(s.p, s.l, "HTTP/[0-9.]+ [0-9]+ [^]*",
|
||||
&msg->ver, &scode, &msg->reason) ||
|
||||
msg->ver.p != s.p + 5) {
|
||||
err = EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
msg->scode = pl_u32(&scode);
|
||||
}
|
||||
|
||||
l -= e.p + e.l - p;
|
||||
p = e.p + e.l;
|
||||
|
||||
name.p = cv = NULL;
|
||||
name.l = ws = lf = 0;
|
||||
comsep = false;
|
||||
quote = false;
|
||||
|
||||
for (; l > 0; p++, l--) {
|
||||
|
||||
switch (*p) {
|
||||
|
||||
case ' ':
|
||||
case '\t':
|
||||
lf = 0; /* folding */
|
||||
++ws;
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
++ws;
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
++ws;
|
||||
|
||||
if (!name.p) {
|
||||
++p; --l; /* no headers */
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!lf++)
|
||||
break;
|
||||
|
||||
++p; --l; /* eoh */
|
||||
|
||||
/*@fallthrough@*/
|
||||
|
||||
default:
|
||||
if (lf || (*p == ',' && comsep && !quote)) {
|
||||
|
||||
if (!name.l) {
|
||||
err = EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = hdr_add(msg, &name, id, cv ? cv : p,
|
||||
cv ? p - cv - ws : 0);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (!lf) { /* comma separated */
|
||||
cv = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (lf > 1) { /* eoh */
|
||||
err = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
comsep = false;
|
||||
name.p = NULL;
|
||||
cv = NULL;
|
||||
lf = 0;
|
||||
}
|
||||
|
||||
if (!name.p) {
|
||||
name.p = p;
|
||||
name.l = 0;
|
||||
ws = 0;
|
||||
}
|
||||
|
||||
if (!name.l) {
|
||||
if (*p != ':') {
|
||||
ws = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
name.l = MAX((int)(p - name.p - ws), 0);
|
||||
if (!name.l) {
|
||||
err = EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
id = hdr_hash(&name);
|
||||
comsep = hdr_comma_separated(id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cv) {
|
||||
quote = false;
|
||||
cv = p;
|
||||
}
|
||||
|
||||
if (*p == '"')
|
||||
quote = !quote;
|
||||
|
||||
ws = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
err = ENODATA;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(msg);
|
||||
else {
|
||||
*msgp = msg;
|
||||
mb->pos = mb->end - l;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a HTTP Header from a HTTP Message
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param id HTTP Header ID
|
||||
*
|
||||
* @return HTTP Header if found, NULL if not found
|
||||
*/
|
||||
const struct http_hdr *http_msg_hdr(const struct http_msg *msg,
|
||||
enum http_hdrid id)
|
||||
{
|
||||
return http_msg_hdr_apply(msg, true, id, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply a function handler to certain HTTP Headers
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param fwd True to traverse forwards, false to traverse backwards
|
||||
* @param id HTTP Header ID
|
||||
* @param h Function handler
|
||||
* @param arg Handler argument
|
||||
*
|
||||
* @return HTTP Header if handler returns true, otherwise NULL
|
||||
*/
|
||||
const struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg,
|
||||
bool fwd, enum http_hdrid id,
|
||||
http_hdr_h *h, void *arg)
|
||||
{
|
||||
struct le *le;
|
||||
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
le = fwd ? msg->hdrl.head : msg->hdrl.tail;
|
||||
|
||||
while (le) {
|
||||
const struct http_hdr *hdr = le->data;
|
||||
|
||||
le = fwd ? le->next : le->prev;
|
||||
|
||||
if (hdr->id != id)
|
||||
continue;
|
||||
|
||||
if (!h || h(hdr, arg))
|
||||
return hdr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get an unknown HTTP Header from a HTTP Message
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param name Header name
|
||||
*
|
||||
* @return HTTP Header if found, NULL if not found
|
||||
*/
|
||||
const struct http_hdr *http_msg_xhdr(const struct http_msg *msg,
|
||||
const char *name)
|
||||
{
|
||||
return http_msg_xhdr_apply(msg, true, name, NULL, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply a function handler to certain unknown HTTP Headers
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param fwd True to traverse forwards, false to traverse backwards
|
||||
* @param name HTTP Header name
|
||||
* @param h Function handler
|
||||
* @param arg Handler argument
|
||||
*
|
||||
* @return HTTP Header if handler returns true, otherwise NULL
|
||||
*/
|
||||
const struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg,
|
||||
bool fwd, const char *name,
|
||||
http_hdr_h *h, void *arg)
|
||||
{
|
||||
struct le *le;
|
||||
struct pl pl;
|
||||
|
||||
if (!msg || !name)
|
||||
return NULL;
|
||||
|
||||
pl_set_str(&pl, name);
|
||||
|
||||
le = fwd ? msg->hdrl.head : msg->hdrl.tail;
|
||||
|
||||
while (le) {
|
||||
const struct http_hdr *hdr = le->data;
|
||||
|
||||
le = fwd ? le->next : le->prev;
|
||||
|
||||
if (pl_casecmp(&hdr->name, &pl))
|
||||
continue;
|
||||
|
||||
if (!h || h(hdr, arg))
|
||||
return hdr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static bool count_handler(const struct http_hdr *hdr, void *arg)
|
||||
{
|
||||
uint32_t *n = arg;
|
||||
(void)hdr;
|
||||
|
||||
++(*n);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count the number of HTTP Headers
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param id HTTP Header ID
|
||||
*
|
||||
* @return Number of HTTP Headers
|
||||
*/
|
||||
uint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id)
|
||||
{
|
||||
uint32_t n = 0;
|
||||
|
||||
http_msg_hdr_apply(msg, true, id, count_handler, &n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Count the number of unknown HTTP Headers
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param name HTTP Header name
|
||||
*
|
||||
* @return Number of HTTP Headers
|
||||
*/
|
||||
uint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name)
|
||||
{
|
||||
uint32_t n = 0;
|
||||
|
||||
http_msg_xhdr_apply(msg, true, name, count_handler, &n);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static bool value_handler(const struct http_hdr *hdr, void *arg)
|
||||
{
|
||||
return 0 == pl_strcasecmp(&hdr->val, (const char *)arg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if a HTTP Header matches a certain value
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param id HTTP Header ID
|
||||
* @param value Header value to check
|
||||
*
|
||||
* @return True if value matches, false if not
|
||||
*/
|
||||
bool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id,
|
||||
const char *value)
|
||||
{
|
||||
return NULL != http_msg_hdr_apply(msg, true, id, value_handler,
|
||||
(void *)value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if an unknown HTTP Header matches a certain value
|
||||
*
|
||||
* @param msg HTTP Message
|
||||
* @param name HTTP Header name
|
||||
* @param value Header value to check
|
||||
*
|
||||
* @return True if value matches, false if not
|
||||
*/
|
||||
bool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
return NULL != http_msg_xhdr_apply(msg, true, name, value_handler,
|
||||
(void *)value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print a HTTP Message
|
||||
*
|
||||
* @param pf Print function for output
|
||||
* @param msg HTTP Message
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int http_msg_print(struct re_printf *pf, const struct http_msg *msg)
|
||||
{
|
||||
struct le *le;
|
||||
int err;
|
||||
|
||||
if (!msg)
|
||||
return 0;
|
||||
|
||||
if (pl_isset(&msg->met))
|
||||
err = re_hprintf(pf, "%r %r%r HTTP/%r\n", &msg->met,
|
||||
&msg->path, &msg->prm, &msg->ver);
|
||||
else
|
||||
err = re_hprintf(pf, "HTTP/%r %u %r\n", &msg->ver, msg->scode,
|
||||
&msg->reason);
|
||||
|
||||
for (le=msg->hdrl.head; le; le=le->next) {
|
||||
|
||||
const struct http_hdr *hdr = le->data;
|
||||
|
||||
err |= re_hprintf(pf, "%r: %r (%i)\n", &hdr->name, &hdr->val,
|
||||
hdr->id);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
504
src/http/server.c
Normal file
504
src/http/server.c
Normal file
|
@ -0,0 +1,504 @@
|
|||
/**
|
||||
* @file http/server.c HTTP Server
|
||||
*
|
||||
* Copyright (C) 2011 Creytiv.com
|
||||
*/
|
||||
|
||||
#include <re_types.h>
|
||||
#include <re_mem.h>
|
||||
#include <re_mbuf.h>
|
||||
#include <re_sa.h>
|
||||
#include <re_list.h>
|
||||
#include <re_fmt.h>
|
||||
#include <re_tmr.h>
|
||||
#include <re_tcp.h>
|
||||
#include <re_tls.h>
|
||||
#include <re_http.h>
|
||||
|
||||
|
||||
enum {
|
||||
TIMEOUT_IDLE = 600000,
|
||||
TIMEOUT_INIT = 10000,
|
||||
BUFSIZE_MAX = 524288,
|
||||
};
|
||||
|
||||
struct http_sock {
|
||||
struct list connl;
|
||||
struct tcp_sock *ts;
|
||||
struct tls *tls;
|
||||
http_req_h *reqh;
|
||||
void *arg;
|
||||
};
|
||||
|
||||
struct http_conn {
|
||||
struct le le;
|
||||
struct tmr tmr;
|
||||
struct sa peer;
|
||||
struct http_sock *sock;
|
||||
struct tcp_conn *tc;
|
||||
struct tls_conn *sc;
|
||||
struct mbuf *mb;
|
||||
};
|
||||
|
||||
|
||||
static void conn_close(struct http_conn *conn);
|
||||
|
||||
|
||||
static void sock_destructor(void *arg)
|
||||
{
|
||||
struct http_sock *sock = arg;
|
||||
struct le *le;
|
||||
|
||||
for (le=sock->connl.head; le;) {
|
||||
|
||||
struct http_conn *conn = le->data;
|
||||
|
||||
le = le->next;
|
||||
|
||||
conn_close(conn);
|
||||
mem_deref(conn);
|
||||
}
|
||||
|
||||
mem_deref(sock->tls);
|
||||
mem_deref(sock->ts);
|
||||
}
|
||||
|
||||
|
||||
static void conn_destructor(void *arg)
|
||||
{
|
||||
struct http_conn *conn = arg;
|
||||
|
||||
list_unlink(&conn->le);
|
||||
tmr_cancel(&conn->tmr);
|
||||
mem_deref(conn->sc);
|
||||
mem_deref(conn->tc);
|
||||
mem_deref(conn->mb);
|
||||
}
|
||||
|
||||
|
||||
static void conn_close(struct http_conn *conn)
|
||||
{
|
||||
list_unlink(&conn->le);
|
||||
tmr_cancel(&conn->tmr);
|
||||
conn->sc = mem_deref(conn->sc);
|
||||
conn->tc = mem_deref(conn->tc);
|
||||
conn->sock = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void timeout_handler(void *arg)
|
||||
{
|
||||
struct http_conn *conn = arg;
|
||||
|
||||
conn_close(conn);
|
||||
mem_deref(conn);
|
||||
}
|
||||
|
||||
|
||||
static void recv_handler(struct mbuf *mb, void *arg)
|
||||
{
|
||||
struct http_conn *conn = arg;
|
||||
int err = 0;
|
||||
|
||||
if (conn->mb) {
|
||||
|
||||
const size_t len = mbuf_get_left(mb), pos = conn->mb->pos;
|
||||
|
||||
if ((mbuf_get_left(conn->mb) + len) > BUFSIZE_MAX) {
|
||||
err = EOVERFLOW;
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn->mb->pos = conn->mb->end;
|
||||
|
||||
err = mbuf_write_mem(conn->mb, mbuf_buf(mb), len);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
conn->mb->pos = pos;
|
||||
}
|
||||
else {
|
||||
conn->mb = mem_ref(mb);
|
||||
}
|
||||
|
||||
while (conn->mb) {
|
||||
size_t end, pos = conn->mb->pos;
|
||||
struct http_msg *msg;
|
||||
|
||||
err = http_msg_decode(&msg, conn->mb, true);
|
||||
if (err) {
|
||||
if (err == ENODATA) {
|
||||
conn->mb->pos = pos;
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mbuf_get_left(conn->mb) < msg->clen) {
|
||||
conn->mb->pos = pos;
|
||||
mem_deref(msg);
|
||||
break;
|
||||
}
|
||||
|
||||
mb = conn->mb;
|
||||
|
||||
end = mb->end;
|
||||
mb->end = mb->pos + msg->clen;
|
||||
|
||||
if (end > mb->end) {
|
||||
struct mbuf *mbn = mbuf_alloc(end - mb->end);
|
||||
if (!mbn) {
|
||||
mem_deref(msg);
|
||||
err = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
(void)mbuf_write_mem(mbn, mb->buf + mb->end,
|
||||
end - mb->end);
|
||||
mbn->pos = 0;
|
||||
|
||||
mem_deref(conn->mb);
|
||||
conn->mb = mbn;
|
||||
}
|
||||
else {
|
||||
conn->mb = mem_deref(conn->mb);
|
||||
}
|
||||
|
||||
if (conn->sock)
|
||||
conn->sock->reqh(conn, msg, conn->sock->arg);
|
||||
|
||||
mem_deref(msg);
|
||||
|
||||
if (!conn->tc) {
|
||||
err = ENOTCONN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tmr_start(&conn->tmr, TIMEOUT_IDLE, timeout_handler, conn);
|
||||
}
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
conn_close(conn);
|
||||
mem_deref(conn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void close_handler(int err, void *arg)
|
||||
{
|
||||
struct http_conn *conn = arg;
|
||||
(void)err;
|
||||
|
||||
conn_close(conn);
|
||||
mem_deref(conn);
|
||||
}
|
||||
|
||||
|
||||
static void connect_handler(const struct sa *peer, void *arg)
|
||||
{
|
||||
struct http_sock *sock = arg;
|
||||
struct http_conn *conn;
|
||||
int err;
|
||||
|
||||
conn = mem_zalloc(sizeof(*conn), conn_destructor);
|
||||
if (!conn) {
|
||||
err = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_append(&sock->connl, &conn->le, conn);
|
||||
conn->peer = *peer;
|
||||
conn->sock = sock;
|
||||
|
||||
err = tcp_accept(&conn->tc, sock->ts, NULL, recv_handler,
|
||||
close_handler, conn);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
#ifdef USE_TLS
|
||||
if (sock->tls) {
|
||||
err = tls_start_tcp(&conn->sc, sock->tls, conn->tc, 0);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
|
||||
tmr_start(&conn->tmr, TIMEOUT_INIT, timeout_handler, conn);
|
||||
|
||||
out:
|
||||
if (err) {
|
||||
mem_deref(conn);
|
||||
tcp_reject(sock->ts);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an HTTP socket
|
||||
*
|
||||
* @param sockp Pointer to returned HTTP Socket
|
||||
* @param laddr Network address to listen on
|
||||
* @param reqh Request handler
|
||||
* @param arg Handler argument
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int http_listen(struct http_sock **sockp, const struct sa *laddr,
|
||||
http_req_h *reqh, void *arg)
|
||||
{
|
||||
struct http_sock *sock;
|
||||
int err;
|
||||
|
||||
if (!sockp || !laddr || !reqh)
|
||||
return EINVAL;
|
||||
|
||||
sock = mem_zalloc(sizeof(*sock), sock_destructor);
|
||||
if (!sock)
|
||||
return ENOMEM;
|
||||
|
||||
err = tcp_listen(&sock->ts, laddr, connect_handler, sock);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
sock->reqh = reqh;
|
||||
sock->arg = arg;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(sock);
|
||||
else
|
||||
*sockp = sock;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an HTTP secure socket
|
||||
*
|
||||
* @param sockp Pointer to returned HTTP Socket
|
||||
* @param laddr Network address to listen on
|
||||
* @param cert File path of TLS certificate
|
||||
* @param reqh Request handler
|
||||
* @param arg Handler argument
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int https_listen(struct http_sock **sockp, const struct sa *laddr,
|
||||
const char *cert, http_req_h *reqh, void *arg)
|
||||
{
|
||||
struct http_sock *sock;
|
||||
int err;
|
||||
|
||||
if (!sockp || !laddr || !cert || !reqh)
|
||||
return EINVAL;
|
||||
|
||||
err = http_listen(&sock, laddr, reqh, arg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
#ifdef USE_TLS
|
||||
err = tls_alloc(&sock->tls, TLS_METHOD_SSLV23, cert, NULL);
|
||||
#else
|
||||
err = EPROTONOSUPPORT;
|
||||
#endif
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (err)
|
||||
mem_deref(sock);
|
||||
else
|
||||
*sockp = sock;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the peer address of an HTTP connection
|
||||
*
|
||||
* @param conn HTTP connection
|
||||
*
|
||||
* @return Peer address
|
||||
*/
|
||||
const struct sa *http_conn_peer(const struct http_conn *conn)
|
||||
{
|
||||
return conn ? &conn->peer : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the TCP connection of an HTTP connection
|
||||
*
|
||||
* @param conn HTTP connection
|
||||
*
|
||||
* @return TCP connection
|
||||
*/
|
||||
struct tcp_conn *http_conn_tcp(struct http_conn *conn)
|
||||
{
|
||||
return conn ? conn->tc : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the TLS connection of an HTTP connection
|
||||
*
|
||||
* @param conn HTTP connection
|
||||
*
|
||||
* @return TLS connection
|
||||
*/
|
||||
struct tls_conn *http_conn_tls(struct http_conn *conn)
|
||||
{
|
||||
return conn ? conn->sc : NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close the HTTP connection
|
||||
*
|
||||
* @param conn HTTP connection
|
||||
*/
|
||||
void http_conn_close(struct http_conn *conn)
|
||||
{
|
||||
if (!conn)
|
||||
return;
|
||||
|
||||
conn->sc = mem_deref(conn->sc);
|
||||
conn->tc = mem_deref(conn->tc);
|
||||
}
|
||||
|
||||
|
||||
static int http_vreply(struct http_conn *conn, uint16_t scode,
|
||||
const char *reason, const char *fmt, va_list ap)
|
||||
{
|
||||
struct mbuf *mb;
|
||||
int err;
|
||||
|
||||
if (!conn || !scode || !reason)
|
||||
return EINVAL;
|
||||
|
||||
if (!conn->tc)
|
||||
return ENOTCONN;
|
||||
|
||||
mb = mbuf_alloc(8192);
|
||||
if (!mb)
|
||||
return ENOMEM;
|
||||
|
||||
err = mbuf_printf(mb, "HTTP/1.1 %u %s\r\n", scode, reason);
|
||||
if (fmt)
|
||||
err |= mbuf_vprintf(mb, fmt, ap);
|
||||
else
|
||||
err |= mbuf_write_str(mb, "Content-Length: 0\r\n\r\n");
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
mb->pos = 0;
|
||||
|
||||
err = tcp_send(conn->tc, mb);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
mem_deref(mb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an HTTP response
|
||||
*
|
||||
* @param conn HTTP connection
|
||||
* @param scode Response status code
|
||||
* @param reason Response reason phrase
|
||||
* @param fmt Formatted HTTP message
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int http_reply(struct http_conn *conn, uint16_t scode, const char *reason,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = http_vreply(conn, scode, reason, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an HTTP response with content formating
|
||||
*
|
||||
* @param conn HTTP connection
|
||||
* @param scode Response status code
|
||||
* @param reason Response reason phrase
|
||||
* @param ctype Content type
|
||||
* @param fmt Formatted HTTP content
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int http_creply(struct http_conn *conn, uint16_t scode, const char *reason,
|
||||
const char *ctype, const char *fmt, ...)
|
||||
{
|
||||
struct mbuf *mb;
|
||||
va_list ap;
|
||||
int err;
|
||||
|
||||
if (!ctype || !fmt)
|
||||
return EINVAL;
|
||||
|
||||
mb = mbuf_alloc(8192);
|
||||
if (!mb)
|
||||
return ENOMEM;
|
||||
|
||||
va_start(ap, fmt);
|
||||
err = mbuf_vprintf(mb, fmt, ap);
|
||||
va_end(ap);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = http_reply(conn, scode, reason,
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %zu\r\n"
|
||||
"\r\n"
|
||||
"%b",
|
||||
ctype,
|
||||
mb->end,
|
||||
mb->buf, mb->end);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
mem_deref(mb);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an HTTP error response
|
||||
*
|
||||
* @param conn HTTP connection
|
||||
* @param scode Response status code
|
||||
* @param reason Response reason phrase
|
||||
*
|
||||
* @return 0 if success, otherwise errorcode
|
||||
*/
|
||||
int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason)
|
||||
{
|
||||
return http_creply(conn, scode, reason, "text/html",
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html>\n"
|
||||
"<head><title>%u %s</title></head>\n"
|
||||
"<body><h2>%u %s</h2></body>\n"
|
||||
"</html>\n",
|
||||
scode, reason,
|
||||
scode, reason);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* @file auth.c SIP Authentication
|
||||
* @file sip/auth.c SIP Authentication
|
||||
*
|
||||
* Copyright (C) 2010 Creytiv.com
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue