Add support for config based redirection.

Inside the domain contexts a 'redirect' rule will allow you to redirect
a request to another URI.

Ex:

Redirect all requests with a 301 to example.com

	redirect ^/.*$ 301 https://example.com

Using capture groups

	redirect ^/account/(.*)$ 301 https://example.com/account/$1

Adding the query string in the mix

	redirect ^/(.*)$ 301 https://example.com/$1?$qs
This commit is contained in:
Joris Vink 2020-02-07 06:42:33 +01:00
parent 6072828d8f
commit fa2e8ef0b6
7 changed files with 174 additions and 34 deletions

View File

@ -10,8 +10,15 @@
#socket_backlog 5000
# Server configuration.
bind 127.0.0.1 443
#bind_unix /var/run/kore.sock
server tls {
bind 127.0.0.1 443
#bind_unix /var/run/kore.sock
}
#server notls {
# bind 127.0.0.1 80
# tls no
#}
# The worker process root directory. If chrooting was not disabled
# at startup the worker processes will chroot into this directory.
@ -276,6 +283,8 @@ authentication auth_example {
# Example domain that responds to localhost.
domain localhost {
attach tls
certfile cert/server.crt
certkey cert/server.key
accesslog /var/log/kore_access.log
@ -334,6 +343,17 @@ domain localhost {
}
}
# Example redirect 80->443.
#domain localhost {
# attach notls
#
# # specific redirect with a capture group and arguments
# redirect ^/account/(.*)$ 301 https://localhost/account/$1
#
# # redirect the others back to root.
# redirect ^/.*$ 301 https://localhost
#}
#domain domain.com {
# certfile cert/other/server.crt
# certkey cert/other/server.key

View File

@ -236,6 +236,13 @@ struct reqcall;
struct kore_task;
struct http_client;
struct http_redirect {
regex_t rctx;
int status;
char *target;
TAILQ_ENTRY(http_redirect) list;
};
struct http_request {
u_int8_t method;
u_int8_t fsm_state;
@ -345,6 +352,9 @@ int http_media_register(const char *, const char *);
int http_check_timeout(struct connection *, u_int64_t);
ssize_t http_body_read(struct http_request *, void *, size_t);
int http_body_digest(struct http_request *, char *, size_t);
int http_redirect_add(struct kore_domain *,
const char *, int, const char *);
void http_response(struct http_request *, int, const void *, size_t);
void http_response_fileref(struct http_request *, int,
struct kore_fileref *);

View File

@ -134,6 +134,7 @@ extern int daemon(int, int);
/* XXX hackish. */
#if !defined(KORE_NO_HTTP)
struct http_request;
struct http_redirect;
#endif
#define KORE_FILEREF_SOFT_REMOVED 0x1000
@ -323,6 +324,7 @@ struct kore_domain {
int x509_verify_depth;
#if !defined(KORE_NO_HTTP)
TAILQ_HEAD(, kore_module_handle) handlers;
TAILQ_HEAD(, http_redirect) redirects;
#endif
TAILQ_ENTRY(kore_domain) list;
};
@ -927,7 +929,7 @@ int kore_module_handler_new(struct kore_domain *, const char *,
const char *, const char *, int);
void kore_module_handler_free(struct kore_module_handle *);
struct kore_module_handle *kore_module_handler_find(struct http_request *,
const char *, const char *);
struct kore_domain *);
#endif
struct kore_runtime_call *kore_runtime_getcall(const char *);

View File

@ -110,6 +110,7 @@ static int configure_client_verify_depth(char *);
#if !defined(KORE_NO_HTTP)
static int configure_route(char *);
static int configure_filemap(char *);
static int configure_redirect(char *);
static int configure_static_handler(char *);
static int configure_dynamic_handler(char *);
static int configure_restrict(char *);
@ -189,6 +190,7 @@ static struct {
#if !defined(KORE_NO_HTTP)
{ "route", configure_route},
{ "filemap", configure_filemap },
{ "redirect", configure_redirect },
{ "static", configure_static_handler },
{ "dynamic", configure_dynamic_handler },
{ "accesslog", configure_accesslog },
@ -1026,6 +1028,37 @@ configure_route(char *options)
return (KORE_RESULT_OK);
}
static int
configure_redirect(char *options)
{
char *argv[4];
int elm, status, err;
if (current_domain == NULL) {
printf("redirect outside of domain context\n");
return (KORE_RESULT_ERROR);
}
elm = kore_split_string(options, " ", argv, 4);
if (elm != 3) {
printf("missing parameters for redirect\n");
return (KORE_RESULT_ERROR);
}
status = kore_strtonum(argv[1], 10, 300, 399, &err);
if (err != KORE_RESULT_OK) {
printf("invalid status code on redirect (%s)\n", argv[1]);
return (KORE_RESULT_ERROR);
}
if (!http_redirect_add(current_domain, argv[0], status, argv[2])) {
printf("invalid regex on redirect path\n");
return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
}
static int
configure_filemap(char *options)
{

View File

@ -188,6 +188,7 @@ kore_domain_new(const char *domain)
#if !defined(KORE_NO_HTTP)
TAILQ_INIT(&(dom->handlers));
TAILQ_INIT(&(dom->redirects));
#endif
if (dom->id < KORE_DOMAIN_CACHE) {
@ -225,7 +226,8 @@ void
kore_domain_free(struct kore_domain *dom)
{
#if !defined(KORE_NO_HTTP)
struct kore_module_handle *hdlr;
struct http_redirect *rdr;
struct kore_module_handle *hdlr;
#endif
if (dom == NULL)
return;
@ -255,6 +257,13 @@ kore_domain_free(struct kore_domain *dom)
TAILQ_REMOVE(&(dom->handlers), hdlr, list);
kore_module_handler_free(hdlr);
}
while ((rdr = TAILQ_FIRST(&(dom->redirects))) != NULL) {
TAILQ_REMOVE(&(dom->redirects), rdr, list);
regfree(&rdr->rctx);
kore_free(rdr->target);
kore_free(rdr);
}
#endif
kore_free(dom);
}

View File

@ -123,6 +123,8 @@ static void http_error_response(struct connection *, int);
static void http_write_response_cookie(struct http_cookie *);
static void http_argument_add(struct http_request *, char *, char *,
int, int);
static int http_check_redirect(struct http_request *,
struct kore_domain *);
static void http_response_normal(struct http_request *,
struct connection *, int, const void *, size_t);
static void multipart_add_field(struct http_request *, struct kore_buf *,
@ -1482,13 +1484,82 @@ http_runlock_release(struct http_runlock *lock, struct http_request *req)
}
}
int
http_redirect_add(struct kore_domain *dom, const char *path, int status,
const char *target)
{
struct http_redirect *rdr;
rdr = kore_calloc(1, sizeof(*rdr));
if (regcomp(&(rdr->rctx), path, REG_EXTENDED)) {
kore_free(rdr);
return (KORE_RESULT_ERROR);
}
rdr->status = status;
rdr->target = kore_strdup(target);
TAILQ_INSERT_TAIL(&dom->redirects, rdr, list);
return (KORE_RESULT_OK);
}
static int
http_check_redirect(struct http_request *req, struct kore_domain *dom)
{
int idx;
struct http_redirect *rdr;
const char *uri;
char key[4];
struct kore_buf location;
TAILQ_FOREACH(rdr, &dom->redirects, list) {
if (!regexec(&(rdr->rctx), req->path,
HTTP_CAPTURE_GROUPS, req->cgroups, 0))
break;
}
if (rdr == NULL)
return (KORE_RESULT_ERROR);
kore_buf_init(&location, 128);
kore_buf_appendf(&location, "%s", rdr->target);
if (req->query_string != NULL) {
kore_buf_replace_string(&location, "$qs",
req->query_string, strlen(req->query_string));
}
/* Starts at 1 to skip the full path. */
for (idx = 1; idx < HTTP_CAPTURE_GROUPS - 1; idx++) {
if (req->cgroups[idx].rm_so == -1 ||
req->cgroups[idx].rm_eo == -1)
break;
(void)snprintf(key, sizeof(key), "$%d", idx);
kore_buf_replace_string(&location, key,
req->path + req->cgroups[idx].rm_so,
req->cgroups[idx].rm_eo - req->cgroups[idx].rm_so);
}
uri = kore_buf_stringify(&location, NULL);
http_response_header(req, "location", uri);
http_response(req, rdr->status, NULL, 0);
kore_buf_cleanup(&location);
return (KORE_RESULT_OK);
}
static struct http_request *
http_request_new(struct connection *c, const char *host,
const char *method, char *path, const char *version)
{
struct kore_domain *dom;
struct http_request *req;
struct kore_module_handle *hdlr;
char *p, *hp;
int m, flags;
size_t hostlen, pathlen, qsoff;
@ -1523,7 +1594,6 @@ http_request_new(struct connection *c, const char *host,
}
if ((p = strchr(path, '?')) != NULL) {
*p = '\0';
qsoff = p - path;
} else {
qsoff = 0;
@ -1568,21 +1638,9 @@ http_request_new(struct connection *c, const char *host,
return (NULL);
}
req = kore_pool_get(&http_request_pool);
req->owner = c;
if ((hdlr = kore_module_handler_find(req, host, path)) == NULL) {
kore_pool_put(&http_request_pool, req);
http_error_response(c, 404);
return (NULL);
}
if (hp != NULL)
*hp = ':';
if (p != NULL)
*p = '?';
if (!strcasecmp(method, "get")) {
m = HTTP_METHOD_GET;
flags |= HTTP_REQUEST_COMPLETE;
@ -1605,7 +1663,6 @@ http_request_new(struct connection *c, const char *host,
m = HTTP_METHOD_PATCH;
flags |= HTTP_REQUEST_EXPECT_BODY;
} else {
kore_pool_put(&http_request_pool, req);
http_error_response(c, 400);
return (NULL);
}
@ -1613,17 +1670,12 @@ http_request_new(struct connection *c, const char *host,
if (flags & HTTP_VERSION_1_0) {
if (m != HTTP_METHOD_GET && m != HTTP_METHOD_POST &&
m != HTTP_METHOD_HEAD) {
kore_pool_put(&http_request_pool, req);
http_error_response(c, HTTP_STATUS_METHOD_NOT_ALLOWED);
return (NULL);
}
}
if (!(hdlr->methods & m)) {
kore_pool_put(&http_request_pool, req);
http_error_response(c, HTTP_STATUS_METHOD_NOT_ALLOWED);
return (NULL);
}
req = kore_pool_get(&http_request_pool);
req->end = 0;
req->total = 0;
@ -1631,7 +1683,6 @@ http_request_new(struct connection *c, const char *host,
req->owner = c;
req->status = 0;
req->method = m;
req->hdlr = hdlr;
req->agent = NULL;
req->onfree = NULL;
req->referer = NULL;
@ -1663,6 +1714,9 @@ http_request_new(struct connection *c, const char *host,
req->query_string = NULL;
}
/* Checked further down below if we need to 404. */
req->hdlr = kore_module_handler_find(req, dom);
TAILQ_INIT(&(req->resp_headers));
TAILQ_INIT(&(req->req_headers));
TAILQ_INIT(&(req->resp_cookies));
@ -1682,6 +1736,23 @@ http_request_new(struct connection *c, const char *host,
TAILQ_INSERT_HEAD(&http_requests, req, list);
TAILQ_INSERT_TAIL(&(c->http_requests), req, olist);
if (http_check_redirect(req, dom)) {
http_request_free(req);
return (NULL);
}
if (req->hdlr == NULL) {
http_request_free(req);
http_error_response(c, HTTP_STATUS_NOT_FOUND);
return (NULL);
}
if (!(req->hdlr->methods & m)) {
http_request_free(req);
http_error_response(c, HTTP_STATUS_METHOD_NOT_ALLOWED);
return (NULL);
}
return (req);
}

View File

@ -284,24 +284,19 @@ kore_module_handler_free(struct kore_module_handle *hdlr)
}
struct kore_module_handle *
kore_module_handler_find(struct http_request *req, const char *domain,
const char *path)
kore_module_handler_find(struct http_request *req, struct kore_domain *dom)
{
struct connection *c;
struct kore_domain *dom;
struct kore_module_handle *hdlr;
c = req->owner;
if ((dom = kore_domain_lookup(c->owner->server, domain)) == NULL)
return (NULL);
TAILQ_FOREACH(hdlr, &(dom->handlers), list) {
if (hdlr->type == HANDLER_TYPE_STATIC) {
if (!strcmp(hdlr->path, path))
if (!strcmp(hdlr->path, req->path))
return (hdlr);
} else {
if (!regexec(&(hdlr->rctx), path,
if (!regexec(&(hdlr->rctx), req->path,
HTTP_CAPTURE_GROUPS, req->cgroups, 0))
return (hdlr);
}