1 /*
2 * This file is part of the Sofia-SIP package
3 *
4 * Copyright (C) 2005 Nokia Corporation.
5 *
6 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 * 02110-1301 USA
22 *
23 */
24
25 /**@internal @file nth_server.c
26 * @brief HTTP server.
27 *
28 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
29 *
30 * @date Created: Sat Oct 19 01:37:36 2002 ppessi
31 */
32
33 #include "config.h"
34
35 #include <sofia-sip/su_string.h>
36 #include <sofia-sip/su.h>
37
38 typedef struct server_s server_t;
39
40 /** @internal SU timer argument pointer type */
41 #define SU_TIMER_ARG_T server_t
42
43 #include <sofia-sip/http_header.h>
44 #include <sofia-sip/http_status.h>
45 #include <sofia-sip/http_tag.h>
46
47 #include "sofia-sip/nth.h"
48
49 #include <sofia-sip/msg_date.h>
50 #include <sofia-sip/msg_addr.h>
51 #include <sofia-sip/su_tagarg.h>
52
53 #include <sofia-sip/hostdomain.h>
54
55 /* We are customer of tport_t */
56 #define TP_STACK_T server_t
57 #define TP_MAGIC_T void
58
59 #include <sofia-sip/tport.h>
60 #include <sofia-sip/htable.h>
61
62 #include <sofia-sip/auth_module.h>
63
64 #include <stddef.h>
65 #include <stdlib.h>
66 #include <stdio.h>
67 #include <stdarg.h>
68 #include <errno.h>
69 #include <assert.h>
70
71 #ifndef UINT32_MAX
72 #define UINT32_MAX (0xffffffffU)
73 #endif
74
75 enum { SERVER_TICK = 1000 };
76
77 #define SERVER_VERSION "nth/" NTH_VERSION
78
79 HTABLE_DECLARE(hc_htable, hct, nth_client_t);
80
81 struct server_s
82 {
83 su_home_t srv_home[1];
84 su_root_t *srv_root;
85
86 su_timer_t *srv_timer;
87 unsigned srv_now;
88
89 msg_mclass_t const*srv_mclass;
90 int srv_mflags; /**< Message flags */
91
92 tport_t *srv_tports;
93 unsigned srv_queuesize; /**< Maximum number of queued responses */
94
95 size_t srv_max_bodylen; /**< Maximum accepted length */
96
97 unsigned srv_persistent:1; /**< Allow persistent connections */
98
99 /** Sites */
100 nth_site_t *srv_sites;
101
102 /* Statistics */
103 struct {
104 uint32_t st_requests; /**< Received requests */
105 uint32_t st_responses; /**< Sent responses */
106 uint32_t st_bad; /**< Bad requests */
107 } srv_stats[1];
108
109 http_server_t *srv_server; /**< Server header */
110 };
111
112 struct nth_site_s
113 {
114 nth_site_t *site_next, **site_prev;
115
116 nth_site_t *site_kids;
117
118 server_t *site_server;
119 auth_mod_t *site_auth;
120
121 url_t *site_url;
122 char const *site_path;
123 size_t site_path_len;
124
125 nth_request_f *site_callback;
126 nth_site_magic_t *site_magic;
127
128 su_time_t site_access; /**< Last request served */
129
130 /** Host header must match with server name */
131 unsigned site_strict : 1;
132 /** Site can have kids */
133 unsigned site_isdir : 1;
134 /** Site does not have domain name */
135 unsigned site_wildcard : 1;
136 };
137
138 struct nth_request_s
139 {
140 server_t *req_server;
141
142 http_method_t req_method;
143 char const *req_method_name;
144 url_t const *req_url; /**< RequestURI */
145 char const *req_version;
146
147 tport_t *req_tport;
148 msg_t *req_request;
149 msg_t *req_response;
150
151 auth_status_t *req_as;
152
153 unsigned short req_status;
154 unsigned req_close : 1; /**< Client asked for close */
155 unsigned req_in_callback : 1;
156 unsigned req_destroyed : 1;
157 };
158
159 /* ====================================================================== */
160 /* Debug log settings */
161
162 #define SU_LOG nth_server_log
163
164 #ifdef SU_DEBUG_H
165 #error <su_debug.h> included directly.
166 #endif
167 #include <sofia-sip/su_debug.h>
168
169 /**Environment variable determining the debug log level for @b nth
170 * module.
171 *
172 * The NTH_DEBUG environment variable is used to determine the debug
173 * logging level for @b nth module. The default level is 1.
174 *
175 * @sa <sofia-sip/su_debug.h>, nth_server_log, SOFIA_DEBUG
176 */
177 extern char const NTH_DEBUG[];
178
179 #ifndef SU_DEBUG
180 #define SU_DEBUG 1
181 #endif
182
183 /**Debug log for @b nth module.
184 *
185 * The nth_server_log is the log object used by @b nth module. The level of
186 * #nth_server_log is set using #NTH_DEBUG environment variable.
187 */
188 su_log_t nth_server_log[] = { SU_LOG_INIT("nth", "NTH_DEBUG", SU_DEBUG) };
189
190 #if HAVE_FUNC
191 #elif HAVE_FUNCTION
192 #define __func__ __FUNCTION__
193 #else
194 static char const __func__[] = "nth";
195 #endif
196
197 /* ====================================================================== */
198 /** Server side
199 */
200 static server_t *server_create(url_t const *url,
201 tag_type_t tag, tag_value_t value, ...);
202 void server_destroy(server_t *srv);
203 static void server_request(server_t *srv, tport_t *tport, msg_t *msg,
204 void *arg, su_time_t now);
205 static nth_site_t **site_get_host(nth_site_t **, char const *host, char const *port);
206 static nth_site_t **site_get_rslot(nth_site_t *parent, char *path,
207 char **return_rest);
208 static nth_site_t *site_get_subdir(nth_site_t *parent, char const *path, char const **res);
209 static void server_tport_error(server_t *srv, tport_t *tport,
210 int errcode, char const *remote);
211 static msg_t *server_msg_create(server_t *srv, int flags,
212 char const data[], usize_t dlen,
213 tport_t const *tp, tp_client_t *tpc);
214
215 static void server_reply(server_t *srv, tport_t *tport,
216 msg_t *request, msg_t *response,
217 int status, char const *phrase);
218
219 static
220 void nth_site_request(server_t *srv,
221 nth_site_t *site,
222 tport_t *tport,
223 msg_t *request,
224 http_t *http,
225 char const *path,
226 msg_t *response);
227
228 /* ----------------------------------------------------------------------
229 * 5) Site functions
230 */
231
232 /** Create a http site object.
233 *
234 * The function nth_site_create() allocates and initializes a web site
235 * object. A web site object can be either
236 * - a primary http server (@a parent is NULL),
237 * - a virtual http server (@a address contains hostpart), or
238 * - a site within a server
239 * (@a address does not have hostpart, only path part).
240 *
241 * @param parent pointer to parent site
242 * (NULL when creating a primary server object)
243 * @param callback pointer to callback function called when
244 * a request is received
245 * @param magic application context included in callback parameters
246 * @param address absolute or relative URI specifying the address of
247 * site
248 * @param tag, value, ... list of tagged parameters
249 *
250 * @TAGS
251 * If the @a parent is NULL, the list of tagged parameters must contain
252 * NTHTAG_ROOT() used to create the server engine. Tags supported when @a
253 * parent is NULL are NTHTAG_ROOT(), NTHTAG_MCLASS(), TPTAG_REUSE(),
254 * HTTPTAG_SERVER(), and HTTPTAG_SERVER_STR(). All the tags are passed to
255 * tport_tcreate() and tport_tbind(), too.
256 *
257 * @since Support for multiple sites was added to @VERSION_1_12_4
258 */
nth_site_create(nth_site_t * parent,nth_request_f * callback,nth_site_magic_t * magic,url_string_t const * address,tag_type_t tag,tag_value_t value,...)259 nth_site_t *nth_site_create(nth_site_t *parent,
260 nth_request_f *callback,
261 nth_site_magic_t *magic,
262 url_string_t const *address,
263 tag_type_t tag, tag_value_t value,
264 ...)
265 {
266 nth_site_t *site = NULL, **prev = NULL;
267 su_home_t home[SU_HOME_AUTO_SIZE(256)];
268 url_t *url, url0[1];
269 server_t *srv = NULL;
270 ta_list ta;
271 char *path = NULL;
272 size_t usize;
273 int is_host, is_path, wildcard = 0;
274
275 su_home_auto(home, sizeof home);
276
277 if (parent && url_is_string(address)) {
278 char const *s = (char const *)address;
279 size_t sep = strcspn(s, "/:");
280
281 if (parent->site_path) {
282 /* subpath */
283 url_init(url0, (enum url_type_e)parent->site_url->url_type);
284 url0->url_path = s;
285 address = (url_string_t*)url0;
286 }
287 else if (s[sep] == ':')
288 /* absolute URL with scheme */;
289 else if (s[sep] == '\0' && strchr(s, '.') && host_is_valid(s)) {
290 /* looks like a domain name */;
291 url_init(url0, (enum url_type_e)parent->site_url->url_type);
292 url0->url_host = s;
293 address = (url_string_t*)url0;
294 }
295 else {
296 /* looks like a path */
297 url_init(url0, (enum url_type_e)parent->site_url->url_type);
298 url0->url_path = s;
299 address = (url_string_t*)url0;
300 }
301 }
302
303 url = url_hdup(home, address->us_url);
304
305 if (!url || !callback)
306 return NULL;
307
308 is_host = url->url_host != NULL;
309 is_path = url->url_path != NULL;
310
311 if (is_host && is_path) {
312 SU_DEBUG_3(("nth_site_create(): virtual host and path simultanously\n" VA_NONE));
313 errno = EINVAL;
314 goto error;
315 }
316
317 if (!parent && !is_host) {
318 SU_DEBUG_3(("nth_site_create(): host is required\n" VA_NONE));
319 errno = EINVAL;
320 goto error;
321 }
322
323 if (parent) {
324 if (!parent->site_isdir) {
325 SU_DEBUG_3(("nth_site_create(): invalid parent resource \n" VA_NONE));
326 errno = EINVAL;
327 goto error;
328 }
329
330 srv = parent->site_server; assert(srv);
331 if (is_host) {
332 prev = site_get_host(&srv->srv_sites, url->url_host, url->url_port);
333
334 if (prev == NULL) {
335 SU_DEBUG_3(("nth_site_create(): host %s:%s already exists\n",
336 url->url_host, url->url_port ? url->url_port : ""));
337 errno = EEXIST;
338 goto error;
339 }
340 }
341 else {
342 size_t i, j;
343
344 path = (char *)url->url_path;
345
346 if (!path) {
347 SU_DEBUG_3(("nth_site_create(): invalid url\n" VA_NONE));
348 errno = EINVAL;
349 goto error;
350 }
351
352 while (path[0] == '/')
353 path++;
354
355 /* Remove duplicate // */
356 for (i = j = 0; path[i];) {
357 while (path[i] == '/' && path[i + 1] == '/')
358 i++;
359 path[j++] = path[i++];
360 }
361 path[j] = path[i];
362
363 url = url0, *url = *parent->site_url;
364
365 if (url->url_path) {
366 url->url_path = su_strcat(home, url->url_path, path);
367 if (!url->url_path)
368 goto error;
369 path = (char *)url->url_path + strlen(parent->site_url->url_path);
370 }
371 else
372 url->url_path = path;
373
374 prev = site_get_rslot(parent, path, &path);
375
376 if (!prev || path[0] == '\0') {
377 SU_DEBUG_3(("nth_site_create(): directory \"%s\" already exists\n",
378 url->url_path));
379 errno = EEXIST;
380 goto error;
381 }
382 }
383 }
384
385 if (!parent) {
386 if (strcmp(url->url_host, "*") == 0 ||
387 host_cmp(url->url_host, "0.0.0.0") == 0 ||
388 host_cmp(url->url_host, "::") == 0)
389 wildcard = 1, url->url_host = "*";
390 }
391
392 usize = sizeof(*url) + url_xtra(url);
393
394 ta_start(ta, tag, value);
395
396 if (!parent) {
397 srv = server_create(url, ta_tags(ta));
398 prev = &srv->srv_sites;
399 }
400
401 if (srv && (site = su_zalloc(srv->srv_home, (sizeof *site) + usize))) {
402 site->site_url = (url_t *)(site + 1);
403 url_dup((void *)(site->site_url + 1), usize - sizeof(*url),
404 site->site_url, url);
405
406 assert(prev);
407 if ((site->site_next = *prev))
408 site->site_next->site_prev = &site->site_next;
409 *prev = site, site->site_prev = prev;
410 site->site_server = srv;
411
412 if (path) {
413 size_t path_len;
414
415 site->site_path = site->site_url->url_path + (path - url->url_path);
416 path_len = strlen(site->site_path); assert(path_len > 0);
417 if (path_len > 0 && site->site_path[path_len - 1] == '/')
418 path_len--, site->site_isdir = 1;
419 site->site_path_len = path_len;
420 }
421 else {
422 site->site_isdir = is_host;
423 site->site_path = "";
424 site->site_path_len = 0;
425 }
426
427 site->site_wildcard = wildcard;
428 site->site_callback = callback;
429 site->site_magic = magic;
430
431 if (parent)
432 site->site_auth = parent->site_auth;
433
434 nth_site_set_params(site, ta_tags(ta));
435 }
436
437 ta_end(ta);
438
439 error:
440 su_home_deinit(home);
441 return site;
442 }
443
nth_site_destroy(nth_site_t * site)444 void nth_site_destroy(nth_site_t *site)
445 {
446 if (site == NULL)
447 return;
448
449 if (site->site_auth)
450 auth_mod_unref(site->site_auth), site->site_auth = NULL;
451
452 if (site->site_server->srv_sites == site) {
453 server_destroy(site->site_server);
454 }
455 }
456
457
nth_site_magic(nth_site_t const * site)458 nth_site_magic_t *nth_site_magic(nth_site_t const *site)
459 {
460 return site ? site->site_magic : NULL;
461 }
462
463
nth_site_bind(nth_site_t * site,nth_request_f * callback,nth_site_magic_t * magic)464 void nth_site_bind(nth_site_t *site,
465 nth_request_f *callback,
466 nth_site_magic_t *magic)
467 {
468 if (site) {
469 site->site_callback = callback;
470 site->site_magic = magic;
471 }
472 }
473
474
475 /** Get the site URL. @NEW_1_12_4. */
nth_site_url(nth_site_t const * site)476 url_t const *nth_site_url(nth_site_t const *site)
477 {
478 return site ? site->site_url : NULL;
479 }
480
481 /** Return server name and version */
nth_site_server_version(void)482 char const *nth_site_server_version(void)
483 {
484 return "nth/" NTH_VERSION;
485 }
486
487 /** Get the time last time served. @NEW_1_12_4. */
nth_site_access_time(nth_site_t const * site)488 su_time_t nth_site_access_time(nth_site_t const *site)
489 {
490 su_time_t const never = { 0, 0 };
491
492 return site ? site->site_access : never;
493 }
494
nth_site_set_params(nth_site_t * site,tag_type_t tag,tag_value_t value,...)495 int nth_site_set_params(nth_site_t *site,
496 tag_type_t tag, tag_value_t value, ...)
497 {
498 int n;
499 ta_list ta;
500
501 server_t *server;
502 int master;
503 msg_mclass_t const *mclass;
504 int mflags;
505 auth_mod_t *am;
506
507 if (site == NULL)
508 return (errno = EINVAL), -1;
509
510 server = site->site_server;
511 master = site == server->srv_sites;
512 am = site->site_auth;
513
514 mclass = server->srv_mclass;
515 mflags = server->srv_mflags;
516
517 ta_start(ta, tag, value);
518
519 n = tl_gets(ta_args(ta),
520 TAG_IF(master, NTHTAG_MCLASS_REF(mclass)),
521 TAG_IF(master, NTHTAG_MFLAGS_REF(mflags)),
522 NTHTAG_AUTH_MODULE_REF(am),
523 TAG_END());
524
525 if (n > 0) {
526 if (mclass)
527 server->srv_mclass = mclass;
528 else
529 server->srv_mclass = http_default_mclass();
530 server->srv_mflags = mflags;
531 auth_mod_ref(am), auth_mod_unref(site->site_auth), site->site_auth = am;
532 }
533
534 ta_end(ta);
535
536 return n;
537 }
538
nth_site_get_params(nth_site_t const * site,tag_type_t tag,tag_value_t value,...)539 int nth_site_get_params(nth_site_t const *site,
540 tag_type_t tag, tag_value_t value, ...)
541 {
542 int n;
543 ta_list ta;
544 server_t *server;
545 int master;
546 msg_mclass_t const *mclass;
547
548 if (site == NULL)
549 return (errno = EINVAL), -1;
550
551 server = site->site_server;
552 master = site == server->srv_sites;
553
554 if (master && server->srv_mclass != http_default_mclass())
555 mclass = server->srv_mclass;
556 else
557 mclass = NULL;
558
559 ta_start(ta, tag, value);
560
561 n = tl_tgets(ta_args(ta),
562 TAG_IF(master, NTHTAG_MCLASS(mclass)),
563 TAG_IF(master, NTHTAG_MFLAGS(server->srv_mflags)),
564 TAG_END());
565
566 ta_end(ta);
567
568 return n;
569 }
570
nth_site_get_stats(nth_site_t const * site,tag_type_t tag,tag_value_t value,...)571 int nth_site_get_stats(nth_site_t const *site,
572 tag_type_t tag, tag_value_t value, ...)
573 {
574 int n;
575 ta_list ta;
576
577 if (site == NULL)
578 return (errno = EINVAL), -1;
579
580 ta_start(ta, tag, value);
581
582 n = tl_tgets(ta_args(ta),
583 TAG_END());
584
585 ta_end(ta);
586
587 return n;
588 }
589
590 static
site_get_host(nth_site_t ** list,char const * host,char const * port)591 nth_site_t **site_get_host(nth_site_t **list, char const *host, char const *port)
592 {
593 nth_site_t *site;
594
595 assert(host);
596
597 for (; (site = *list); list = &site->site_next) {
598 if (host_cmp(host, site->site_url->url_host) == 0 &&
599 su_strcmp(port, site->site_url->url_port) == 0) {
600 break;
601 }
602 }
603
604 return list;
605 }
606
607 /** Find a place to insert site from the hierarchy.
608 *
609 * A resource can be either a 'dir' (name ends with '/') or 'file'.
610 * When a resource
611 */
612 static
site_get_rslot(nth_site_t * parent,char * path,char ** return_rest)613 nth_site_t **site_get_rslot(nth_site_t *parent, char *path,
614 char **return_rest)
615 {
616 nth_site_t *site, **prev;
617 size_t len;
618 int cmp;
619
620 assert(path);
621
622 if (path[0] == '\0')
623 return errno = EEXIST, NULL;
624
625 for (prev = &parent->site_kids; (site = *prev); prev = &site->site_next) {
626 cmp = strncmp(path, site->site_path, len = site->site_path_len);
627 if (cmp > 0)
628 break;
629 if (cmp < 0)
630 continue;
631 if (path[len] == '\0') {
632 if (site->site_isdir)
633 return *return_rest = path, prev;
634 return errno = EEXIST, NULL;
635 }
636 if (path[len] != '/' || site->site_path[len] != '/')
637 continue;
638
639 while (path[++len] == '/')
640 ;
641
642 return site_get_rslot(site, path + len, return_rest);
643 }
644
645 *return_rest = path;
646
647 return prev;
648 }
649
650 static char const site_nodir_match[] = "";
651
652 /** Find a subdir from site hierarchy */
653 static
site_get_subdir(nth_site_t * parent,char const * path,char const ** return_rest)654 nth_site_t *site_get_subdir(nth_site_t *parent,
655 char const *path,
656 char const **return_rest)
657 {
658 nth_site_t *site;
659 size_t len;
660 int cmp;
661
662 assert(path);
663
664 while (path[0] == '/') /* Skip multiple slashes */
665 path++;
666
667 if (path[0] == '\0')
668 return *return_rest = path, parent;
669
670 for (site = parent->site_kids; site; site = site->site_next) {
671 cmp = strncmp(path, site->site_path, len = site->site_path_len);
672 if (cmp > 0)
673 break;
674 if (cmp < 0)
675 continue;
676 if (path[len] == '\0')
677 return *return_rest = site_nodir_match, site;
678 if (site->site_path[len] == '/' && path[len] == '/')
679 return site_get_subdir(site, path + len + 1, return_rest);
680 }
681
682 return *return_rest = path, parent;
683 }
684
685
686 /* ----------------------------------------------------------------------
687 * Server functions
688 */
689
690 static char const * const http_tports[] =
691 {
692 "tcp", "tls", NULL
693 };
694
695 static char const * const http_no_tls_tports[] = { "tcp", NULL };
696
697 static tp_stack_class_t nth_server_class[1] =
698 {{
699 sizeof(nth_server_class),
700 server_request,
701 server_tport_error,
702 server_msg_create
703 }};
704
server_create(url_t const * url,tag_type_t tag,tag_value_t value,...)705 server_t *server_create(url_t const *url,
706 tag_type_t tag, tag_value_t value, ...)
707 {
708 server_t *srv;
709 msg_mclass_t const *mclass = NULL;
710 tp_name_t tpn[1] = {{ NULL }};
711 su_root_t *root = NULL;
712 http_server_t const *server = NULL;
713 int persistent = 0;
714 char const *server_str = SERVER_VERSION;
715 ta_list ta;
716
717 ta_start(ta, tag, value);
718 tl_gets(ta_args(ta),
719 NTHTAG_ROOT_REF(root),
720 NTHTAG_MCLASS_REF(mclass),
721 TPTAG_REUSE_REF(persistent),
722 HTTPTAG_SERVER_REF(server),
723 HTTPTAG_SERVER_STR_REF(server_str),
724 TAG_END());
725
726 if (!root || !url ||
727 (url->url_type != url_http && url->url_type != url_https)
728 || !(srv = su_home_new(sizeof(*srv)))) {
729 ta_end(ta);
730 return NULL;
731 }
732
733 tpn->tpn_proto = url_tport_default((enum url_type_e)url->url_type);
734 tpn->tpn_canon = url->url_host;
735 tpn->tpn_host = url->url_host;
736 tpn->tpn_port = url_port(url);
737
738 srv->srv_tports = tport_tcreate(srv, nth_server_class, root,
739 TPTAG_IDLE(600000),
740 TPTAG_TIMEOUT(300000),
741 ta_tags(ta));
742
743 srv->srv_persistent = persistent;
744 srv->srv_max_bodylen = 1 << 30; /* 1 GB */
745
746 if (tport_tbind(srv->srv_tports, tpn, http_tports,
747 TAG_END()) >= 0 ||
748 tport_tbind(srv->srv_tports, tpn, http_no_tls_tports,
749 TAG_END()) >= 0) {
750 srv->srv_root = root;
751 srv->srv_mclass = mclass ? mclass : http_default_mclass();
752 srv->srv_mflags = MSG_DO_CANONIC;
753
754 if (server)
755 srv->srv_server = http_server_dup(srv->srv_home, server);
756 else
757 srv->srv_server = http_server_make(srv->srv_home, server_str);
758
759 tport_get_params(srv->srv_tports,
760 TPTAG_QUEUESIZE_REF(srv->srv_queuesize),
761 TAG_END());
762 }
763 else {
764 SU_DEBUG_1(("nth_server_create: cannot bind transports "
765 URL_FORMAT_STRING "\n",
766 URL_PRINT_ARGS(url)));
767 server_destroy(srv), srv = NULL;
768 }
769
770 ta_end(ta);
771
772 return srv;
773 }
774
server_destroy(server_t * srv)775 void server_destroy(server_t *srv)
776 {
777 tport_destroy(srv->srv_tports);
778 su_timer_destroy(srv->srv_timer);
779 su_home_unref(srv->srv_home);
780 }
781
782 /** Process incoming request message */
783 static
server_request(server_t * srv,tport_t * tport,msg_t * request,void * arg,su_time_t now)784 void server_request(server_t *srv,
785 tport_t *tport,
786 msg_t *request,
787 void *arg,
788 su_time_t now)
789 {
790 nth_site_t *site = NULL, *subsite = NULL;
791 msg_t *response;
792 http_t *http = http_object(request);
793 http_host_t *h;
794 char const *host, *port, *path, *subpath = NULL;
795
796 /* Disable streaming */
797 if (msg_is_streaming(request)) {
798 msg_set_streaming(request, (enum msg_streaming_status)0);
799 return;
800 }
801
802 /* Create a response message */
803 response = server_msg_create(srv, 0, NULL, 0, NULL, NULL);
804 tport_tqueue(tport, response, TAG_END());
805
806 if (http && http->http_flags & MSG_FLG_TIMEOUT) {
807 server_reply(srv, tport, request, response, 400, "Request timeout");
808 return;
809 } else if (http && http->http_flags & MSG_FLG_TOOLARGE) {
810 server_reply(srv, tport, request, response, HTTP_413_ENTITY_TOO_LARGE);
811 return;
812 } else if (!http || !http->http_request ||
813 (http->http_flags & MSG_FLG_ERROR)) {
814 server_reply(srv, tport, request, response, HTTP_400_BAD_REQUEST);
815 return;
816 }
817
818 if (http->http_request->rq_version != http_version_1_0 &&
819 http->http_request->rq_version != http_version_1_1) {
820 server_reply(srv, tport, request, response, HTTP_505_HTTP_VERSION);
821 return;
822 }
823
824 h = http->http_host;
825
826 if (h) {
827 host = h->h_host, port = h->h_port;
828 }
829 else if (http->http_request->rq_url->url_host) {
830 host = http->http_request->rq_url->url_host;
831 port = http->http_request->rq_url->url_port;
832 }
833 else
834 host = NULL, port = NULL;
835
836 path = http->http_request->rq_url->url_path;
837
838 if (host)
839 site = *site_get_host(&srv->srv_sites, host, port);
840
841 if (site == NULL && !srv->srv_sites->site_strict)
842 site = srv->srv_sites;
843
844 if (path == NULL)
845 path = "";
846
847 if (path[0])
848 subsite = site_get_subdir(site, path, &subpath);
849
850 if (subsite)
851 subsite->site_access = now;
852 else if (site)
853 site->site_access = now;
854
855 if (site && subsite && subsite->site_isdir && subpath == site_nodir_match) {
856 /* Answer with 301 */
857 http_location_t loc[1];
858 http_location_init(loc);
859
860 *loc->loc_url = *site->site_url;
861
862 if (site->site_wildcard) {
863 if (http->http_host) {
864 loc->loc_url->url_host = http->http_host->h_host;
865 loc->loc_url->url_port = http->http_host->h_port;
866 }
867 else {
868 tp_name_t const *tpn = tport_name(tport); assert(tpn);
869 loc->loc_url->url_host = tpn->tpn_canon;
870 if (strcmp(url_port_default((enum url_type_e)loc->loc_url->url_type), tpn->tpn_port))
871 loc->loc_url->url_port = tpn->tpn_port;
872 }
873 }
874
875 loc->loc_url->url_root = 1;
876 loc->loc_url->url_path = subsite->site_url->url_path;
877
878 msg_header_add_dup(response, NULL, (msg_header_t *)loc);
879
880 server_reply(srv, tport, request, response, HTTP_301_MOVED_PERMANENTLY);
881 }
882 else if (subsite)
883 nth_site_request(srv, subsite, tport, request, http, subpath, response);
884 else if (site)
885 nth_site_request(srv, site, tport, request, http, path, response);
886 else
887 /* Answer with 404 */
888 server_reply(srv, tport, request, response, HTTP_404_NOT_FOUND);
889 }
890
server_tport_error(server_t * srv,tport_t * tport,int errcode,char const * remote)891 static void server_tport_error(server_t *srv,
892 tport_t *tport,
893 int errcode,
894 char const *remote)
895 {
896 su_log("\nth: tport: %s%s%s\n",
897 remote ? remote : "", remote ? ": " : "",
898 su_strerror(errcode));
899 }
900
901 /** Respond without creating a request structure */
server_reply(server_t * srv,tport_t * tport,msg_t * request,msg_t * response,int status,char const * phrase)902 static void server_reply(server_t *srv, tport_t *tport,
903 msg_t *request, msg_t *response,
904 int status, char const *phrase)
905 {
906 http_t *http;
907 http_payload_t *pl;
908 int close;
909 http_status_t st[1];
910 char const *req_version = NULL;
911
912 if (status < 200 || status >= 600)
913 status = 500, phrase = http_500_internal_server;
914
915 http = http_object(request);
916
917 if (http && http->http_request)
918 req_version = http->http_request->rq_version;
919
920 close = status >= 200 &&
921 (!srv->srv_persistent
922 || status == 400
923 || (http && http->http_request &&
924 http->http_request->rq_version != http_version_1_1)
925 || (http && http->http_connection &&
926 msg_params_find(http->http_connection->k_items, "close")));
927
928 msg_destroy(request);
929
930 http = http_object(response);
931
932 pl = http_payload_format(msg_home(response),
933 "<html>\n"
934 "<head><title>%u %s</title></head>\n"
935 "<body><h2>%u %s</h2></body>\n"
936 "</html>\n", status, phrase, status, phrase);
937
938 msg_header_insert(response, (msg_pub_t *)http, (msg_header_t *)pl);
939
940 if (req_version != http_version_0_9) {
941 http_status_init(st);
942 st->st_version = http_version_1_1;
943 st->st_status = status;
944 st->st_phrase = phrase;
945
946 http_add_tl(response, http,
947 HTTPTAG_STATUS(st),
948 HTTPTAG_SERVER(srv->srv_server),
949 HTTPTAG_CONTENT_TYPE_STR("text/html"),
950 HTTPTAG_SEPARATOR_STR("\r\n"),
951 TAG_IF(close, HTTPTAG_CONNECTION_STR("close")),
952 TAG_END());
953
954 msg_serialize(response, (msg_pub_t *)http);
955 } else {
956 /* Just send the response */
957 *msg_chain_head(response) = (msg_header_t *)pl;
958 close = 1;
959 }
960
961 if (tport_tqsend(tport, response, NULL,
962 TPTAG_CLOSE_AFTER(close),
963 TAG_END()) == -1) {
964 SU_DEBUG_3(("server_reply(): cannot queue response\n" VA_NONE));
965 tport_shutdown(tport, 2);
966 }
967
968 msg_destroy(response);
969 }
970
971 /** Create a new message for transport */
972 static
server_msg_create(server_t * srv,int flags,char const data[],usize_t dlen,tport_t const * tp,tp_client_t * tpc)973 msg_t *server_msg_create(server_t *srv, int flags,
974 char const data[], usize_t dlen,
975 tport_t const *tp, tp_client_t *tpc)
976 {
977 msg_t *msg = msg_create(srv->srv_mclass, srv->srv_mflags | flags);
978
979 return msg;
980 }
981
982 /* ----------------------------------------------------------------------
983 * 6) Server transactions
984 */
985
986 struct auth_info
987 {
988 nth_site_t *site;
989 nth_request_t *req;
990 http_t const *http;
991 char const *path;
992 };
993
994 static void nth_authentication_result(void *ai0, auth_status_t *as);
995
996 static
nth_site_request(server_t * srv,nth_site_t * site,tport_t * tport,msg_t * request,http_t * http,char const * path,msg_t * response)997 void nth_site_request(server_t *srv,
998 nth_site_t *site,
999 tport_t *tport,
1000 msg_t *request,
1001 http_t *http,
1002 char const *path,
1003 msg_t *response)
1004 {
1005 auth_mod_t *am = site->site_auth;
1006 nth_request_t *req;
1007 auth_status_t *as;
1008 struct auth_info *ai;
1009 size_t size = (am ? (sizeof *as) + (sizeof *ai) : 0) + (sizeof *req);
1010 int status;
1011
1012 req = su_zalloc(srv->srv_home, size);
1013
1014 if (req == NULL) {
1015 server_reply(srv, tport, request, response, HTTP_500_INTERNAL_SERVER);
1016 return;
1017 }
1018
1019 if (am)
1020 as = auth_status_init(req + 1, sizeof *as), ai = (void *)(as + 1);
1021 else
1022 as = NULL, ai = NULL;
1023
1024 req->req_server = srv;
1025 req->req_method = http->http_request->rq_method;
1026 req->req_method_name = http->http_request->rq_method_name;
1027 req->req_url = http->http_request->rq_url;
1028 req->req_version = http->http_request->rq_version;
1029
1030 req->req_tport = tport_incref(tport);
1031 req->req_request = request;
1032 req->req_response = response;
1033
1034 req->req_status = 100;
1035 req->req_close =
1036 !srv->srv_persistent
1037 || http->http_request->rq_version != http_version_1_1
1038 || (http->http_connection &&
1039 msg_params_find(http->http_connection->k_items, "close"));
1040
1041 if (am) {
1042 static auth_challenger_t const http_server_challenger[] =
1043 {{ HTTP_401_UNAUTHORIZED, http_www_authenticate_class }};
1044
1045 req->req_as = as;
1046
1047 as->as_method = http->http_request->rq_method_name;
1048 as->as_uri = path;
1049
1050 if (http->http_payload) {
1051 as->as_body = http->http_payload->pl_data;
1052 as->as_bodylen = http->http_payload->pl_len;
1053 }
1054
1055 auth_mod_check_client(am, as,
1056 http->http_authorization,
1057 http_server_challenger);
1058
1059 if (as->as_status == 100) {
1060 /* Stall transport - do not read more requests */
1061 if (tport_queuelen(tport) * 2 >= srv->srv_queuesize)
1062 tport_stall(tport);
1063
1064 as->as_callback = nth_authentication_result;
1065 as->as_magic = ai;
1066 ai->site = site;
1067 ai->req = req;
1068 ai->http = http;
1069 ai->path = path;
1070 return;
1071 }
1072 else if (as->as_status) {
1073 assert(as->as_status >= 200);
1074 nth_request_treply(req, as->as_status, as->as_phrase,
1075 HTTPTAG_HEADER((http_header_t *)as->as_response),
1076 HTTPTAG_HEADER((http_header_t *)as->as_info),
1077 TAG_END());
1078 nth_request_destroy(req);
1079 return;
1080 }
1081 }
1082
1083 req->req_in_callback = 1;
1084 status = site->site_callback(site->site_magic, site, req, http, path);
1085 req->req_in_callback = 0;
1086
1087 if (status != 0 && (status < 100 || status >= 600))
1088 status = 500;
1089
1090 if (status != 0 && req->req_status < 200) {
1091 nth_request_treply(req, status, NULL, TAG_END());
1092 }
1093
1094 if (req->req_status < 100) {
1095 /* Stall transport - do not read more requests */
1096 if (tport_queuelen(tport) * 2 >= srv->srv_queuesize)
1097 tport_stall(tport);
1098 }
1099
1100 if (status >= 200 || req->req_destroyed)
1101 nth_request_destroy(req);
1102 }
1103
nth_authentication_result(void * ai0,auth_status_t * as)1104 static void nth_authentication_result(void *ai0, auth_status_t *as)
1105 {
1106 struct auth_info *ai = ai0;
1107 nth_request_t *req = ai->req;
1108 int status;
1109
1110 if (as->as_status != 0) {
1111 assert(as->as_status >= 300);
1112 nth_request_treply(req, status = as->as_status, as->as_phrase,
1113 HTTPTAG_HEADER((http_header_t *)as->as_response),
1114 TAG_END());
1115 }
1116 else {
1117 req->req_in_callback = 1;
1118 status = ai->site->site_callback(ai->site->site_magic,
1119 ai->site,
1120 ai->req,
1121 ai->http,
1122 ai->path);
1123 req->req_in_callback = 0;
1124
1125 if (status != 0 && (status < 100 || status >= 600))
1126 status = 500;
1127
1128 if (status != 0 && req->req_status < 200) {
1129 nth_request_treply(req, status, NULL, TAG_END());
1130 }
1131 }
1132
1133 if (status >= 200 || req->req_destroyed)
1134 nth_request_destroy(req);
1135 }
1136
nth_request_destroy(nth_request_t * req)1137 void nth_request_destroy(nth_request_t *req)
1138 {
1139 if (req == NULL)
1140 return;
1141
1142 if (req->req_status < 200)
1143 nth_request_treply(req, HTTP_500_INTERNAL_SERVER, TAG_END());
1144
1145 req->req_destroyed = 1;
1146
1147 if (req->req_in_callback)
1148 return;
1149
1150 if (req->req_as)
1151 su_home_deinit(req->req_as->as_home);
1152
1153 tport_decref(&req->req_tport), req->req_tport = NULL;
1154 msg_destroy(req->req_request), req->req_request = NULL;
1155 msg_destroy(req->req_response), req->req_response = NULL;
1156 su_free(req->req_server->srv_home, req);
1157 }
1158
1159 /** Return request authentication status.
1160 *
1161 * @param req pointer to HTTP request object
1162 *
1163 * @retval Status code
1164 *
1165 * @since New in @VERSION_1_12_4
1166 */
nth_request_status(nth_request_t const * req)1167 int nth_request_status(nth_request_t const *req)
1168 {
1169 return req ? req->req_status : 400;
1170 }
1171
1172 /** Return request authentication status.
1173 *
1174 * @param req pointer to HTTP request object
1175 *
1176 * @retval Pointer to authentication status struct
1177 *
1178 * @note The authentication status struct is freed when the #nth_request_t
1179 * object is destroyed.
1180 *
1181 * @since New in @VERSION_1_12_4
1182 *
1183 * @sa AUTH
1184 */
nth_request_auth(nth_request_t const * req)1185 auth_status_t *nth_request_auth(nth_request_t const *req)
1186 {
1187 return req ? req->req_as : NULL;
1188 }
1189
nth_request_method(nth_request_t const * req)1190 http_method_t nth_request_method(nth_request_t const *req)
1191 {
1192 return req ? req->req_method : http_method_invalid;
1193 }
1194
nth_request_message(nth_request_t * req)1195 msg_t *nth_request_message(nth_request_t *req)
1196 {
1197 msg_t *retval = NULL;
1198
1199 if (req)
1200 retval = msg_ref_create(req->req_request);
1201
1202 return retval;
1203 }
1204
nth_request_treply(nth_request_t * req,int status,char const * phrase,tag_type_t tag,tag_value_t value,...)1205 int nth_request_treply(nth_request_t *req,
1206 int status, char const *phrase,
1207 tag_type_t tag, tag_value_t value, ...)
1208 {
1209 msg_t *response, *next = NULL;
1210 http_t *http;
1211 int retval = -1;
1212 int req_close, close;
1213 ta_list ta;
1214 http_header_t const *as_info = NULL;
1215
1216 if (req == NULL || status < 100 || status >= 600) {
1217 return -1;
1218 }
1219
1220 response = req->req_response;
1221 http = http_object(response);
1222
1223 if (status >= 200 && req->req_as)
1224 as_info = (http_header_t const *)req->req_as->as_info;
1225
1226 ta_start(ta, tag, value);
1227
1228 http_add_tl(response, http,
1229 HTTPTAG_SERVER(req->req_server->srv_server),
1230 HTTPTAG_HEADER(as_info),
1231 ta_tags(ta));
1232
1233 if (http->http_payload && !http->http_content_length) {
1234 http_content_length_t *l;
1235 http_payload_t *pl;
1236 size_t len = 0;
1237
1238 for (pl = http->http_payload; pl; pl = pl->pl_next)
1239 len += pl->pl_len;
1240
1241 if (len > UINT32_MAX)
1242 goto fail;
1243
1244 l = http_content_length_create(msg_home(response), (uint32_t)len);
1245
1246 msg_header_insert(response, (msg_pub_t *)http, (msg_header_t *)l);
1247 }
1248
1249 if (req->req_method == http_method_head && http->http_payload) {
1250 http_payload_t *pl;
1251
1252 for (pl = http->http_payload; pl; pl = pl->pl_next)
1253 msg_header_remove(response, (msg_pub_t *)http, (msg_header_t *)pl);
1254 }
1255
1256 http_complete_response(response, status, phrase,
1257 http_object(req->req_request));
1258
1259 if (!http->http_date) {
1260 http_date_t date[1];
1261 http_date_init(date)->d_time = msg_now();
1262 msg_header_add_dup(response, (msg_pub_t *)http, (msg_header_t*)date);
1263 }
1264
1265 if (status < 200) {
1266 close = 0;
1267 next = server_msg_create(req->req_server, 0, NULL, 0, NULL, NULL);
1268 }
1269 else {
1270 req_close = req->req_close;
1271
1272 close = (http->http_connection &&
1273 msg_params_find(http->http_connection->k_items, "close"));
1274
1275 if (req_close && !close && status >= 200) {
1276 close = 1;
1277 http_add_tl(response, http, HTTPTAG_CONNECTION_STR("close"), TAG_END());
1278 }
1279 }
1280
1281 msg_serialize(response, (msg_pub_t *)http);
1282
1283 retval = tport_tqsend(req->req_tport, response, next,
1284 TAG_IF(close, TPTAG_CLOSE_AFTER(1)),
1285 ta_tags(ta));
1286
1287 fail:
1288 ta_end(ta);
1289
1290 if (retval == 0)
1291 req->req_status = status;
1292
1293 return retval;
1294 }
1295