r17873 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r17872‎ | r17873 | r17874 >
Date:01:34, 23 November 2006
Author:river
Status:old
Tags:
Comment:
rename wfoo -> foo
Modified paths:
  • /trunk/willow/configure.in (modified) (history)
  • /trunk/willow/src/include/backend.h (added) (history)
  • /trunk/willow/src/include/cache.h (modified) (history)
  • /trunk/willow/src/include/config.h (added) (history)
  • /trunk/willow/src/include/flalloc.h (modified) (history)
  • /trunk/willow/src/include/flowio.h (modified) (history)
  • /trunk/willow/src/include/http.h (added) (history)
  • /trunk/willow/src/include/http_header.h (added) (history)
  • /trunk/willow/src/include/log.h (added) (history)
  • /trunk/willow/src/include/net.h (added) (history)
  • /trunk/willow/src/include/wbackend.h (deleted) (history)
  • /trunk/willow/src/include/wconfig.h (deleted) (history)
  • /trunk/willow/src/include/whttp.h (deleted) (history)
  • /trunk/willow/src/include/whttp_header.h (deleted) (history)
  • /trunk/willow/src/include/willow.h (modified) (history)
  • /trunk/willow/src/include/wlog.h (deleted) (history)
  • /trunk/willow/src/include/wnet.h (deleted) (history)
  • /trunk/willow/src/libwillow/libwillow.cc (modified) (history)
  • /trunk/willow/src/willow/Makefile.in (modified) (history)
  • /trunk/willow/src/willow/backend.cc (added) (history)
  • /trunk/willow/src/willow/cache.cc (modified) (history)
  • /trunk/willow/src/willow/chunking.cc (modified) (history)
  • /trunk/willow/src/willow/config.cc (added) (history)
  • /trunk/willow/src/willow/confparse.cc (modified) (history)
  • /trunk/willow/src/willow/flowio.cc (modified) (history)
  • /trunk/willow/src/willow/htcp.cc (modified) (history)
  • /trunk/willow/src/willow/http.cc (added) (history)
  • /trunk/willow/src/willow/http_header.cc (added) (history)
  • /trunk/willow/src/willow/log.cc (added) (history)
  • /trunk/willow/src/willow/net.cc (added) (history)
  • /trunk/willow/src/willow/wbackend.cc (deleted) (history)
  • /trunk/willow/src/willow/wconfig.cc (deleted) (history)
  • /trunk/willow/src/willow/whttp.cc (deleted) (history)
  • /trunk/willow/src/willow/whttp_header.cc (deleted) (history)
  • /trunk/willow/src/willow/willow.cc (modified) (history)
  • /trunk/willow/src/willow/wlog.cc (deleted) (history)
  • /trunk/willow/src/willow/wnet.cc (deleted) (history)

Diff [purge]

Index: trunk/willow/configure.in
@@ -4,7 +4,7 @@
55
66 USER_CXXFLAGS="$CXXFLAGS"
77 CPPFLAGS="-D_REENTRANT $CPPFLAGS"
8 -AC_CONFIG_HEADER(src/include/config.h)
 8+AC_CONFIG_HEADER(src/include/autoconf.h)
99
1010 echo ""
1111 echo "examining environment..."
Index: trunk/willow/src/willow/whttp.cc
@@ -1,1213 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * whttp: HTTP implementation.
7 - */
8 -
9 -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
10 -# pragma ident "@(#)$Id$"
11 -#endif
12 -
13 -/*
14 - * The logic of whttp is explained in whttp_entity.c
15 - */
16 -
17 -#ifndef _GNU_SOURCE
18 -# define _GNU_SOURCE /* glibc strptime */
19 -#endif
20 -
21 -#include <sys/types.h>
22 -#include <sys/stat.h>
23 -#include <sys/param.h>
24 -
25 -#include <cstdlib>
26 -#include <cstdio>
27 -#include <cstring>
28 -#include <unistd.h>
29 -#include <cerrno>
30 -#include <netdb.h>
31 -#include <fcntl.h>
32 -#include <cassert>
33 -#include <ctime>
34 -#include <fstream>
35 -#include <pthread.h>
36 -
37 -#include <utility>
38 -#include <deque>
39 -using std::deque;
40 -using std::min;
41 -using std::ofstream;
42 -using std::endl;
43 -
44 -#include "willow.h"
45 -#include "whttp.h"
46 -#include "wnet.h"
47 -#include "wbackend.h"
48 -#include "wconfig.h"
49 -#include "wlogwriter.h"
50 -#include "whttp_entity.h"
51 -#include "wlog.h"
52 -#include "radix.h"
53 -#include "chunking.h"
54 -#include "flowio.h"
55 -#include "format.h"
56 -#include "cache.h"
57 -
58 -using namespace wnet;
59 -
60 -#ifndef MAXHOSTNAMELEN
61 -# define MAXHOSTNAMELEN HOST_NAME_MAX /* SysV / BSD disagreement */
62 -#endif
63 -
64 -/*
65 - * Error handling.
66 - */
67 -#define ERR_NONE -1 /* error with no body */
68 -#define ERR_GENERAL 0 /* unspecified error */
69 -#define ERR_BADREQUEST 1 /* client request invalid */
70 -#define ERR_BADRESPONSE 2 /* backend response invalid */
71 -#define ERR_CACHE_IO 3 /* i/o failure reading cache */
72 -#define ERR_BLOCKED 4 /* client denied by configuration */
73 -
74 -static const char *error_files[] = {
75 - /* ERR_GENERAL */ DATADIR "/errors/ERR_GENERAL",
76 - /* ERR_BADREQUEST */ DATADIR "/errors/ERR_BADREQUEST",
77 - /* ERR_BADRESPONSE */ DATADIR "/errors/ERR_BADRESPONSE",
78 - /* ERR_CACHE_IO */ DATADIR "/errors/ERR_CACHE_IO",
79 - /* ERR_BLOCKED */ DATADIR "/errors/ERR_BLOCKED",
80 -};
81 -
82 -static void *client_thread(void *);
83 -static void stats_merge(int, short, void *);
84 -
85 -char via_hdr[1024];
86 -char *cache_hit_hdr;
87 -char *cache_miss_hdr;
88 -
89 -tss<event> merge_ev;
90 -
91 -char my_hostname[MAXHOSTNAMELEN + 1];
92 -static char my_version[64];
93 -static ofstream alf;
94 -lockable alf_lock;
95 -
96 -static int const default_udplog_port = 4445;
97 -wnet::socket *udplog_sock;
98 -static atomic<int> log_count;
99 -static bool do_udplog;
100 -
101 -struct error_transform_filter : io::buffering_filter, freelist_allocator<error_transform_filter>
102 -{
103 - string const _url;
104 - string const _errdata;
105 - string const _statstr;
106 - int _status;
107 -
108 - error_transform_filter(
109 - string const &url,
110 - string const &errdata,
111 - string const &statstr,
112 - int status);
113 -
114 - io::sink_result bf_transform(char const *, size_t, ssize_t &);
115 -};
116 -
117 -struct httpcllr : freelist_allocator<httpcllr> {
118 - /* Accept a new client and start processing it. */
119 - httpcllr(wsocket *, int);
120 - ~httpcllr();
121 -
122 - void start_request (void);
123 - void end_request (bool = true);
124 - void force_end_request (void);
125 -
126 - void start_backend_request (imstring const &host, imstring const &path);
127 - void start_backend_request (imstring const &url);
128 -
129 - /* reading request from client */
130 - void header_read_complete (void);
131 - void header_read_error (void);
132 - /* sending request to backend */
133 - void backend_ready (backend *, wsocket *, int);
134 - void backend_write_headers_done (void);
135 - void backend_write_body_done (void);
136 - void backend_write_error (void);
137 - /* reading request from backend */
138 - void backend_read_headers_done (void);
139 - void backend_read_headers_error (void);
140 - /* sending request to client */
141 - void send_headers_to_client_done (void);
142 - void send_headers_to_client_error (void);
143 - void send_body_to_client_done (void);
144 - void send_body_to_client_error (void);
145 - /* sending errors to the client */
146 - void send_error_to_client (void);
147 - void error_send_headers_done (void);
148 - void error_send_done (void);
149 -
150 - void send_error(int, char const *, int, char const *);
151 - void send_cached(void);
152 - void log_request (void);
153 -
154 - wsocket *_client_socket;
155 - backend *_backend;
156 - wsocket *_backend_socket;
157 -
158 - io::socket_spigot *_client_spigot;
159 - io::socket_spigot *_backend_spigot;
160 - io::socket_sink *_backend_sink,
161 - *_client_sink;
162 - header_parser *_header_parser,
163 - *_backend_headers;
164 - dechunking_filter *_dechunking_filter;
165 - header_spigot *_error_headers;
166 - io::file_spigot *_error_body;
167 - error_transform_filter *_error_filter;
168 - chunking_filter *_chunking_filter;
169 - io::size_limiting_filter *_size_limit;
170 - shared_ptr<cachedentity> _cachedent;
171 - caching_filter *_cache_filter;
172 - cached_spigot *_cache_spigot;
173 -
174 - backend_list *_blist;
175 - bool _denied;
176 - int _group;
177 - int _response;
178 - imstring _request_host;
179 - imstring _request_path;
180 - int _nredir;
181 - bool _validating;
182 -
183 -private:
184 - httpcllr(const httpcllr &);
185 -};
186 -
187 -httpcllr::httpcllr(wsocket *s, int gr)
188 - : _client_socket(s)
189 - , _backend(NULL)
190 - , _backend_socket(NULL)
191 - , _client_spigot(NULL)
192 - , _backend_spigot(NULL)
193 - , _backend_sink(NULL)
194 - , _client_sink(NULL)
195 - , _header_parser(NULL)
196 - , _backend_headers(NULL)
197 - , _dechunking_filter(NULL)
198 - , _error_headers(NULL)
199 - , _error_body(NULL)
200 - , _error_filter(NULL)
201 - , _chunking_filter(NULL)
202 - , _size_limit(NULL)
203 - , _cache_filter(NULL)
204 - , _cache_spigot(NULL)
205 - , _blist(NULL)
206 - , _denied(false)
207 - , _group(gr)
208 - , _response(0)
209 - , _nredir(0)
210 - , _validating(false)
211 -{
212 - /*
213 - * Check access controls.
214 - */
215 -pair<bool, uint16_t> acc = config.access.allowed(s->address().addr());
216 - if (!acc.first) {
217 - if (acc.second & whttp_deny_connect) {
218 - delete this;
219 - return;
220 - }
221 - _denied = true;
222 - }
223 -
224 - _client_spigot = new io::socket_spigot(_client_socket);
225 - start_request();
226 -}
227 -
228 -void
229 -httpcllr::start_request(void)
230 -{
231 - /*
232 - * Start by reading headers.
233 - */
234 - _header_parser = new header_parser;
235 -
236 - _client_spigot->completed_callee(this, &httpcllr::header_read_complete);
237 - _client_spigot->error_callee(this, &httpcllr::header_read_error);
238 -
239 - _client_spigot->sp_connect(_header_parser);
240 - _client_spigot->sp_uncork();
241 -}
242 -
243 -void
244 -httpcllr::force_end_request(void)
245 -{
246 - end_request(false);
247 -}
248 -
249 -void
250 -httpcllr::end_request(bool tryke)
251 -{
252 -bool can_keepalive = false;
253 - if (tryke && ((_header_parser->_http_vers == http11 &&
254 - !_header_parser->_no_keepalive) || _header_parser->_force_keepalive)) {
255 - can_keepalive = true;
256 - }
257 -
258 - delete _backend_spigot;
259 - _backend_spigot = NULL;
260 - delete _backend_sink;
261 - _backend_sink = NULL;
262 - delete _client_sink;
263 - _client_sink = NULL;
264 - delete _dechunking_filter;
265 - _dechunking_filter = NULL;
266 - delete _error_headers;
267 - _error_headers = NULL;
268 - delete _error_filter;
269 - _error_filter = NULL;
270 - delete _error_body;
271 - _error_body = NULL;
272 - delete _chunking_filter;
273 - _chunking_filter = NULL;
274 - delete _size_limit;
275 - _size_limit = NULL;
276 - delete _header_parser;
277 - _header_parser = NULL;
278 - delete _cache_filter;
279 - _cache_filter = NULL;
280 - delete _cache_spigot;
281 - _cache_spigot = NULL;
282 - if (_cachedent)
283 - entitycache.release(_cachedent);
284 - _cachedent.reset();
285 -
286 - /*
287 - * Return the backend to the keepalive pool, if we can.
288 - */
289 - if (_backend_socket && !_backend_headers->_no_keepalive &&
290 - _backend_headers->_http_vers == http11 && (!_blist || !_blist->failed())) {
291 - bpools.find(_group)->second.add_keptalive(
292 - make_pair(_backend_socket, _backend));
293 - } else {
294 - delete _backend_socket;
295 - }
296 -
297 - delete _blist;
298 - _blist = NULL;
299 - _backend_socket = NULL;
300 - delete _backend_headers;
301 - _backend_headers = NULL;
302 -
303 - _client_spigot->sp_disconnect();
304 - _client_spigot->sp_cork();
305 -
306 - if (can_keepalive && config.client_keepalive) {
307 - /*
308 - * leave the connection open, assuming they will send another
309 - * request (keep-alive).
310 - */
311 - start_request();
312 - return;
313 - }
314 -
315 - /*
316 - * No keep alive, close the connection.
317 - */
318 - delete this;
319 -}
320 -
321 -httpcllr::~httpcllr(void)
322 -{
323 - delete _client_spigot;
324 - delete _client_socket;
325 -}
326 -
327 -void
328 -httpcllr::send_cached(void)
329 -{
330 - if (_header_parser->_http_reqtype == REQTYPE_PURGE) {
331 - entitycache.purge(_cachedent);
332 - send_error(ERR_NONE, NULL, 200, "Object removed from cache");
333 - return;
334 - }
335 -
336 - _response = _cachedent->status_code();
337 -
338 - _cache_spigot = new cached_spigot(_cachedent);
339 - if (_header_parser->_force_keepalive)
340 - _cache_spigot->keepalive(true);
341 -
342 - if (!_client_sink)
343 - _client_sink = new io::socket_sink(_client_socket);
344 -
345 - _client_socket->cork();
346 - _cache_spigot->error_callee(this, &httpcllr::send_body_to_client_error);
347 - _cache_spigot->completed_callee(this, &httpcllr::send_body_to_client_done);
348 - _cache_spigot->sp_connect(_client_sink);
349 - _cache_spigot->sp_uncork();
350 - return;
351 -}
352 -
353 -void
354 -httpcllr::header_read_complete(void)
355 -{
356 - _request_host = _header_parser->_http_host;
357 - _request_path = _header_parser->_http_path;
358 -
359 - if (_denied) {
360 - send_error(ERR_BLOCKED, "You are not permitted to access this server.",
361 - 403, "Forbidden");
362 - return;
363 - }
364 -
365 - /*
366 - * Now parse the client's headers and decide what to do with
367 - * the request.
368 - */
369 - _header_parser->_headers.add("X-Forwarded-For", _client_socket->straddr(false).c_str());
370 -
371 - if (_header_parser->_http_reqtype == REQTYPE_POST) {
372 - if (_header_parser->_content_length == -1) {
373 - send_error(ERR_BADREQUEST, "POST request without content length",
374 - 400, "Bad request");
375 - return;
376 - }
377 - }
378 -
379 - _client_spigot->sp_disconnect();
380 -
381 - /*
382 - * See if this entity has been cached.
383 - */
384 - if (_header_parser->_http_reqtype != REQTYPE_POST) {
385 - bool created = false;
386 - string url = str(format("http://%s%s") % _header_parser->_http_host
387 - % _header_parser->_http_path);
388 - _cachedent = entitycache.find_cached(imstring(url), true, created);
389 - if (_cachedent) {
390 - if (_cachedent->complete()) {
391 - /* yes - complete object is available */
392 - if (_cachedent->expired()) {
393 - char dstr[64];
394 - struct tm tm;
395 - time_t mod = _cachedent->modified();
396 - WDEBUG(format(
397 - "HTTP: need to revalidate, %d refs")
398 - % _cachedent->refs());
399 - gmtime_r(&mod, &tm);
400 - /* need to revalidate */
401 - if (_cachedent->refs() == 1) {
402 - _validating = true;
403 - strftime(dstr, sizeof(dstr),
404 - "%a, %d %b %Y %H:%M:%S GMT", &tm);
405 - _header_parser->_headers.add(
406 - "If-Modified-Since", dstr);
407 - } else {
408 - entitycache.release(_cachedent);
409 - _cachedent.reset();
410 - }
411 - } else {
412 - send_cached();
413 - return;
414 - }
415 - } else if (!created) {
416 - /*
417 - * If the entity already exists but is not complete, we don't care
418 - * about it.
419 - */
420 - entitycache.release(_cachedent);
421 - _cachedent.reset();
422 - }
423 - }
424 - }
425 -
426 - /*
427 - * If we get here for a PURGE request, it means that the requested
428 - * object was not cached. So, send an error reply.
429 - */
430 - if (_header_parser->_http_reqtype == REQTYPE_PURGE) {
431 - send_error(ERR_NONE, NULL, 404, "Object not cached");
432 - return;
433 - }
434 -
435 -map<string,int>::iterator it;
436 -map<imstring,int>::iterator mit;
437 -pair<bool, uint16_t> acheck;
438 -
439 - if ((mit = host_to_bpool.find(_header_parser->_http_host)) !=
440 - host_to_bpool.end())
441 - _group = mit->second;
442 -
443 - if (!_header_parser->_http_backend.empty()) {
444 - acheck = config.force_backend.allowed(_client_socket->address().addr());
445 - if (acheck.first && acheck.second) {
446 - if ((it = poolnames.find(_header_parser->_http_backend))
447 - != poolnames.end()) {
448 - _group = it->second;
449 - }
450 - }
451 - }
452 -
453 - start_backend_request(_header_parser->_http_host,
454 - _header_parser->_http_path);
455 -}
456 -
457 -void
458 -httpcllr::start_backend_request(imstring const &url_)
459 -{
460 -char *url = url_.c_str(), *slash;
461 - if (strncasecmp(url, "http://", 7)) {
462 - send_error(ERR_BADRESPONSE,
463 - "Could not parse server redirect location",
464 - 503, "Internal server error");
465 - return;
466 - }
467 - url += 7;
468 - slash = strchr(url, '/');
469 - if (slash == NULL) {
470 - start_backend_request(url, "/");
471 - return;
472 - }
473 - start_backend_request(imstring(url, slash - url), slash);
474 -}
475 -
476 -void
477 -httpcllr::start_backend_request(imstring const &host, imstring const &path)
478 -{
479 -pair<wsocket *, backend *> ke;
480 -
481 - /*
482 - * Never try to send a POST request over keepalive - if the connection
483 - * breaks we don't have the POST data anymore so we can't retry.
484 - */
485 - if (_header_parser->_http_reqtype != REQTYPE_POST)
486 - ke = bpools.find(_group)->second.get_keptalive();
487 -
488 - _request_host = host;
489 - _request_path = path;
490 -
491 - delete _backend_sink;
492 - _backend_sink = NULL;
493 - delete _backend_spigot;
494 - _backend_spigot = NULL;
495 - delete _size_limit;
496 - _size_limit = NULL;
497 - delete _dechunking_filter;
498 - _dechunking_filter = NULL;
499 - delete _chunking_filter;
500 - _chunking_filter = NULL;
501 - delete _blist;
502 - _blist = NULL;
503 - delete _backend_headers;
504 - _backend_headers = NULL;
505 -
506 - if (ke.first) {
507 - backend_ready(ke.second, ke.first, 0);
508 - return;
509 - }
510 -
511 - if (!_blist)
512 - _blist = bpools.find(_group)->second.get_list(
513 - _request_path, _request_host);
514 -
515 - if (_blist->get(polycaller<backend *, wsocket *, int>(*this,
516 - &httpcllr::backend_ready), 0) == -1)
517 - backend_ready(NULL, NULL, 0);
518 -}
519 -
520 -void
521 -httpcllr::header_read_error(void)
522 -{
523 - WDEBUG(format("header read error errno=%s") % strerror(errno));
524 -
525 - if (_header_parser->_eof) {
526 - force_end_request();
527 - return;
528 - }
529 -
530 - stats.tcur->n_httpreq_fail++;
531 - send_error(ERR_BADREQUEST, "Could not parse client headers", 400, "Bad request");
532 -}
533 -
534 -void
535 -httpcllr::backend_ready(backend *be, wsocket *s, int)
536 -{
537 - if (be == NULL) {
538 - stats.tcur->n_httpreq_fail++;
539 - send_error(ERR_GENERAL, "No backends were available to serve your request",
540 - 503, "Internal server error");
541 - return;
542 - }
543 -
544 - /*
545 - * Create the backend socket_sink, connect the header parser to it
546 - * and start sending headers.
547 - */
548 - s->cork();
549 - _backend_socket = s;
550 - _backend = be;
551 - _backend_sink = new io::socket_sink(s);
552 -
553 - if (_request_host.size())
554 - _header_parser->_http_host = _request_host;
555 - if (_request_path.size())
556 - _header_parser->_http_path = _request_path;
557 -
558 - _header_parser->sending_restart();
559 - _header_parser->completed_callee(this, &httpcllr::backend_write_headers_done);
560 - _header_parser->error_callee(this, &httpcllr::backend_write_error);
561 - _header_parser->sp_connect(_backend_sink);
562 - _header_parser->sp_uncork();
563 -}
564 -
565 -void
566 -httpcllr::backend_write_error(void)
567 -{
568 - start_backend_request(_request_host, _request_path);
569 -}
570 -
571 -void
572 -httpcllr::backend_write_headers_done(void)
573 -{
574 - if (_header_parser->_http_reqtype == REQTYPE_POST) {
575 - /*
576 - * Connect the client to the backend and read the POST data.
577 - */
578 - _size_limit = new io::size_limiting_filter(_header_parser->_content_length);
579 - _client_spigot->sp_connect(_size_limit);
580 - _size_limit->sp_connect(_backend_sink);
581 -
582 - _client_spigot->completed_callee(this, &httpcllr::backend_write_body_done);
583 - _client_spigot->error_callee(this, &httpcllr::backend_write_error);
584 -
585 - _client_spigot->sp_uncork();
586 - return;
587 - }
588 - backend_write_body_done();
589 -}
590 -
591 -void
592 -httpcllr::backend_write_body_done(void)
593 -{
594 - _backend_socket->uncork();
595 -
596 - /*
597 - * Detach the backend sink and create a spigot to read the reply.
598 - */
599 - _header_parser->sp_disconnect();
600 -
601 - _backend_headers = new header_parser;
602 - _backend_headers->set_response();
603 -
604 - _backend_spigot = new io::socket_spigot(_backend_socket);
605 - _backend_spigot->completed_callee(this, &httpcllr::backend_read_headers_done);
606 - _backend_spigot->error_callee(this, &httpcllr::backend_read_headers_error);
607 - _backend_spigot->sp_connect(_backend_headers);
608 - _backend_spigot->sp_uncork();
609 -}
610 -
611 -void
612 -httpcllr::backend_read_headers_done(void)
613 -{
614 - _response = _backend_headers->_response;
615 -
616 - if (_validating && _response == 304) {
617 - /* Our cached entity was still valid */
618 - _cachedent->revalidated();
619 - send_cached();
620 - return;
621 - }
622 -
623 - /*
624 - * Check for X-Willow-Follow-Redirect header, which means we should
625 - * follow the redirect.
626 - */
627 - if (config.x_follow &&
628 - _backend_headers->_follow_redirect &&
629 - _backend_headers->_location.size() &&
630 - (_backend_headers->_response >= 300 && _backend_headers->_response < 400)) {
631 - if (config.max_redirects && (_nredir++ == config.max_redirects)) {
632 - send_error(ERR_BADRESPONSE, "Too many redirects in server reply",
633 - 503, "Internal server error");
634 - return;
635 - }
636 -
637 - start_backend_request(_backend_headers->_location);
638 - return;
639 - }
640 -
641 - /*
642 - * If we're caching this entity, store the headers.
643 - */
644 - if (_cachedent) {
645 - string status = str(format("HTTP/1.1 %s\r\n")
646 - % _backend_headers->_http_path);
647 - _cachedent->store_status(status, _backend_headers->_response);
648 - _cachedent->store_headers(_backend_headers->_headers);
649 - }
650 -
651 - /*
652 - * If the client is HTTP/1.0 or MSIE, we need to dechunk.
653 - */
654 - if (_backend_headers->_flags.f_chunked && _header_parser->_http_vers == http10)
655 - _backend_headers->_headers.remove("Transfer-Encoding");
656 - else if (_backend_headers->_flags.f_chunked && config.msie_hack && _header_parser->_is_msie)
657 - _backend_headers->_headers.remove("Transfer-Encoding");
658 -
659 - /*
660 - * Insert HTTP/1.0 keep-alive headers.
661 - */
662 - if (_header_parser->_force_keepalive) {
663 - _backend_headers->_headers.add("Keep-Alive", "300");
664 - }
665 -
666 - _backend_headers->_headers.add("X-Cache", cache_miss_hdr);
667 - _backend_headers->_headers.add("Via", via_hdr);
668 -
669 - /*
670 - * Send the headers to the client.
671 - */
672 - _backend_spigot->sp_disconnect();
673 -
674 - _client_socket->cork();
675 - _client_sink = new io::socket_sink(_client_socket);
676 - _backend_headers->completed_callee(this, &httpcllr::send_headers_to_client_done);
677 - _backend_headers->error_callee(this, &httpcllr::send_headers_to_client_error);
678 -
679 - _backend_headers->sp_connect(_client_sink);
680 - _backend_headers->sp_uncork();
681 -}
682 -
683 -void
684 -httpcllr::backend_read_headers_error(void)
685 -{
686 - /*
687 - * Try another backend...
688 - */
689 - start_backend_request(_request_host, _request_path);
690 -}
691 -
692 -void
693 -httpcllr::send_headers_to_client_done(void)
694 -{
695 -bool cache = false;
696 -io::spigot *cur = _backend_spigot;
697 -bool rechunk = false;
698 -bool chunked;
699 -
700 - /*
701 - * Now connect the backend directly to the client.
702 - */
703 - _backend_spigot->error_callee(this, &httpcllr::send_body_to_client_error);
704 - _backend_spigot->completed_callee(this, &httpcllr::send_body_to_client_done);
705 -
706 - /*
707 - * See if we can cache this entity.
708 - */
709 - if (_cachedent) {
710 - cache = true;
711 - _cache_filter = new caching_filter(_cachedent);
712 - }
713 -
714 - _backend_spigot->sp_disconnect();
715 -
716 - if (_backend_headers->_response == 304 && _backend_headers->_content_length < 0) {
717 - /* No body */
718 - send_body_to_client_done();
719 - return;
720 - }
721 -
722 - cur = _backend_spigot;
723 - chunked = _backend_headers->_flags.f_chunked;
724 -
725 - /*
726 - * If the client is HTTP/1.0 or MSIE, we need to dechunk.
727 - */
728 - if (chunked) {
729 - if (_header_parser->_http_vers == http10 ||
730 - (config.msie_hack && _header_parser->_is_msie)) {
731 - _dechunking_filter = new dechunking_filter;
732 - cur = &(*cur >> *_dechunking_filter);
733 - } else if (cache) {
734 - _dechunking_filter = new dechunking_filter;
735 - cur = &(*cur >> *_dechunking_filter);
736 - rechunk = true;
737 - }
738 - }
739 -
740 - /* if we got a content-length, insert a size limit */
741 - if (!chunked && _backend_headers->_content_length != -1) {
742 - _size_limit = new io::size_limiting_filter(
743 - _backend_headers->_content_length);
744 - cur = &(*cur >> *_size_limit);
745 - }
746 -
747 - /* if we're caching, insert the caching filter */
748 - if (cache) {
749 - cur = &(*cur >> *_cache_filter);
750 - /* if we dechunked it to cache, rechunk now */
751 - if (rechunk) {
752 - _chunking_filter = new chunking_filter;
753 - cur = &(*cur >> *_chunking_filter);
754 - }
755 - }
756 -
757 - /* finally, connect to the client */
758 - *cur >> *_client_sink;
759 -
760 - _client_sink->_counter = 0;
761 - _backend_spigot->sp_uncork();
762 -}
763 -
764 -
765 -void
766 -httpcllr::send_body_to_client_done(void)
767 -{
768 - _client_socket->uncork();
769 - stats.tcur->n_httpreq_ok++;
770 -
771 - log_request();
772 - end_request();
773 -}
774 -
775 -void
776 -httpcllr::send_body_to_client_error(void)
777 -{
778 - stats.tcur->n_httpreq_fail++;
779 - end_request();
780 -}
781 -
782 -void
783 -httpcllr::send_headers_to_client_error(void)
784 -{
785 - stats.tcur->n_httpreq_fail++;
786 - end_request();
787 -}
788 -
789 -/*
790 - * Initialize whttp, start loggers.
791 - */
792 -struct http_thread : freelist_allocator<http_thread> {
793 - pthread_t thr;
794 - pair<wnet::socket *, wnet::socket *>
795 - sv;
796 -
797 - void execute (void);
798 - void accept_wakeup (wsocket *, int);
799 -};
800 -vector<http_thread *> threads;
801 -
802 -void
803 -whttp_init(void)
804 -{
805 - int hsize;
806 -
807 - if (gethostname(my_hostname, MAXHOSTNAMELEN) < 0) {
808 - perror("gethostname");
809 - exit(8);
810 - }
811 -
812 - (void)strlcpy(my_version, "Willow/" PACKAGE_VERSION, 64);
813 - snprintf(via_hdr, sizeof(via_hdr), "1.1 %s (%s)", my_hostname, my_version);
814 -
815 - hsize = sizeof("MISS from ") + strlen(my_hostname);
816 - cache_hit_hdr = (char *)wmalloc(hsize + 1);
817 - cache_miss_hdr = (char *)wmalloc(hsize + 1);
818 -
819 - if (cache_hit_hdr == NULL || cache_miss_hdr == NULL)
820 - outofmemory();
821 -
822 - snprintf(cache_hit_hdr, hsize, "HIT from %s", my_hostname);
823 - snprintf(cache_miss_hdr, hsize, "MISS from %s", my_hostname);
824 -
825 - wlog.notice(format("whttp: starting %d worker threads")
826 - % config.nthreads);
827 - for (int i = 0; i < config.nthreads; ++i) {
828 - http_thread *t = new http_thread;
829 - pthread_attr_t attr;
830 - pthread_attr_init(&attr);
831 - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
832 - t->sv = wnet::socket::socketpair(st_dgram);
833 - wnet_add_accept_wakeup(t->sv.first);
834 - threads.push_back(t);
835 - pthread_create(&t->thr, &attr, client_thread, t);
836 - }
837 - whttp_header_init();
838 -}
839 -
840 -void
841 -http_thread::accept_wakeup(wsocket *s, int)
842 -{
843 -wsocket *socks[2];
844 -map<wsocket *, listener *>::iterator lsnit;
845 -
846 - if (s->read((char *)socks, sizeof(socks)) < (int)sizeof(socks)) {
847 - wlog.error(format("accept_wakeup: reading fd: %s")
848 - % strerror(errno));
849 - exit(1);
850 - }
851 - WDEBUG(format("accept_wakeup, lsnr = %d") % socks[1]);
852 - s->readback(polycaller<wsocket *, int>(*this,
853 - &http_thread::accept_wakeup), 0);
854 - if ((lsnit = sock2lsn.find(socks[1])) == sock2lsn.end())
855 - throw runtime_error("listener not found");
856 -
857 - ++lsnit->second->nconns;
858 - new httpcllr(socks[0], lsnit->second->group);
859 -}
860 -
861 -static
862 -void merge_sched(void)
863 -{
864 -timeval tv;
865 - tv.tv_usec = 250000;
866 - tv.tv_sec = 0;
867 - evtimer_set(merge_ev, stats_merge, NULL);
868 - event_base_set(evb, merge_ev);
869 - event_add(merge_ev, &tv);
870 -}
871 -
872 -static void *
873 -client_thread(void *arg)
874 -{
875 -http_thread *t = (http_thread *)arg;
876 - t->execute();
877 - return NULL;
878 -}
879 -
880 -void
881 -http_thread::execute(void)
882 -{
883 - make_event_base();
884 - stats.tcur = new stats_stru::abs_t;
885 - merge_ev = new event;
886 - memset(merge_ev, 0, sizeof(*merge_ev));
887 - merge_sched();
888 -
889 - sv.second->readback(polycaller<wsocket *, int>(*this,
890 - &http_thread::accept_wakeup), 0);
891 - event_base_loop(evb, 0);
892 - delete merge_ev;
893 - delete stats.tcur;
894 - return;
895 -}
896 -
897 -static void
898 -stats_merge(int, short, void *)
899 -{
900 -timeval tv = {0, 0};
901 - if (wnet_exit) {
902 - event_base_loopexit(evb, &tv);
903 - return;
904 - }
905 -
906 - { HOLDING(stats.cur_lock);
907 - stats.cur.n_httpreq_ok += stats.tcur->n_httpreq_ok;
908 - stats.tcur->n_httpreq_ok = 0;
909 - stats.cur.n_httpreq_fail += stats.tcur->n_httpreq_fail;
910 - stats.tcur->n_httpreq_fail = 0;
911 - stats.cur.n_httpresp_ok += stats.tcur->n_httpresp_ok;
912 - stats.tcur->n_httpreq_ok = 0;
913 - stats.cur.n_httpresp_fail += stats.tcur->n_httpresp_fail;
914 - stats.tcur->n_httpresp_fail = 0;
915 - }
916 - merge_sched();
917 -}
918 -
919 -void
920 -whttp_reconfigure(void)
921 -{
922 - /* file logging */
923 - if (config.access_log.size()) {
924 - alf.open(config.access_log.c_str(), ofstream::app);
925 - if (!alf.good()) {
926 - wlog.warn(format("opening %s: %s")
927 - % config.access_log % strerror(errno));
928 - }
929 - }
930 -
931 - /* UDP logging */
932 - if (config.udp_log) {
933 - if (config.udplog_port == 0)
934 - config.udplog_port = default_udplog_port;
935 -
936 - try {
937 - udplog_sock = wnet::socket::create(config.udplog_host,
938 - config.udplog_port, st_dgram, "UDP logger", prio_norm);
939 - udplog_sock->connect();
940 - } catch (socket_error &e) {
941 - wlog.warn(format(
942 - "connecting to UDP log host %s: %s; disabling UDP logging")
943 - % config.udplog_host % e.what());
944 - return;
945 - }
946 -
947 - do_udplog = true;
948 - wlog.notice(format("UDP logging to %s%s, sample rate 1/%d")
949 - % config.udplog_host
950 - % udplog_sock->straddr()
951 - % config.log_sample);
952 - }
953 -
954 -}
955 -
956 -void
957 -whttp_shutdown(void)
958 -{
959 - wfree(cache_hit_hdr);
960 - wfree(cache_miss_hdr);
961 -}
962 -
963 -static string
964 -errsafe(string const &s)
965 -{
966 -string::const_iterator it = s.begin(), end = s.end();
967 -string res;
968 - res.reserve((long) (s.size() * 1.2));
969 - for (; it != end; ++it)
970 - switch (*it) {
971 - case '<':
972 - res += "&lt;";
973 - break;
974 - case '>':
975 - res += "&gt;";
976 - break;
977 - case '"':
978 - res += "&quot;";
979 - break;
980 - case '\'':
981 - res += "&apos;";
982 - break;
983 - default:
984 - res += *it;
985 - }
986 - return res;
987 -}
988 -
989 -error_transform_filter::error_transform_filter(
990 - string const &url,
991 - string const &errdata,
992 - string const &statstr,
993 - int status)
994 - : _url(url)
995 - , _errdata(errdata)
996 - , _statstr(statstr)
997 - , _status(status) {
998 -}
999 -
1000 -io::sink_result
1001 -error_transform_filter::bf_transform(char const *buf, size_t len, ssize_t &discard)
1002 -{
1003 -string errtxt;
1004 -char const *p = buf;
1005 - errtxt.reserve((int) (len * 1.2));
1006 - while (p < buf + len) {
1007 - switch(*p) {
1008 - case '%':
1009 - if (p + 1 < buf + len) {
1010 - switch (*++p) {
1011 - case 'A':
1012 - errtxt += errsafe(config.admin);
1013 - break;
1014 - case 'U':
1015 - errtxt += _url;
1016 - break;
1017 - case 'D':
1018 - errtxt += current_time_str;
1019 - break;
1020 - case 'H':
1021 - errtxt += my_hostname;
1022 - break;
1023 - case 'E':
1024 - errtxt += errsafe(_errdata);
1025 - break;
1026 - case 'V':
1027 - errtxt += my_version;
1028 - break;
1029 - case 'C': {
1030 - char s[4];
1031 - sprintf(s, "%d", _status);
1032 - errtxt += s;
1033 - break;
1034 - }
1035 - case 'S':
1036 - errtxt += errsafe(_statstr);
1037 - break;
1038 - default:
1039 - errtxt += *p;
1040 - break;
1041 - }
1042 - p++;
1043 - continue;
1044 - }
1045 - break;
1046 - default:
1047 - errtxt += *p;
1048 - break;
1049 - }
1050 - ++p;
1051 - }
1052 -char *r;
1053 - r = new char[errtxt.size()];
1054 - memcpy(r, errtxt.data(), errtxt.size());
1055 - _buf.add(r, errtxt.size(), true);
1056 - discard += len;
1057 - return io::sink_result_okay;
1058 -}
1059 -
1060 -
1061 -void
1062 -httpcllr::send_error(int errnum, char const *errdata, int status, char const *statstr)
1063 -{
1064 -string url = "NONE";
1065 - _response = status;
1066 -
1067 - if (_request_path.size())
1068 - url = errsafe(_request_path.c_str());
1069 -
1070 - _error_headers = new header_spigot(status, statstr);
1071 - if (!_client_sink)
1072 - _client_sink = new io::socket_sink(_client_socket);
1073 -
1074 - _error_headers->add("Date", current_time_str);
1075 - _error_headers->add("Expires", current_time_str);
1076 - _error_headers->add("Server", my_version);
1077 -
1078 - _error_headers->add("Content-Type", "text/html;charset=UTF-8");
1079 -
1080 - if (errnum != ERR_NONE) {
1081 - _error_body = io::file_spigot::from_path(error_files[errnum], true);
1082 - if (_error_body == NULL) {
1083 - delete this;
1084 - return;
1085 - }
1086 - _error_filter = new error_transform_filter(url, errdata, statstr, status);
1087 - }
1088 -
1089 - _error_headers->completed_callee(this, &httpcllr::error_send_headers_done);
1090 - _error_headers->error_callee(this, &httpcllr::error_send_done);
1091 -
1092 - /*
1093 - * Can we chunk the error?
1094 - */
1095 - if (_error_body) {
1096 - if (_header_parser->_http_vers != http11)
1097 - _error_headers->add("Connection", "close");
1098 - else
1099 - _error_headers->add("Transfer-Encoding", "chunked");
1100 - } else
1101 - _error_headers->add("Content-Length", "0");
1102 -
1103 - _error_headers->sp_connect(_client_sink);
1104 - _error_headers->sp_uncork();
1105 -}
1106 -
1107 -void
1108 -httpcllr::error_send_headers_done(void)
1109 -{
1110 - if (!_error_body) {
1111 - error_send_done();
1112 - return;
1113 - }
1114 -
1115 - _error_headers->sp_disconnect();
1116 - _error_body->completed_callee(this, &httpcllr::error_send_done);
1117 - _error_body->error_callee(this, &httpcllr::error_send_done);
1118 -
1119 - _error_body->sp_connect(_error_filter);
1120 - if (_header_parser->_http_vers == http11) {
1121 - _chunking_filter = new chunking_filter;
1122 - _error_filter->sp_connect(_chunking_filter);
1123 - _chunking_filter->sp_connect(_client_sink);
1124 - } else {
1125 - _header_parser->_no_keepalive = true;
1126 - _header_parser->_force_keepalive = false;
1127 - _error_filter->sp_connect(_client_sink);
1128 - }
1129 -
1130 - _error_body->sp_uncork();
1131 -}
1132 -
1133 -void
1134 -httpcllr::error_send_done(void)
1135 -{
1136 - log_request();
1137 - end_request();
1138 -}
1139 -
1140 -void
1141 -httpcllr::log_request(void)
1142 -{
1143 -size_t size;
1144 -
1145 - if (_header_parser->_http_reqtype == REQTYPE_INVALID)
1146 - return;
1147 -
1148 - if (++log_count != config.log_sample)
1149 - return;
1150 - log_count = 0;
1151 -
1152 - if (_chunking_filter)
1153 - size = _chunking_filter->_counter;
1154 - else if (_dechunking_filter)
1155 - size = _dechunking_filter->_counter;
1156 - else
1157 - size = _client_sink->_counter;
1158 -
1159 - if (alf.is_open()) {
1160 - string line;
1161 - line = str(format("[%s] %s %s\"%s\" %d %d %s %s")
1162 - % (char *)current_time_short
1163 - % _client_socket->straddr(false)
1164 - % request_string[_header_parser->_http_reqtype]
1165 - % _request_path
1166 - % size
1167 - % _response
1168 - % (_backend ? _backend->be_name : string("-"))
1169 - % ((_cachedent && !_backend) ? "HIT" : "MISS"));
1170 -
1171 - HOLDING(alf_lock);
1172 -
1173 - if (!(alf << line << endl)) {
1174 - wlog.error(format(
1175 - "writing access log: %s; log will be closed")
1176 - % strerror(errno));
1177 - alf.close();
1178 - }
1179 - }
1180 -
1181 - if (do_udplog) {
1182 - char buf[65535];
1183 - char *bufp = buf, *endp = buf + sizeof(buf);
1184 - /*
1185 - * The log format is a packed binary strucure laid out like this:
1186 - *
1187 - * <curtime><addrlen><straddr><reqtype><pathlen><reqpath><status>
1188 - * <belen><bestr><cached><docsize>
1189 - *
1190 - * curtime is a 32-bit Unix timestamp. *len are the length in bytes
1191 - * of the next element. straddr is the ASCII IP address of the client.
1192 - * reqtype is an 8-bit integer:
1193 - * 0 - GET
1194 - * 1 - POST
1195 - * 2 - HEAD
1196 - * 3 - TRACE
1197 - * 4 - OPTIONS
1198 - * reqpath is the request path, including "http://" and the host.
1199 - * status is a 16-bit HTTP status code for the response.
1200 - * bestr is the ASCII IP address of the backend. cached is an
1201 - * 8-bit value, 1 if the request was served from the cache and 0 if not.
1202 - * docsize is the size of the response object, excluding headers.
1203 - */
1204 - ADD_UINT32(bufp, (uint32_t)time(NULL), endp);
1205 - ADD_STRING(bufp, _client_socket->straddr(false), endp);
1206 - ADD_UINT8(bufp, _header_parser->_http_reqtype, endp);
1207 - ADD_STRING(bufp, _request_path, endp);
1208 - ADD_UINT16(bufp, _response, endp);
1209 - ADD_STRING(bufp, string(_backend ? _backend->be_name : "-"), endp);
1210 - ADD_UINT8(bufp, (_cachedent && !_backend) ? 1 : 0, endp);
1211 - ADD_UINT32(bufp, size, endp);
1212 - udplog_sock->write(buf, bufp - buf);
1213 - }
1214 -}
Index: trunk/willow/src/willow/wlog.cc
@@ -1,134 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wlog: logging.
7 - */
8 -
9 -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
10 -# pragma ident "@(#)$Id$"
11 -#endif
12 -
13 -#include <boost/format.hpp>
14 -
15 -#include <cstdio>
16 -#include <cstdarg>
17 -#include <cstdlib>
18 -#include <cstring>
19 -#include <syslog.h>
20 -#include <cerrno>
21 -#include <iostream>
22 -using std::cout;
23 -using std::fopen;
24 -using std::fprintf;
25 -using std::fclose;
26 -
27 -#include "config.h"
28 -
29 -#include "wlog.h"
30 -#include "wnet.h"
31 -#include "wconfig.h"
32 -#include "format.h"
33 -
34 -logger wlog;
35 -
36 -static const char *sev_names[] = {
37 - "Debug",
38 - "Notice",
39 - "Warning",
40 - "Error",
41 -};
42 -
43 -static const int syslog_pri[] = {
44 - LOG_INFO,
45 - LOG_INFO,
46 - LOG_WARNING,
47 - LOG_ERR,
48 -};
49 -
50 -bool
51 -logger::open(void)
52 -{
53 - if (_syslog)
54 - openlog("willow", LOG_PID, _facility);
55 -
56 - if (_file.empty())
57 - return true;
58 -
59 - _fp = new ofstream(_file.c_str(), ios::app);
60 - if (!_fp->is_open()) {
61 - wlog.error(format("cannot open error log file %s: %s")
62 - % _file % strerror(errno));
63 - delete _fp;
64 - _fp = NULL;
65 - return false;
66 - }
67 -
68 - return true;
69 -}
70 -
71 -void
72 -logger::_log(log_level sev, string const &e)
73 -{
74 -string r;
75 -
76 - if (sev < _level)
77 - return;
78 -
79 - r = str(format("%s| %s: %s") % current_time_short % sev_names[sev] % e);
80 -
81 - HOLDING(_lock);
82 - if (_syslog)
83 - ::syslog(syslog_pri[sev], "%s", e.c_str());
84 -
85 - if (_fp) {
86 - if (!(*_fp << r << '\n')) {
87 - _fp->close();
88 - wlog.error(format("writing to logfile: %s")
89 - % strerror(errno));
90 - exit(8);
91 - }
92 - }
93 -
94 - if (config.foreground)
95 - cout << r << '\n';
96 -}
97 -
98 -void
99 -logger::close(void)
100 -{
101 - HOLDING(_lock);
102 -
103 - if (_fp)
104 - _fp->close();
105 -
106 - if (_syslog)
107 - closelog();
108 -}
109 -
110 -void
111 -logger::syslog(bool do_, int facility)
112 -{
113 - _syslog = do_;
114 - _facility = facility;
115 -}
116 -
117 -bool
118 -logger::file(string const &file)
119 -{
120 - _file = file;
121 - return true;
122 -}
123 -
124 -void
125 -logger::level(log_level l)
126 -{
127 - _level = l;
128 -}
129 -
130 -logger::logger(void)
131 - : _syslog(false)
132 - , _facility(0)
133 - , _level(ll_notice)
134 -{
135 -}
Index: trunk/willow/src/willow/wnet.cc
@@ -1,657 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wnet: Networking.
7 - */
8 -
9 -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
10 -# pragma ident "@(#)$Id$"
11 -#endif
12 -
13 -#include <sys/types.h>
14 -#include <sys/socket.h>
15 -#include <sys/ioctl.h>
16 -
17 -#include "config.h"
18 -#ifdef HAVE_SYS_SENDFILE_H
19 -# include <sys/sendfile.h>
20 -#endif
21 -
22 -#include <netinet/tcp.h>
23 -#include <net/if.h>
24 -#include <arpa/inet.h>
25 -
26 -#include <cstdio>
27 -#include <cstring>
28 -#include <cstdlib>
29 -#include <cerrno>
30 -#include <csignal>
31 -#include <cassert>
32 -#include <ctime>
33 -#include <deque>
34 -using std::deque;
35 -using std::signal;
36 -
37 -#include <unistd.h>
38 -#include <fcntl.h>
39 -
40 -#include "willow.h"
41 -#include "wnet.h"
42 -#include "wconfig.h"
43 -#include "wlog.h"
44 -#include "whttp.h"
45 -#include "format.h"
46 -
47 -#define RDBUF_INC 8192 /* buffer in 8 KiB incrs */
48 -
49 -struct event ev_sigint;
50 -struct event ev_sigterm;
51 -tss<event_base> evb;
52 -
53 -static void sig_exit(int, short, void *);
54 -
55 -ioloop_t *ioloop;
56 -
57 -char current_time_str[30];
58 -char current_time_short[30];
59 -time_t current_time;
60 -
61 -static void secondly_sched(void);
62 -
63 -bool wnet_exit;
64 -vector<wsocket *> awaks;
65 -size_t cawak;
66 -
67 -void
68 -wnet_add_accept_wakeup(wsocket *s)
69 -{
70 - awaks.push_back(s);
71 -}
72 -
73 -event secondly_ev;
74 -timeval secondly_tv;
75 -
76 -static void
77 -secondly_update(int, short, void *)
78 -{
79 - wnet_set_time();
80 - secondly_sched();
81 -}
82 -
83 -static void
84 -secondly_sched(void)
85 -{
86 - secondly_tv.tv_usec = 0;
87 - secondly_tv.tv_sec = 1;
88 - evtimer_set(&secondly_ev, secondly_update, NULL);
89 - event_base_set(evb, &secondly_ev);
90 - event_add(&secondly_ev, &secondly_tv);
91 -}
92 -
93 -ioloop_t::ioloop_t(void)
94 -{
95 - prepare();
96 -}
97 -
98 -void
99 -ioloop_t::prepare(void)
100 -{
101 -size_t i;
102 -
103 - wlog.notice(format("maximum number of open files: %d")
104 - % getdtablesize());
105 -
106 - signal(SIGPIPE, SIG_IGN);
107 -
108 - for (i = 0; i < listeners.size(); ++i) {
109 - listener *lns = listeners[i];
110 -
111 - try {
112 - lns->sock->reuseaddr(true);
113 - lns->sock->bind();
114 - lns->sock->listen();
115 - } catch (socket_error &e) {
116 - wlog.error(format("creating listener %s: %s")
117 - % lns->sock->straddr() % e.what());
118 - exit(8);
119 - }
120 -
121 - lns->sock->readback(polycaller<wsocket *, int>(*this, &ioloop_t::_accept), 0);
122 - }
123 - wlog.notice(format("wnet: initialised, using libevent %s (%s)")
124 - % event_get_version() % event_get_method());
125 - secondly_sched();
126 -}
127 -
128 -void
129 -ioloop_t::_accept(wsocket *s, int)
130 -{
131 - wsocket *newe;
132 -static atomic<time_t> last_nfile = 0;
133 - time_t now = time(NULL);
134 -
135 - if ((newe = s->accept("HTTP client", prio_norm)) == NULL) {
136 - if ((errno != ENFILE && errno != EMFILE) || (now - last_nfile) > 60) {
137 - if (errno == ENFILE || errno == EMFILE)
138 - last_nfile = now;
139 - wlog.warn(format("accept error: %s") % strerror(errno));
140 - }
141 - s->readback(polycaller<wsocket *, int>(*this, &ioloop_t::_accept), 0);
142 - return;
143 - }
144 -
145 - s->readback(polycaller<wsocket *, int>(*this, &ioloop_t::_accept), 0);
146 -
147 - newe->nonblocking(true);
148 -
149 - if (cawak == awaks.size())
150 - cawak = 0;
151 -char buf[sizeof(wsocket *) * 2];
152 - memcpy(buf, &newe, sizeof(newe));
153 - memcpy(buf + sizeof(newe), &s, sizeof(s));
154 - WDEBUG(format("_accept, lsnr=%d") % s);
155 -
156 - if (awaks[cawak]->write(buf, sizeof(wsocket *) * 2) < 0) {
157 - wlog.error(format("writing to thread wakeup socket: %s")
158 - % strerror(errno));
159 - exit(1);
160 - }
161 - cawak++;
162 - return;
163 -}
164 -
165 -void
166 -wnet_set_time(void)
167 -{
168 -struct tm *now;
169 - time_t old = current_time;
170 - size_t n;
171 -
172 - current_time = time(NULL);
173 - if (current_time == old)
174 - return;
175 -
176 - now = gmtime(&current_time);
177 -
178 - n = strftime(current_time_str, sizeof(current_time_str), "%a, %d %b %Y %H:%M:%S GMT", now);
179 - assert(n);
180 - n = strftime(current_time_short, sizeof(current_time_short), "%Y-%m-%d %H:%M:%S", now);
181 - assert(n);
182 -}
183 -
184 -namespace wnet {
185 -
186 -void
187 -socket::_ev_callback(int fd, short ev, void *d)
188 -{
189 -wsocket *s = (wsocket *)d;
190 -
191 - WDEBUG(format("_ev_callback: %s%son %d (%s)")
192 - % ((ev & EV_READ) ? "read " : "")
193 - % ((ev & EV_WRITE) ? "write " : "")
194 - % fd % s->_desc);
195 -
196 - if (ev & EV_READ)
197 - s->_read_handler(s);
198 - if (ev & EV_WRITE)
199 - s->_write_handler(s);
200 -}
201 -
202 -void
203 -socket::_register(int what, polycallback<wsocket *> handler)
204 -{
205 - int ev_flags = 0;
206 -
207 - WDEBUG(format("_register: %s%son %d (%s)")
208 - % ((what & FDE_READ) ? "read " : "")
209 - % ((what & FDE_WRITE) ? "write " : "")
210 - % _s % _desc);
211 -
212 - if (event_pending(&ev, EV_READ | EV_WRITE, NULL))
213 - event_del(&ev);
214 -
215 - if (what & FDE_READ) {
216 - _read_handler = handler;
217 - ev_flags |= EV_READ;
218 - }
219 - if (what & FDE_WRITE) {
220 - _write_handler = handler;
221 - ev_flags |= EV_WRITE;
222 - }
223 -
224 - event_set(&ev, _s, ev_flags, _ev_callback, this);
225 - event_base_set(evb, &ev);
226 - event_priority_set(&ev, (int) _prio);
227 - event_add(&ev, NULL);
228 -}
229 -
230 -address::address(void)
231 -{
232 - memset(&_addr, 0, sizeof(_addr));
233 - _addrlen = 0;
234 - _fam = AF_UNSPEC;
235 - _stype = _prot = 0;
236 -}
237 -
238 -address::address(sockaddr *sa, socklen_t len)
239 -{
240 - memcpy(&_addr, sa, len);
241 - _addrlen = len;
242 - _stype = _prot = 0;
243 - _fam = ((sockaddr_storage *)sa)->ss_family;
244 -}
245 -
246 -address::address(addrinfo *ai)
247 -{
248 - memcpy(&_addr, ai->ai_addr, ai->ai_addrlen);
249 - _addrlen = ai->ai_addrlen;
250 - _fam = ai->ai_family;
251 - _stype = ai->ai_socktype;
252 - _prot = ai->ai_protocol;
253 -}
254 -
255 -socket *
256 -address::makesocket(char const *desc, sprio p) const
257 -{
258 - return new socket(*this, desc, p);
259 -}
260 -
261 -address::address(address const &o)
262 - : _addrlen(o._addrlen)
263 - , _fam(o._fam)
264 - , _stype(o._stype)
265 - , _prot(o._prot) {
266 - memcpy(&_addr, &o._addr, _addrlen);
267 -}
268 -
269 -address &
270 -address::operator= (address const &o)
271 -{
272 - _addrlen = o._addrlen;
273 - _fam = o._fam;
274 - _stype = o._stype;
275 - _prot = o._prot;
276 - memcpy(&_addr, &o._addr, _addrlen);
277 - return *this;
278 -}
279 -
280 -string const &
281 -address::straddr(bool lng) const
282 -{
283 -char res[NI_MAXHOST];
284 -int i;
285 - if (!lng) {
286 - if (_shortaddr.empty()) {
287 - if ((i = getnameinfo((sockaddr *) &_addr, _addrlen,
288 - res, sizeof(res), NULL, 0, NI_NUMERICHOST)) != 0)
289 - throw resolution_error(i);
290 - _shortaddr = res;
291 - }
292 - return _shortaddr;
293 - }
294 -
295 - if (_straddr.empty()) {
296 - char port[NI_MAXSERV];
297 - if ((i = getnameinfo((sockaddr *) &_addr, _addrlen,
298 - res, sizeof(res), port, sizeof(port),
299 - NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
300 - throw resolution_error(i);
301 - _straddr = str(format("[%s]:%s") % res % port);
302 - }
303 - return _straddr;
304 -}
305 -
306 -addrlist *
307 -addrlist::resolve(string const &addr, string const &port,
308 - enum socktype socktype, int family)
309 -{
310 -addrinfo hints, *res, *ai;
311 -int r;
312 - memset(&hints, 0, sizeof(hints));
313 - hints.ai_socktype = (int) socktype;
314 - if (family != AF_UNSPEC)
315 - hints.ai_family = family;
316 -
317 - if ((r = getaddrinfo(addr.c_str(),
318 - port.c_str(), &hints, &res)) != 0)
319 - throw resolution_error(r);
320 -
321 -addrlist *al = new addrlist;
322 - for (ai = res; ai; ai = ai->ai_next)
323 - al->_addrs.push_back(address(ai));
324 -
325 - freeaddrinfo(res);
326 - return al;
327 -}
328 -
329 -address
330 -addrlist::first(string const &addr, int port,
331 - enum socktype socktype, int family)
332 -{
333 - return first(addr, lexical_cast<string>(port), socktype, family);
334 -}
335 -
336 -addrlist *
337 -addrlist::resolve(string const &addr, int port,
338 - enum socktype socktype, int family)
339 -{
340 - return resolve(addr, lexical_cast<string>(port), socktype, family);
341 -}
342 -
343 -address
344 -addrlist::first(string const &addr, string const &port,
345 - enum socktype socktype, int family)
346 -{
347 -addrlist *r = addrlist::resolve(addr, port, socktype, family);
348 -address res;
349 - res = *r->begin();
350 - delete r;
351 - return res;
352 -}
353 -
354 -addrlist::~addrlist(void)
355 -{
356 -}
357 -
358 -addrlist::iterator
359 -addrlist::begin(void) const
360 -{
361 - return _addrs.begin();
362 -}
363 -
364 -addrlist::iterator
365 -addrlist::end(void) const
366 -{
367 - return _addrs.end();
368 -}
369 -
370 -socket *
371 -addrlist::makesocket(char const *desc, sprio p) const
372 -{
373 -iterator it = _addrs.begin(), end = _addrs.end();
374 - for (; it != end; ++it) {
375 - socket *ns;
376 - if ((ns = it->makesocket(desc, p)) != NULL)
377 - return ns;
378 - }
379 - throw socket_error();
380 -}
381 -
382 -socket *
383 -socket::create(string const &addr, int port,
384 - enum socktype socktype, char const *desc, sprio p, int family)
385 -{
386 - return create(addr, lexical_cast<string>(port), socktype, desc, p, family);
387 -}
388 -
389 -socket *
390 -socket::create(string const &addr, string const &port,
391 - enum socktype socktype, char const *desc, sprio p, int family)
392 -{
393 -addrlist *al = addrlist::resolve(addr, port, socktype, family);
394 - return al->makesocket(desc, p);
395 -}
396 -
397 -pair<socket *, socket *>
398 -socket::socketpair(enum socktype st)
399 -{
400 -socket *s1 = NULL, *s2 = NULL;
401 -int sv[2];
402 - if (::socketpair(AF_UNIX, (int) st, 0, sv) == -1)
403 - throw socket_error();
404 - s1 = new socket(sv[0], wnet::address(), "socketpair", prio_norm);
405 - try {
406 - s2 = new socket(sv[1], wnet::address(), "socketpair", prio_norm);
407 - } catch (...) {
408 - delete s1;
409 - throw;
410 - }
411 - return make_pair(s1, s2);
412 -}
413 -
414 -connect_status
415 -socket::connect(void)
416 -{
417 - if (::connect(_s, _addr.addr(), _addr.length()) == -1)
418 - if (errno == EINPROGRESS)
419 - return connect_later;
420 - else
421 - throw socket_error();
422 - return connect_okay;
423 -}
424 -
425 -socket *
426 -socket::accept(char const *desc, sprio p)
427 -{
428 -int ns;
429 -sockaddr_storage addr;
430 -socklen_t addrlen = sizeof(addr);
431 - if ((ns = ::accept(_s, (sockaddr *)&addr, &addrlen)) == -1)
432 - return NULL;
433 - return new socket(ns, wnet::address((sockaddr *)&addr, addrlen), desc, p);
434 -}
435 -
436 -int
437 -socket::recvfrom(char *buf, size_t count, wnet::address &addr)
438 -{
439 -sockaddr_storage saddr;
440 -socklen_t addrlen = sizeof(addr);
441 -int i;
442 - if ((i = ::recvfrom(_s, buf, count, 0, (sockaddr *)&saddr, &addrlen)) < 0)
443 - return i;
444 - WDEBUG(format("recvfrom: fam=%d") % saddr.ss_family);
445 - addr = wnet::address((sockaddr *)&saddr, addrlen);
446 - return i;
447 -}
448 -
449 -int
450 -socket::sendto(char const *buf, size_t count, wnet::address const &addr)
451 -{
452 - return ::sendto(_s, buf, count, 0, addr.addr(), addr.length());
453 -}
454 -
455 -void
456 -socket::nonblocking(bool v)
457 -{
458 -int val;
459 - val = fcntl(_s, F_GETFL, 0);
460 - if (val == -1)
461 - throw socket_error();
462 - if (v)
463 - val |= O_NONBLOCK;
464 - else val &= ~O_NONBLOCK;
465 -
466 - if (fcntl(_s, F_SETFL, val) == -1)
467 - throw socket_error();
468 -}
469 -
470 -void
471 -socket::reuseaddr(bool v)
472 -{
473 -int i = v;
474 -int len = sizeof(i);
475 - setopt(SOL_SOCKET, SO_REUSEADDR, &i, len);
476 -}
477 -
478 -void
479 -socket::cork(void)
480 -{
481 -int one = 1;
482 - setopt(IPPROTO_TCP, TCP_CORK, &one, sizeof(one));
483 -}
484 -
485 -void
486 -socket::uncork(void)
487 -{
488 -int zero = 0;
489 - setopt(IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero));
490 -}
491 -
492 -int
493 -socket::getopt(int level, int what, void *addr, socklen_t *len) const
494 -{
495 -int i;
496 - if ((i = getsockopt(_s, level, what, addr, len)) == -1)
497 - throw socket_error();
498 - return i;
499 -}
500 -
501 -int
502 -socket::setopt(int level, int what, void *addr, socklen_t len)
503 -{
504 -int i;
505 - if ((i = setsockopt(_s, level, what, addr, len)) == -1)
506 - throw socket_error();
507 - return i;
508 -}
509 -
510 -int
511 -socket::error(void) const
512 -{
513 -int error = 0;
514 -socklen_t len = sizeof(error);
515 - try {
516 - getopt(SOL_SOCKET, SO_ERROR, &error, &len);
517 - return error;
518 - } catch (socket_error &) {
519 - return 0;
520 - }
521 -}
522 -
523 -socket::socket(int s, wnet::address const &a, char const *desc, sprio p)
524 - : _addr(a)
525 - , _desc(desc)
526 - , _prio(p)
527 -{
528 - memset(&ev, 0, sizeof(ev));
529 - _s = s;
530 -}
531 -
532 -socket::socket(wnet::address const &a, char const *desc, sprio p)
533 - : _addr(a)
534 - , _desc(desc)
535 - , _prio(p)
536 -{
537 - memset(&ev, 0, sizeof(ev));
538 - _s = ::socket(_addr.family(), _addr.socktype(), _addr.protocol());
539 - if (_s == -1)
540 - throw socket_error();
541 -}
542 -
543 -void
544 -socket::bind(void)
545 -{
546 - if (::bind(_s, _addr.addr(), _addr.length()) == -1)
547 - throw socket_error();
548 -}
549 -
550 -void
551 -socket::listen(int bl)
552 -{
553 - if (::listen(_s, bl) == -1)
554 - throw socket_error();
555 -}
556 -
557 -socket::~socket(void)
558 -{
559 - event_del(&ev);
560 - close(_s);
561 -}
562 -
563 -void
564 -socket::clearbacks(void)
565 -{
566 - event_del(&ev);
567 -}
568 -
569 -void
570 -socket::mcast_join(string const &ifname)
571 -{
572 - switch (_addr.family()) {
573 - case AF_INET: {
574 - struct address ifaddr = address::from_ifname(_s, ifname);
575 - sockaddr_in *inbind = (sockaddr_in *)_addr.addr();
576 - sockaddr_in *inif = (sockaddr_in *)ifaddr.addr();
577 - ip_mreq mr;
578 - memset(&mr, 0, sizeof(mr));
579 - mr.imr_multiaddr.s_addr = inbind->sin_addr.s_addr;
580 - mr.imr_interface.s_addr = inif->sin_addr.s_addr;
581 - WDEBUG(format("NET: %s joins mcast on if %s")
582 - % straddr() % ifaddr.straddr());
583 - setopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr));
584 - break;
585 - }
586 -
587 - case AF_INET6: {
588 - u_int ifindex = address::ifname_to_index(ifname);
589 - sockaddr_in6 *inbind = (sockaddr_in6 *)_addr.addr();
590 - ipv6_mreq mr;
591 - memset(&mr, 0, sizeof(mr));
592 - memcpy(&mr.ipv6mr_multiaddr, &inbind->sin6_addr,
593 - sizeof(mr.ipv6mr_multiaddr));
594 - mr.ipv6mr_interface = ifindex;
595 - setopt(IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(mr));
596 - break;
597 - }
598 -
599 - default:
600 - throw socket_error("multicast join not applicable for this socket type");
601 - }
602 -}
603 -
604 -u_int
605 -address::ifname_to_index(string const &ifname)
606 -{
607 -u_int ret = if_nametoindex(ifname.c_str());
608 - if (ret == 0)
609 - throw socket_error("named interface does not exist");
610 - return ret;
611 -}
612 -
613 -address
614 -address::from_ifname(int s, string const &ifname)
615 -{
616 -ifreq ifr;
617 - memset(&ifr, 0, sizeof(ifr));
618 - strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
619 - if (ioctl(s, SIOCGIFADDR, &ifr) < 0)
620 - throw socket_error();
621 -address ret(&ifr.ifr_addr, sizeof(sockaddr_in));
622 - return ret;
623 -}
624 -
625 -} // namespace wnet
626 -
627 -void
628 -make_event_base(void)
629 -{
630 -static lockable meb_lock;
631 - if (evb == NULL) {
632 - HOLDING(meb_lock);
633 - evb = (event_base *)event_init();
634 - event_base_priority_init(evb, prio_max);
635 - signal_set(&ev_sigint, SIGINT, sig_exit, NULL);
636 - signal_add(&ev_sigint, NULL);
637 - signal_set(&ev_sigterm, SIGTERM, sig_exit, NULL);
638 - signal_add(&ev_sigterm, NULL);
639 - }
640 -}
641 -
642 -void
643 -sig_exit(int sig, short what, void *d)
644 -{
645 - wnet_exit = true;
646 -}
647 -
648 -void
649 -ioloop_t::run(void)
650 -{
651 - while (!wnet_exit) {
652 - event_base_loop(evb, EVLOOP_ONCE);
653 - }
654 -
655 -size_t i;
656 - for (i = 0; i < listeners.size(); ++i)
657 - delete listeners[i];
658 -}
Index: trunk/willow/src/willow/whttp_header.cc
@@ -1,746 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * whttp_header: header processing implementation.
7 - */
8 -
9 -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
10 -# pragma ident "@(#)$Id$"
11 -#endif
12 -
13 -#if 0
14 -# define WILLOW_DEBUG
15 -#endif
16 -
17 -#include <vector>
18 -#include <cstring>
19 -#include <cerrno>
20 -using std::strlen;
21 -using std::vector;
22 -using std::sprintf;
23 -
24 -#include <assert.h>
25 -
26 -#include "config.h"
27 -#include "whttp_entity.h"
28 -#include "whttp_header.h"
29 -#include "wnet.h"
30 -#include "flowio.h"
31 -#include "wconfig.h"
32 -#include "format.h"
33 -
34 -using namespace wnet;
35 -
36 -enum {
37 - H_IGNORE,
38 - H_TRANSFER_ENCODING,
39 - H_CONTENT_LENGTH,
40 - H_USER_AGENT,
41 - H_HOST,
42 - H_CONNECTION,
43 - H_LOCATION,
44 - H_X_WILLOW_BACKEND_GROUP,
45 - H_X_WILLOW_FOLLOW_REDIRECT
46 -};
47 -
48 -#if 0
49 -#ifdef __GNUC__
50 -# include <ext/hash_map>
51 -typedef __gnu_cxx::hash_map<imstring,int> hmap_type;
52 -namespace __gnu_cxx {
53 -
54 -#define FNV_32_PRIME 0x01000193u
55 -
56 -template<>
57 -struct hash<imstring> {
58 - size_t operator() (imstring const &str) const {
59 - uint32_t hval = 0x811c9dc5u;
60 - unsigned char *q = (unsigned char *)str.c_str() + str.size() - 1,
61 - *s = q; /* unsigned string */
62 -
63 - while (*s && (q - s < 4)) {
64 -#ifndef __GNUC__
65 - hval *= FNV_32_PRIME;
66 -#else
67 - hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
68 -#endif
69 - hval ^= (uint32_t)std::tolower(*s--);
70 - }
71 - return hval;
72 - }
73 -};
74 -
75 -}
76 -#else
77 -typedef map<imstring, int> hmap_type;
78 -#endif
79 -
80 -hmap_type htypemap;
81 -#endif
82 -vector<pair<char const *, int> > htypemap;
83 -
84 -static struct htypent {
85 - char const *name;
86 - int n;
87 - size_t len;
88 -} list[] = {
89 - { "transfer-encoding", H_TRANSFER_ENCODING, 0 },
90 - { "content-length", H_CONTENT_LENGTH, 0 },
91 - { "user-agent", H_USER_AGENT, 0 },
92 - { "host", H_HOST, 0 },
93 - { "connection", H_CONNECTION, 0 },
94 - { "location", H_LOCATION, 0 },
95 - { "x-willow-backend-group", H_X_WILLOW_BACKEND_GROUP, 0 },
96 - { "x-willow-follow-redirect", H_X_WILLOW_FOLLOW_REDIRECT, 0 },
97 - { "keep-alive", H_IGNORE, 0 },
98 - { "te", H_IGNORE, 0 },
99 - { "trailers", H_IGNORE, 0 },
100 - { "upgrade", H_IGNORE, 0 },
101 - { "proxy-authenticate", H_IGNORE, 0 },
102 - { "proxy-connection", H_IGNORE, 0 },
103 - { 0, 0, 0 }
104 -};
105 -void
106 -whttp_header_init(void)
107 -{
108 - for (htypent *tit = list; tit->name; ++tit)
109 - tit->len = strlen(tit->name);
110 -}
111 -
112 -static inline int
113 -find_htype(char const *s, size_t slen)
114 -{
115 - for (htypent *tit = list; tit->name; ++tit)
116 - if (tit->len == slen && !strncasecmp(s, tit->name, slen))
117 - return tit->n;
118 - return -1;
119 -}
120 -
121 -#if 0
122 -int
123 -find_htype(char const *s, size_t slen)
124 -{
125 -hmap_type::iterator it;
126 - if (slen >= 24)
127 - return -1;
128 - it = htypemap.find(imstring(s, slen));
129 - if (it == htypemap.end())
130 - return -1;
131 - return it->second;
132 -}
133 -#endif
134 -
135 -const char *request_string[] = {
136 - "GET ",
137 - "POST ",
138 - "HEAD ",
139 - "TRACE ",
140 - "OPTIONS ",
141 - "TRACE ",
142 -};
143 -
144 -struct request_type supported_reqtypes[] = {
145 - { "GET", 3, REQTYPE_GET },
146 - { "POST", 4, REQTYPE_POST },
147 - { "HEAD", 4, REQTYPE_HEAD },
148 - { "TRACE", 5, REQTYPE_TRACE },
149 - { "OPTIONS", 7, REQTYPE_OPTIONS },
150 - { "PURGE", 5, REQTYPE_PURGE },
151 - { NULL, 0, REQTYPE_INVALID }
152 -};
153 -
154 -static int
155 -find_reqtype(char const *str, int len)
156 -{
157 - for (request_type *r = supported_reqtypes; r->name; r++)
158 - if (r->len == len && !memcmp(r->name, str, len))
159 - return r->type;
160 - return REQTYPE_INVALID;
161 -}
162 -
163 -pt_allocator<char> header::alloc;
164 -
165 -header::header(char const *n, size_t nlen, char const *v, size_t vlen)
166 - : hr_allocd(0)
167 -{
168 - assign(n, nlen, v, vlen);
169 -}
170 -
171 -header::header(header const &other)
172 - : hr_allocd(0)
173 -{
174 - assign(other.hr_name, strlen(other.hr_name),
175 - other.hr_value, strlen(other.hr_value));
176 -}
177 -
178 -void
179 -header::assign(char const *n, size_t nlen, char const *v, size_t vlen)
180 -{
181 -char *buf = hr_buffer;
182 - if (hr_allocd) {
183 - alloc.deallocate(hr_name, hr_allocd);
184 - hr_allocd = false;
185 - }
186 -
187 - if ((nlen + vlen + 2) >= HDR_BUFSZ) {
188 - buf = alloc.allocate(nlen + vlen + 2);
189 - hr_allocd = nlen + vlen + 2;
190 - }
191 -
192 - hr_name = buf;
193 - hr_value = buf + nlen + 1;
194 -
195 - memcpy(hr_name, n, nlen);
196 - memcpy(hr_value, v, vlen);
197 - hr_name[nlen] = hr_value[vlen] = '\0';
198 -}
199 -
200 -header&
201 -header::operator= (header const &other)
202 -{
203 - assign(other.hr_name, strlen(other.hr_name),
204 - other.hr_value, strlen(other.hr_value));
205 - return *this;
206 -}
207 -
208 -header::~header(void)
209 -{
210 - if (hr_allocd)
211 - alloc.deallocate(hr_name, hr_allocd);
212 -}
213 -
214 -void
215 -header::move(header &other)
216 -{
217 - /*
218 - * The other header is static, just copy the string.
219 - */
220 - if (!other.hr_allocd) {
221 - if (hr_allocd) {
222 - alloc.deallocate(hr_name, hr_allocd);
223 - hr_allocd = 0;
224 - }
225 -
226 - hr_name = hr_buffer;
227 - strcpy(hr_name, other.hr_name);
228 - hr_value = hr_buffer + strlen(hr_name) + 1;
229 - strcpy(hr_value, other.hr_value);
230 - return;
231 - }
232 -
233 - /*
234 - * The other header is allocd, steal its buffer.
235 - */
236 - hr_allocd = other.hr_allocd;
237 - hr_name = other.hr_name;
238 - hr_value = other.hr_value;
239 - other.hr_allocd = 0;
240 -}
241 -
242 -header_list::header_list()
243 - : hl_len(0)
244 -{
245 - hl_hdrs.reserve(20);
246 -}
247 -
248 -header_list::~header_list()
249 -{
250 - for (vector<header *, pt_allocator<header *> >::iterator
251 - it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it)
252 - delete *it;
253 -}
254 -
255 -void
256 -header_list::add(char const *name, size_t namelen, char const *value, size_t vallen)
257 -{
258 -header *h = new header(name, namelen, value, vallen);
259 -
260 - hl_hdrs.push_back(h);
261 - hl_last = h;
262 - hl_len += namelen + vallen + 4;
263 -}
264 -
265 -void
266 -header_list::add(char const *name, char const *value)
267 -{
268 - add(name, strlen(name), value, strlen(value));
269 -}
270 -
271 -void
272 -header_list::append_last(const char *append, size_t len)
273 -{
274 -int curnlen, curvlen;
275 - curnlen = strlen(hl_last->hr_name);
276 - curvlen = strlen(hl_last->hr_value);
277 -
278 - hl_len += len + 2;
279 -
280 -size_t nbufsz = curnlen + curvlen + 4 + len;
281 - /*
282 - * Simple case: not allocated, and new header fits in static buf.
283 - */
284 - if (!hl_last->hr_allocd && nbufsz < HDR_BUFSZ) {
285 - strcat(hl_last->hr_value, ", ");
286 - strncat(hl_last->hr_value, append, len);
287 - return;
288 - }
289 -
290 - /*
291 - * New header is too long.
292 - */
293 - if ((!hl_last->hr_allocd && nbufsz >= HDR_BUFSZ) ||
294 - (hl_last->hr_allocd && nbufsz >= hl_last->hr_allocd)) {
295 - char *nbuf = hl_last->alloc.allocate(nbufsz);
296 - strcpy(nbuf, hl_last->hr_name);
297 - sprintf(nbuf + curnlen + 1, "%s, %.*s",
298 - hl_last->hr_value, (int) len, append);
299 -
300 - if (hl_last->hr_allocd)
301 - hl_last->alloc.deallocate(hl_last->hr_name, hl_last->hr_allocd);
302 -
303 - hl_last->hr_name = nbuf;
304 - hl_last->hr_value = nbuf + curnlen + 1;
305 - hl_last->hr_allocd = nbufsz;
306 - return;
307 - }
308 -
309 - /* should not get here */
310 - abort();
311 -}
312 -
313 -void
314 -header_list::remove(const char *name)
315 -{
316 -vector<header *, pt_allocator<header *> >::iterator it, end;
317 - for (it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it) {
318 - if (strcasecmp((*it)->hr_name, name))
319 - continue;
320 - hl_len -= strlen((*it)->hr_name) + strlen((*it)->hr_value) + 4;
321 - (*it)->move(**hl_hdrs.rbegin());
322 - hl_hdrs.pop_back();
323 - return;
324 - }
325 -
326 -}
327 -
328 -struct header *
329 -header_list::find(const char *name)
330 -{
331 -vector<header *, pt_allocator<header *> >::iterator it, end;
332 - for (it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it) {
333 - if (strcasecmp((*it)->hr_name, name))
334 - continue;
335 - return *it;
336 - }
337 - return NULL;
338 -}
339 -
340 -header_list&
341 -header_list::operator= (header_list const &other)
342 -{
343 - for (vector<header *, pt_allocator<header *> >::iterator
344 - it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it)
345 - delete *it;
346 - hl_hdrs.clear();
347 - for (vector<header *, pt_allocator<header *> >::const_iterator
348 - it = other.hl_hdrs.begin(), end = other.hl_hdrs.end(); it != end; ++it)
349 - hl_hdrs.push_back(new header(**it));
350 - hl_len = other.hl_len;
351 - hl_last = *hl_hdrs.rbegin();
352 - return *this;
353 -}
354 -
355 -char *
356 -header_list::build(void)
357 -{
358 -char *buf;
359 -size_t bufsz;
360 -size_t buflen = 0;
361 -
362 - bufsz = hl_len + 3;
363 - buf = new char[bufsz];
364 -
365 - *buf = '\0';
366 -vector<header *, pt_allocator<header *> >::iterator it, end;
367 - for (it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it) {
368 - int incr;
369 - incr = strlen((*it)->hr_name);
370 - memcpy(buf + buflen, (*it)->hr_name, incr);
371 - buflen += incr;
372 - memcpy(buf + buflen, ": ", 2);
373 - buflen += 2;
374 - incr = strlen((*it)->hr_value);
375 - memcpy(buf + buflen, (*it)->hr_value, incr);
376 - buflen += incr;
377 - memcpy(buf + buflen, "\r\n", 2);
378 - buflen += 2;
379 - }
380 -
381 - memcpy(buf + buflen, "\r\n", 2);
382 -
383 - return buf;
384 -}
385 -
386 -io::sink_result
387 -header_parser::data_ready(char const *buf, size_t len, ssize_t &discard)
388 -{
389 -static char const *msie = "MSIE";
390 -char const *rn, *value, *name, *bufp = buf;
391 -size_t vlen, nlen, rnpos;
392 -int htype;
393 -
394 - WDEBUG(format("header parser: got [%s]") % string(buf, buf + len));
395 - while ((rn = find_rn(bufp, bufp + len)) != NULL) {
396 - WDEBUG(format("processing: [%s]") % string(bufp, rn));
397 - for (char const *c = bufp; c < rn; ++c)
398 - if (*(unsigned char *)c > 0x7f || !*c)
399 - return io::sink_result_error;
400 - WDEBUG("chars all okay");
401 -
402 - if (rn == bufp) {
403 - _sink_spigot->sp_cork();
404 - discard += bufp - buf + 2;
405 - /* request with no request is an error */
406 - if (!_got_reqtype)
407 - return io::sink_result_error;
408 - else {
409 - if (!_is_response && _http_host.empty()) {
410 - if (_http_vers == http11)
411 - return io::sink_result_error;
412 - else if (!config.default_host.empty()) {
413 - _headers.add("Host", config.default_host.c_str());
414 - _http_host = config.default_host;
415 - }
416 - }
417 - return io::sink_result_finished;
418 - }
419 - }
420 - rnpos = rn - bufp;
421 - name = bufp;
422 -
423 - if (!_got_reqtype) {
424 - if ((!_is_response && parse_reqtype(bufp, rn) == -1)
425 - || (_is_response && parse_response(bufp, rn) == -1)) {
426 - _sink_spigot->sp_cork();
427 - return io::sink_result_error;
428 - }
429 - _got_reqtype = true;
430 - goto next;
431 - }
432 -
433 - if (*name == ' ') {
434 - const char *s = name;
435 - /* continuation of last header */
436 - if (!_headers.hl_len)
437 - return io::sink_result_error;
438 - while (*s == ' ' && s < rn)
439 - s++;
440 - if (s < rn)
441 - _headers.append_last(s, rnpos - (s - name));
442 - goto next;
443 - }
444 -
445 - if ((value = (const char *)memchr(name, ':', rnpos)) == NULL) {
446 - _sink_spigot->sp_cork();
447 - return io::sink_result_error;
448 - }
449 - nlen = value - name;
450 -
451 - htype = find_htype(name, nlen);
452 - if (htype == H_IGNORE)
453 - goto next;
454 -
455 - value++;
456 - while (isspace(*value) && value < rn)
457 - value++;
458 - vlen = rn - value;
459 -
460 - switch (htype) {
461 - case H_TRANSFER_ENCODING:
462 - if (!strncasecmp(value, "chunked", vlen))
463 - _flags.f_chunked = 1;
464 - break;
465 - case H_CONTENT_LENGTH:
466 - _content_length = str10toint(value, vlen);
467 - break;
468 - case H_USER_AGENT:
469 - if (config.msie_hack &&
470 - std::search(value, value + vlen, msie, msie + 4) != value + vlen)
471 - _is_msie = true;
472 - break;
473 - case H_HOST:
474 - _http_host.assign(value, value + vlen);
475 - break;
476 - case H_CONNECTION:
477 - if (!strncasecmp(value, "close", vlen))
478 - _no_keepalive = true;
479 - else if (!strncasecmp(value, "keep-alive", vlen))
480 - _force_keepalive = true;
481 - goto next;
482 - case H_LOCATION:
483 - _location.assign(value, value + vlen);
484 - break;
485 - case H_X_WILLOW_BACKEND_GROUP:
486 - _http_backend.assign(value, value + vlen);
487 - break;
488 - case H_X_WILLOW_FOLLOW_REDIRECT:
489 - _follow_redirect = true;
490 - break;
491 - }
492 -
493 - _headers.add(name, nlen, value, vlen);
494 - next:
495 - len -= rn - bufp + 2;
496 - bufp = rn + 2;
497 - }
498 - discard += bufp - buf;
499 - return io::sink_result_okay;
500 -}
501 -
502 -int
503 -header_parser::parse_reqtype(char const *buf, char const *endp)
504 -{
505 -char const *path, *vers;
506 -size_t plen, vlen;
507 - if ((path = (char const *)memchr(buf, ' ', endp - buf)) == NULL)
508 - return -1;
509 - path++;
510 - if ((vers = (char const *)memchr(path, ' ', endp - path)) == NULL)
511 - return -1;
512 - plen = vers - path;
513 - vers++;
514 - vlen = endp - vers;
515 - if (vlen != 8)
516 - return -1;
517 - if (strncmp(vers, "HTTP/", 5))
518 - return -1;
519 - if (vers[5] != '1' || vers[6] != '.')
520 - return -1;
521 - if (vers[7] == '0')
522 - _http_vers = http10;
523 - else if (vers[7] == '1')
524 - _http_vers = http11;
525 - else return -1;
526 - if ((_http_reqtype = find_reqtype(buf, path - buf - 1)) == REQTYPE_INVALID)
527 - return -1;
528 -
529 - _http_path.assign(path, path + plen);
530 - return 0;
531 -}
532 -
533 -int
534 -header_parser::parse_response(char const *buf, char const *endp)
535 -{
536 -char const *errcode, *errdesc;
537 -int codelen, desclen;
538 - if ((errcode = (char const *)memchr(buf, ' ', endp - buf)) == NULL)
539 - return -1;
540 - if (errcode - buf != 8)
541 - return -1;
542 - errcode++;
543 - if ((errdesc = (char const *)memchr(errcode, ' ', endp - errcode)) == NULL)
544 - return -1;
545 - codelen = errdesc - errcode;
546 - errdesc++;
547 - desclen = endp - errdesc;
548 - if (strncmp(buf, "HTTP/", 5))
549 - return -1;
550 - if (buf[5] != '1' || buf[6] != '.')
551 - return -1;
552 - if (buf[7] == '0')
553 - _http_vers = http10;
554 - else if (buf[7] == '1')
555 - _http_vers = http11;
556 - else return -1;
557 -
558 - WDEBUG(format("parse_response: codelen=%d [%s] desclen=%d [%s]")
559 - % codelen % string(errcode, errcode + codelen)
560 - % desclen % string(errdesc, errdesc + desclen));
561 -
562 - _response = str10toint(errcode, codelen);
563 - _http_path.reserve(codelen + desclen + 1);
564 - _http_path.assign(errcode, errcode + codelen);
565 - _http_path.append(" ", 1);
566 - _http_path.append(errdesc, errdesc + desclen);
567 - return 0;
568 -}
569 -
570 -/*
571 - * Should never run out of data when reading headers.
572 - */
573 -io::sink_result
574 -header_parser::data_empty(void)
575 -{
576 - _sink_spigot->sp_cork();
577 - if (!_got_reqtype)
578 - _eof = true;
579 - return io::sink_result_error;
580 -}
581 -
582 -void
583 -header_parser::sp_cork(void)
584 -{
585 - _corked = true;
586 -}
587 -
588 -void
589 -header_parser::sending_restart(void)
590 -{
591 - _buf.reset();
592 - _built = false;
593 -}
594 -
595 -void
596 -header_parser::sp_uncork(void)
597 -{
598 - if (!_built) {
599 - char *s;
600 - if (!_is_response) {
601 - string req;
602 - req.reserve(strlen(request_string[_http_reqtype]) +
603 - _http_path.size() + 11);
604 -
605 - req = request_string[_http_reqtype];
606 - req += _http_path.c_str();
607 - req += " HTTP/1.";
608 - if (_http_host.size())
609 - req += "1\r\n";
610 - else req += "0\r\n";
611 - s = new char[req.size()];
612 - memcpy(s, req.data(), req.size());
613 - _buf.add(s, req.size(), true);
614 - } else {
615 - string req;
616 - req.reserve(_http_path.size() + 11);
617 - req = "HTTP/1.1 ";
618 - req += _http_path.c_str();
619 - req += "\r\n";
620 - s = new char[req.size()];
621 - memcpy(s, req.data(), req.size());
622 - _buf.add(s, req.size(), true);
623 - }
624 - s = _headers.build();
625 - _buf.add(s, _headers.hl_len, true);
626 - _buf.add("\r\n", 2, false);
627 - _built = true;
628 - }
629 -
630 - _corked = false;
631 - while (!_corked && _buf.items.size()) {
632 - wnet::buffer_item &b = *_buf.items.begin();
633 - ssize_t discard = 0;
634 - io::sink_result res;
635 - res = _sp_sink->data_ready(b.buf + b.off, b.len - b.off, discard);
636 - if (discard == 0)
637 - return;
638 - if ((size_t)discard == b.len) {
639 - _buf.items.pop_front();
640 - } else {
641 - b.len -= discard;
642 - b.off += discard;
643 - }
644 - switch (res) {
645 - case io::sink_result_finished:
646 - _sp_completed_callee();
647 - break;
648 - case io::sink_result_error:
649 - _sp_error_callee();
650 - return;
651 - case io::sink_result_blocked:
652 - sp_cork();
653 - return;
654 - case io::sink_result_okay:
655 - continue;
656 - }
657 - }
658 - this->_sp_data_empty();
659 - _sp_completed_callee();
660 -}
661 -
662 -void
663 -header_parser::set_response(void)
664 -{
665 - _is_response = true;
666 -}
667 -
668 -header_spigot::header_spigot(int errcode, char const *msg)
669 - : _built(false)
670 - , _corked(true)
671 -{
672 -char cstr[4];
673 - sprintf(cstr, "%d", errcode);
674 - _first = "HTTP/1.1 ";
675 - _first += cstr;
676 - _first += " ";
677 - _first += msg;
678 - _first += "\r\n";
679 -}
680 -
681 -void
682 -header_spigot::add(char const *h, char const *v)
683 -{
684 - _headers.add(h, v);
685 -}
686 -
687 -void
688 -header_spigot::body(string const &body)
689 -{
690 - _body = body;
691 -}
692 -
693 -void
694 -header_spigot::body(string &body)
695 -{
696 - _body.swap(body);
697 -}
698 -
699 -void
700 -header_spigot::sp_uncork(void)
701 -{
702 - _corked = false;
703 -
704 - if (!_built) {
705 - _buf.add(_first.data(), _first.size(), false);
706 - _buf.add(_headers.build(), _headers.hl_len, true);
707 - _buf.add("\r\n", 2, false);
708 - _buf.add(_body.data(), _body.size(), false);
709 - }
710 -
711 - while (!_corked && _buf.items.size()) {
712 - buffer_item &b = *_buf.items.begin();
713 - ssize_t discard = 0;
714 - io::sink_result res;
715 -
716 - res = _sp_sink->data_ready(b.buf + b.off, b.len, discard);
717 - if ((size_t)discard == b.len) {
718 - _buf.items.pop_front();
719 - } else {
720 - b.len -= discard;
721 - b.off += discard;
722 - }
723 - switch (res) {
724 - case io::sink_result_error:
725 - _sp_error_callee();
726 - return;
727 - case io::sink_result_finished:
728 - _sp_completed_callee();
729 - return;
730 - case io::sink_result_okay:
731 - continue;
732 - case io::sink_result_blocked:
733 - sp_cork();
734 - return;
735 - }
736 - }
737 - if (!_corked) {
738 - sp_cork();
739 - _sp_completed_callee();
740 - }
741 -}
742 -
743 -void
744 -header_spigot::sp_cork(void)
745 -{
746 - _corked = true;
747 -}
Index: trunk/willow/src/willow/wbackend.cc
@@ -1,342 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wbackend: HTTP backend handling.
7 - */
8 -
9 -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
10 -# pragma ident "@(#)$Id$"
11 -#endif
12 -
13 -#include <sys/types.h>
14 -#include <sys/socket.h>
15 -
16 -#include <arpa/inet.h>
17 -
18 -#include <cstdlib>
19 -#include <cstdio>
20 -#include <cstring>
21 -#include <cerrno>
22 -#include <climits>
23 -#include <cmath>
24 -#include <ctime>
25 -#include <algorithm>
26 -using std::sort;
27 -using std::pow;
28 -using std::rotate;
29 -
30 -#include "willow.h"
31 -#include "wbackend.h"
32 -#include "wnet.h"
33 -#include "wlog.h"
34 -#include "confparse.h"
35 -#include "wconfig.h"
36 -#include "format.h"
37 -
38 -map<imstring, int> host_to_bpool;
39 -map<int, backend_pool> bpools;
40 -map<string, int> poolnames;
41 -int nbpools = 1;
42 -
43 -struct backend_cb_data : freelist_allocator<backend_cb_data> {
44 -struct backend *bc_backend;
45 - polycallback<backend *, wsocket *> bc_func;
46 - void *bc_data;
47 -};
48 -
49 -backend::backend(
50 - string const &name,
51 - address const &addr)
52 -
53 - : be_name(name)
54 - , be_straddr(addr.straddr())
55 - , be_addr(addr)
56 - , be_dead(false)
57 - , be_hash(_carp_hosthash(be_straddr))
58 - , be_load(1.)
59 -{
60 - WDEBUG(format("adding backend with straddr [%s], hash %s")
61 - % be_straddr % be_hash);
62 -}
63 -
64 -backend_pool::backend_pool(string const &name_, lb_type lbt, int failgroup)
65 - : _lbtype(lbt)
66 - , _name(name_)
67 - , _failgroup(failgroup)
68 -{
69 - WDEBUG(format("creating backend_pool, lbt=%d") % (int) lbt);
70 -}
71 -
72 -void
73 -backend_pool::add(string const &addr, int port, int family)
74 -{
75 -addrlist *list;
76 - try {
77 - list = addrlist::resolve(addr, port, st_stream, family);
78 - } catch (resolution_error &e) {
79 - wlog.error(format("resolving %s: %s") % addr % e.what());
80 - return;
81 - }
82 -
83 -addrlist::iterator it = list->begin(), end = list->end();
84 -
85 - for (; it != end; ++it) {
86 - backends.push_back(new backend(addr, *it));
87 - wlog.notice(format("backend server: %s%s")
88 - % addr % it->straddr());
89 - }
90 -
91 - delete list;
92 - _carp_calc();
93 -}
94 -
95 -int
96 -backend_list::_get_impl(polycallback<backend *, wsocket *> cb)
97 -{
98 -struct backend_cb_data *cbd;
99 - wsocket *s = NULL;
100 -static time_t last_nfile;
101 - time_t now = time(NULL);
102 -
103 - /*
104 - * If we're delegating (for failover), pass this request off.
105 - */
106 - if (_delegate)
107 - return _delegate->_get_impl(cb);
108 -
109 - cbd = new backend_cb_data;
110 - cbd->bc_func = cb;
111 -
112 - for (;;) {
113 - cbd->bc_backend = _next_backend();
114 -
115 - if (cbd->bc_backend == NULL) {
116 - /*
117 - * All out of backends. See if we have a failover
118 - * group to try.
119 - */
120 - delete cbd;
121 - if (_failgroup != -1) {
122 - _delegate = bpools.find(_failgroup)->second.get_list("", "");
123 - return _delegate->_get_impl(cb);
124 - }
125 - return -1;
126 - }
127 -
128 - try {
129 - s = cbd->bc_backend->be_addr.makesocket(
130 - "backend connection", prio_backend);
131 - s->nonblocking(true);
132 - } catch (socket_error &e) {
133 - if (e.err() != ENFILE || now - last_nfile > 60)
134 - wlog.warn(format("opening backend socket: %s")
135 - % e.what());
136 - if (e.err() == ENFILE)
137 - last_nfile = now;
138 - delete cbd;
139 - delete s;
140 - return -1;
141 - }
142 -
143 - connect_status cs;
144 - try {
145 - cs = s->connect();
146 - } catch (socket_error &e) {
147 - time_t retry = time(NULL) + config.backend_retry;
148 - wlog.warn(format("%s: %s; retry in %d seconds")
149 - % cbd->bc_backend->be_name
150 - % e.what() % config.backend_retry);
151 - cbd->bc_backend->be_dead = 1;
152 - cbd->bc_backend->be_time = retry;
153 - delete s;
154 - continue;
155 - }
156 -
157 - if (cs == connect_later) {
158 - s->writeback(
159 - polycaller<wsocket *, backend_cb_data*>(*this,
160 - &backend_list::_backend_read), cbd);
161 - } else {
162 - cb(cbd->bc_backend, s);
163 - delete cbd;
164 - }
165 - return 0;
166 - }
167 -}
168 -
169 -void
170 -backend_list::_backend_read(wsocket *s, backend_cb_data *cbd)
171 -{
172 -int error = s->error();
173 -
174 - if (error && error != EINPROGRESS) {
175 - time_t retry = time(NULL) + config.backend_retry;
176 - wlog.warn(format("%s: %s; retry in %d seconds")
177 - % cbd->bc_backend->be_name
178 - % strerror(error)
179 - % config.backend_retry);
180 - cbd->bc_backend->be_dead = 1;
181 - cbd->bc_backend->be_time = retry;
182 - delete s;
183 - if (_get_impl(cbd->bc_func) == -1) {
184 - cbd->bc_func(NULL, NULL);
185 - }
186 - delete cbd;
187 - return;
188 - }
189 -
190 - cbd->bc_func(cbd->bc_backend, s);
191 - delete cbd;
192 -}
193 -
194 -backend_list::backend_list(
195 - backend_pool const &bp,
196 - imstring const &url,
197 - imstring const &host,
198 - int failgroup,
199 - lb_type lbt,
200 - int cur)
201 -
202 - : backends(bp.backends)
203 - , _cur(0)
204 - , _failgroup(failgroup)
205 - , _delegate(NULL)
206 -{
207 - WDEBUG(format("lbt = %d") % (int)lbt);
208 - rotate(backends.begin(), backends.begin() + cur, backends.end());
209 - if (lbt == lb_carp || lbt == lb_carp_hostonly)
210 - _carp_recalc(url, host, lbt);
211 -}
212 -
213 -backend_pool::~backend_pool(void)
214 -{
215 - for (size_t i = 0; i < backends.size(); ++i)
216 - delete backends[i];
217 -}
218 -
219 -backend_list *
220 -backend_pool::get_list(imstring const &url, imstring const &host)
221 -{
222 - if (_cur == 0)
223 - _cur = new size_t();
224 - if (*_cur >= backends.size())
225 - *_cur = 0;
226 -
227 - return new backend_list(*this, url, host, _failgroup, _lbtype, (*_cur)++);
228 -}
229 -
230 -struct backend *
231 -backend_list::_next_backend(void)
232 -{
233 -size_t tried = 0;
234 -
235 - while (tried++ <= backends.size()) {
236 - time_t now = time(NULL);
237 -
238 - if (_cur >= backends.size())
239 - _cur = 0;
240 -
241 - if (backends[_cur]->be_dead && now >= backends[_cur]->be_time)
242 - backends[_cur]->be_dead = 0;
243 -
244 - if (backends[_cur]->be_dead) {
245 - _cur++;
246 - continue;
247 - }
248 -
249 - return backends[_cur++];
250 - }
251 -
252 - return NULL;
253 -}
254 -
255 -void
256 -backend_pool::_carp_calc(void)
257 -{
258 -struct backend *be, *prev;
259 - size_t i, j;
260 -
261 - backends[0]->be_carp = (uint32_t) pow((double) (backends.size() * backends[0]->be_load), 1.0 / backends.size());
262 - backends[0]->be_carplfm = 1.0;
263 - for (i = 1; i < backends.size(); ++i) {
264 - float l = 0;
265 - be = backends[i];
266 - prev = backends[i - 1];
267 - be->be_carplfm = 1.0 + ((backends.size()-i+1) * (be->be_load - prev->be_load));
268 - for (j = 0; j < i; ++j)
269 - l *= backends[j]->be_carp;
270 - be->be_carp = (uint32_t) (be->be_carp / l);
271 - be->be_carp += (uint32_t) pow(prev->be_carp, (double) backends.size()-i+1);
272 - be->be_carp = (uint32_t) pow(be->be_carp, (double) 1/(backends.size()-i+1));
273 - }
274 -}
275 -
276 -int
277 -backend_pool::size(void) const
278 -{
279 - return backends.size();
280 -}
281 -
282 -string const &
283 -backend_pool::name(void) const
284 -{
285 - return _name;
286 -}
287 -
288 -void
289 -backend_pool::add_keptalive(pair<wsocket *, backend *>s)
290 -{
291 - if (!config.backend_keepalive)
292 - return;
293 -
294 - if (!_keptalive)
295 - _keptalive = new vector<pair<wsocket *, backend *> >;
296 - else while (config.keepalive_max && (_keptalive->size() >= (size_t)config.keepalive_max)) {
297 - delete _keptalive->begin()->first;
298 - _keptalive->erase(_keptalive->begin());
299 - }
300 -
301 - _keptalive->push_back(s);
302 -}
303 -
304 -pair<wsocket *, backend *>
305 -backend_pool::get_keptalive(void)
306 -{
307 - if (!config.backend_keepalive)
308 - return pair<wsocket *, backend *>(0, 0);
309 -
310 - if (!_keptalive)
311 - _keptalive = new vector<pair<wsocket *, backend *> >;
312 - if (_keptalive->empty())
313 - return pair<wsocket *, backend *>(0, 0);
314 -pair<wsocket *, backend *> ret = *_keptalive->rbegin();
315 - _keptalive->pop_back();
316 - return ret;
317 -}
318 -
319 -void
320 -backend_list::_carp_recalc(imstring const &url, imstring const &host, lb_type lbtype)
321 -{
322 - uint32_t hash = 0;
323 - size_t i;
324 - for (i = 0; i < backends.size(); ++i) {
325 - imstring s = url;
326 - if (lbtype == lb_carp_hostonly)
327 - s = host;
328 - hash = _carp_urlhash(s) ^ backends[i]->be_hash;
329 - hash += hash * 0x62531965;
330 - hash = rotl(hash, 21);
331 - hash *= (uint32_t) backends[i]->be_carplfm;
332 - backends[i]->be_carp = hash;
333 - WDEBUG(format("host for CARP: [%s] -> %d, be hash %d")
334 - % s % hash % backends[i]->be_hash);
335 - }
336 - sort(backends.begin(), backends.end(), _becarp_cmp);
337 -}
338 -
339 -int
340 -backend_list::_becarp_cmp(backend const *a, backend const *b)
341 -{
342 - return a->be_carp < b->be_carp ? true : false;
343 -}
Index: trunk/willow/src/willow/wconfig.cc
@@ -1,588 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wconfig: configuration.
7 - */
8 -
9 -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
10 -# pragma ident "@(#)$Id$"
11 -#endif
12 -
13 -#include <sys/types.h>
14 -#include <sys/socket.h>
15 -
16 -#include <netinet/in.h>
17 -#include <arpa/inet.h>
18 -#include <syslog.h>
19 -
20 -#include <cstdlib>
21 -#include <cstdio>
22 -#include <cstring>
23 -#include <cerrno>
24 -#include <climits>
25 -#include <netdb.h>
26 -#include <pthread.h>
27 -#include <set>
28 -#include <fstream>
29 -using std::ifstream;
30 -using std::set;
31 -using std::back_inserter;
32 -
33 -#include "willow.h"
34 -#include "wconfig.h"
35 -#include "wbackend.h"
36 -#include "wlog.h"
37 -#include "whttp.h"
38 -#include "wnet.h"
39 -#include "confparse.h"
40 -#include "radix.h"
41 -#include "format.h"
42 -
43 -using namespace conf;
44 -
45 -map<wsocket *, listener *> sock2lsn;
46 -set<int> used_pools;
47 -
48 -#define CONFIGFILE SYSCONFDIR "/willow.conf"
49 -
50 -vector<listener *> listeners;
51 -struct configuration config;
52 -
53 -static void
54 -set_backend(conf::tree_entry &e)
55 -{
56 -value const *val;
57 -int port = 80, family = AF_UNSPEC, gn = 0;
58 -string group = "<default>";
59 -map<string, int>::iterator it;
60 -
61 - if ((val = e/"port") != NULL)
62 - port = CONF_AINTVAL(*val);
63 - if ((val = e/"aftype") != NULL)
64 - if (val->cv_values[0].av_strval == "ipv6")
65 - family = AF_INET6;
66 - else family = AF_INET;
67 -
68 - if ((val = e/"group") != NULL) {
69 - group = val->cv_values[0].av_strval;
70 -
71 - it = poolnames.find(group);
72 - if (it == poolnames.end()) {
73 - val->report_error("backend group %s does not exist",
74 - group.c_str());
75 - return;
76 - } else
77 - gn = it->second;
78 - }
79 -
80 - used_pools.insert(gn);
81 - bpools.find(gn)->second.add(e.item_key, port, family);
82 -}
83 -
84 -static void
85 -set_listen(conf::tree_entry &e)
86 -{
87 -value const *val;
88 -int port = 80;
89 -struct listener *nl;
90 -int gn = 0;
91 -string group;
92 -int fam = AF_UNSPEC;
93 -addrlist *res;
94 -
95 - if ((val = e/"port") != NULL)
96 - port = CONF_AINTVAL(*val);
97 -
98 - if ((val = e/"aftype") != NULL)
99 - if (val->cv_values[0].av_strval == "ipv6")
100 - fam = AF_INET6;
101 - else fam = AF_INET;
102 -
103 - if ((val = e/"group") != NULL) {
104 - map<string, int>::iterator it;
105 - group = val->cv_values[0].av_strval;
106 -
107 - it = poolnames.find(group);
108 - if (it == poolnames.end()) {
109 - val->report_error("backend group %s does not exist",
110 - group.c_str());
111 - return;
112 - } else
113 - gn = it->second;
114 - }
115 -
116 - try {
117 - res = addrlist::resolve(e.item_key, port, st_stream, fam);
118 - } catch (socket_error &ex) {
119 - wlog.error(format("resolving %s: %s") % e.item_key % ex.what());
120 - return;
121 - }
122 -
123 -addrlist::iterator it = res->begin(), end = res->end();
124 - for (; it != end; ++it) {
125 - nl = new listener;
126 - nl->nconns = 0;
127 -
128 - try {
129 - nl->sock = it->makesocket("HTTP listener", prio_accept);
130 - } catch (socket_error &ex) {
131 - wlog.error(format("creating listener %s: %s")
132 - % e.item_key % ex.what());
133 - delete nl;
134 - delete res;
135 - return;
136 - }
137 - WDEBUG(format("listener %d has group %d") % nl->sock % gn);
138 - sock2lsn[nl->sock] = nl;
139 - used_pools.insert(gn);
140 -
141 - nl->port = port;
142 - nl->name = e.item_key;
143 - nl->group = gn;
144 - listeners.push_back(nl);
145 - wlog.notice(format("listening on %s%s (group %d)")
146 - % e.item_key % it->straddr() % gn);
147 - }
148 - delete res;
149 -}
150 -
151 -map<string, int> log_levels = map_list_of
152 - ("auth", LOG_AUTH)
153 - ("authpriv", LOG_AUTHPRIV)
154 - ("cron", LOG_CRON)
155 - ("daemon", LOG_DAEMON)
156 - ("ftp", LOG_FTP)
157 - ("kern", LOG_KERN)
158 - ("local0", LOG_LOCAL0)
159 - ("local1", LOG_LOCAL1)
160 - ("local2", LOG_LOCAL2)
161 - ("local3", LOG_LOCAL3)
162 - ("local4", LOG_LOCAL4)
163 - ("local5", LOG_LOCAL5)
164 - ("local6", LOG_LOCAL6)
165 - ("local7", LOG_LOCAL7)
166 - ("lpr", LOG_LPR)
167 - ("mail", LOG_MAIL)
168 - ("news", LOG_NEWS)
169 - ("syslog", LOG_SYSLOG)
170 - ("user", LOG_USER)
171 - ("uucp", LOG_UUCP)
172 - ;
173 -
174 -static bool
175 -validate_log_facility(tree_entry &, value &v)
176 -{
177 - if (!v.is_single(cv_string)) {
178 - v.report_error("expected single unquoted string");
179 - return false;
180 - }
181 -
182 - if (log_levels.find(v.cv_values[0].av_strval) == log_levels.end()) {
183 - v.report_error("log level does not exist");
184 - return false;
185 - }
186 -
187 - return true;
188 -}
189 -
190 -static void
191 -set_log_facility(tree_entry &, value &v)
192 -{
193 - wlog.syslog(true, log_levels.find(v.cv_values[0].av_strval)->second);
194 -}
195 -
196 -static bool
197 -v_udp_log(tree_entry &e, value &v)
198 -{
199 -bool ret = true;
200 - if (e/"udp-host" == NULL) {
201 - v.report_error("udp-host must be specified for UDP logging");
202 - ret = false;
203 - }
204 -
205 - if (!v.is_single(cv_yesno)) {
206 - v.report_error("udp-log must be yes/no");
207 - ret = false;
208 - }
209 -
210 - return ret;
211 -}
212 -
213 -static bool
214 -v_aftype(tree_entry &, value &v)
215 -{
216 - if (!v.is_single(cv_string)) {
217 - v.report_error("aftype must be single unquoted string");
218 - return false;
219 - }
220 -string &s = v.cv_values[0].av_strval;
221 - if (s != "ipv4" && s != "ipv6") {
222 - v.report_error("aftype must be \"ipv4\" or \"ipv6\"");
223 - return false;
224 - }
225 - return true;
226 -}
227 -
228 -static void
229 -radix_from_list(tree_entry &e, access_list &rad)
230 -{
231 -value *val;
232 -int immed = 0;
233 - if ((val = e/"apply-at") != NULL)
234 - if (val->cv_values[0].av_strval == "connect")
235 - immed = whttp_deny_connect;
236 -
237 - if ((val = e/"allow") != NULL) {
238 - vector<avalue>::iterator it = val->cv_values.begin(),
239 - end = val->cv_values.end();
240 - for (; it != end; ++it)
241 - rad.allow(it->av_strval, immed);
242 - }
243 -
244 - if ((val = e/"deny") != NULL) {
245 - vector<avalue>::iterator it = val->cv_values.begin(),
246 - end = val->cv_values.end();
247 - for (; it != end; ++it)
248 - rad.deny(it->av_strval, immed);
249 - }
250 -}
251 -
252 -static void
253 -set_access(tree_entry &e)
254 -{
255 - radix_from_list(e, config.access);
256 -}
257 -
258 -static void
259 -stats_access(tree_entry &, value &v)
260 -{
261 -vector<avalue>::iterator it = v.cv_values.begin(),
262 - end = v.cv_values.end();
263 - for (; it != end; ++it)
264 - stats.access.allow(it->av_strval);
265 -}
266 -
267 -static void
268 -force_backend_access(tree_entry &, value &v)
269 -{
270 -vector<avalue>::iterator it = v.cv_values.begin(),
271 - end = v.cv_values.end();
272 - for (; it != end; ++it)
273 - config.force_backend.allow(it->av_strval, 1);
274 -}
275 -
276 -static bool
277 -radix_prefix(tree_entry &, value &v)
278 -{
279 -vector<avalue>::iterator it = v.cv_values.begin(),
280 - end = v.cv_values.end();
281 - for (; it != end; ++it) {
282 - if (it->av_type != cv_qstring) {
283 - v.report_error("access prefix must be a list of quoted strings");
284 - return false;
285 - }
286 -
287 - try {
288 - prefix p(it->av_strval);
289 - } catch (invalid_prefix& e) {
290 - v.report_error("%s: %s", it->av_strval.c_str(), e.what());
291 - return false;
292 - }
293 - }
294 - return true;
295 -}
296 -
297 -bool
298 -v_apply_at(tree_entry &, value &v)
299 -{
300 - if (!v.is_single(cv_string)) {
301 - v.report_error("apply-at must be single unquoted string");
302 - return false;
303 - }
304 -string &s = v.cv_values[0].av_strval;
305 - if (s != "connect" && s != "request") {
306 - v.report_error("expected \"connect\" or \"request\"");
307 - return false;
308 - }
309 - return true;
310 -}
311 -
312 -bool
313 -v_lb_type(tree_entry &, value &v)
314 -{
315 - if (!v.is_single(cv_string)) {
316 - v.report_error("lb-type must be single unquoted string");
317 - return false;
318 - }
319 -string &s = v.cv_values[0].av_strval;
320 - if (s != "rr" && s != "carp" && s != "carp-host") {
321 - v.report_error("expected \"rr\", \"carp\" or \"carp-host\"");
322 - return false;
323 - }
324 - return true;
325 -}
326 -
327 -void
328 -set_backend_group(tree_entry &e)
329 -{
330 -int gn, fogroup = -1;
331 -string group;
332 -map<string, int>::iterator it;
333 - group = e.item_key;
334 -
335 - it = poolnames.find(group);
336 - if (it == poolnames.end()) {
337 - gn = nbpools;
338 - poolnames[group] = nbpools++;
339 - } else
340 - gn = it->second;
341 -
342 -lb_type lbtype = lb_rr;
343 -value *v;
344 - if ((v = e/"lb-type") != NULL) {
345 - string &s = v->cv_values[0].av_strval;
346 - if (s == "rr")
347 - lbtype = lb_rr;
348 - else if (s == "carp")
349 - lbtype = lb_carp;
350 - else if (s == "carp-host")
351 - lbtype = lb_carp_hostonly;
352 - }
353 -
354 - if ((v = e/"failover-group") != NULL) {
355 - if ((it = poolnames.find(v->cv_values[0].av_strval)) == poolnames.end()) {
356 - v->report_error("failover-group does not exist");
357 - } else {
358 - fogroup = it->second;
359 - }
360 - }
361 -
362 - WDEBUG(format("adding backend %d type = %d") % gn % (int) lbtype);
363 - bpools.insert(make_pair(gn, backend_pool(e.item_key, lbtype, fogroup)));
364 -
365 - if ((v = e/"hosts") != NULL) {
366 - vector<avalue>::iterator hit = v->cv_values.begin(), hend = v->cv_values.end();
367 - for (; hit != hend; ++hit)
368 - host_to_bpool[imstring(hit->av_strval)] = gn;
369 - used_pools.insert(gn);
370 - }
371 -}
372 -
373 -bool
374 -v_hosts(tree_entry &, value &v)
375 -{
376 -vector<avalue>::iterator it = v.cv_values.begin(), end = v.cv_values.end();
377 - for (; it != end; ++it)
378 - if (it->av_type != cv_qstring) {
379 - v.report_error("hosts must be a list of quoted strings");
380 - return false;
381 - }
382 - return true;
383 -}
384 -
385 -void
386 -set_cache_dir(tree_entry &e)
387 -{
388 - config.cachedirs.push_back(cachedir(e.item_key));
389 -}
390 -
391 -void
392 -set_htcp_keys(tree_entry &e, value &v)
393 -{
394 -string file = v.cv_values[0].av_strval;
395 -ifstream f(file.c_str());
396 - if (!f.is_open()) {
397 - v.report_error("cannot open HTCP key file %s: %s",
398 - file.c_str(), strerror(errno));
399 - return;
400 - }
401 -
402 -string s;
403 -int line = 0;
404 - while (getline(f, s)) {
405 - string name, key;
406 - string::size_type i;
407 - ++line;
408 - if ((i = s.find(' ')) == string::npos) {
409 - v.report_error("%s(%d): syntax error",
410 - file.c_str(), line);
411 - continue;
412 - }
413 - name = s.substr(0, i);
414 - key = s.substr(i + 1);
415 -
416 - if (key.size() != 683) {
417 - v.report_error("%s(%d): key has wrong length",
418 - file.c_str(), line);
419 - continue;
420 - }
421 -
422 - ustring bkey;
423 - unbase64_string it(key.begin());
424 - for (size_t i = 0; i < 64; ++i) {
425 - bkey.push_back(*it++);
426 - }
427 - config.htcp_keys[name] = bkey;
428 - }
429 -}
430 -
431 -void
432 -set_log_level(tree_entry &e, value &v)
433 -{
434 - wlog.level(log_level(v.cv_values[0].av_intval));
435 -}
436 -
437 -void
438 -set_log_file(tree_entry &e, value &v)
439 -{
440 - wlog.file(v.cv_values[0].av_strval);
441 -}
442 -
443 -bool
444 -read_config(string const &file)
445 -{
446 -conf_definer conf;
447 -tree *t;
448 -conf
449 - .block("log")
450 - .value("level", simple_range(0, 3), func(set_log_level))
451 - .value("file", nonempty_qstring, func(set_log_file))
452 - .value("syslog-facility",
453 - func(validate_log_facility), func(set_log_facility))
454 - .value("access-log", nonempty_qstring, set_qstring(config.access_log))
455 - .value("log-sample", simple_range(1, INT_MAX), set_int(config.log_sample))
456 - .value("udp-log", func(v_udp_log), set_yesno(config.udp_log))
457 - .value("udp-port", simple_range(0, 65535), set_int(config.udplog_port))
458 - .value("udp-host", nonempty_qstring, set_string(config.udplog_host))
459 -
460 - .block("cache")
461 - .value("cache-memory", simple_time, set_long(config.cache_memory))
462 - .value("max-entity-size", simple_time, set_long(config.max_entity_size))
463 - .value("master-state", nonempty_qstring, set_string(config.cache_master))
464 - .value("htcp-listen", ip_address_list, add_ip(config.htcp_hosts))
465 - .value("htcp-keys", nonempty_qstring, func(set_htcp_keys))
466 - .value("htcp-sig-required", simple_yesno, set_yesno(config.htcp_sigrequired))
467 -
468 - .block("cache-dir", require_name)
469 - .end(func(set_cache_dir))
470 -
471 - .block("http")
472 - .value("compress", simple_yesno, set_yesno(config.compress))
473 - .value("compress-level", simple_range(1, 9), set_int(config.complevel))
474 - .value("backend-retry", simple_time, set_time(config.backend_retry))
475 - .value("cache-private", simple_yesno, set_yesno(config.cache_private))
476 - .value("msie-http11-hack", simple_yesno, set_yesno(config.msie_hack))
477 - .value("default-host", nonempty_qstring, set_string(config.default_host))
478 - .value("force-backend", func(radix_prefix), func(force_backend_access))
479 - .value("backend-keepalive", simple_yesno, set_yesno(config.backend_keepalive))
480 - .value("client-keepalive", simple_yesno, set_yesno(config.client_keepalive))
481 - .value("keepalive-max", simple_range(0), set_int(config.keepalive_max))
482 - .value("x-follow-redirect", simple_yesno, set_yesno(config.x_follow))
483 - .value("max-redirects", simple_range(1), set_int(config.max_redirects))
484 -
485 - .block("server")
486 - .value("threads", simple_range(1, 1024), set_int(config.nthreads))
487 - .value("admin", nonempty_qstring, set_string(config.admin))
488 - .value("use-dio", simple_yesno, set_yesno(config.use_dio))
489 -
490 - .block("stats")
491 - .value("interval", simple_range(1, INT_MAX), set_aint(stats.interval))
492 - .value("allow", func(radix_prefix), func(stats_access))
493 - .value("enable", simple_yesno, set_yesno(config.udp_stats))
494 - .value("listen", ip_address_list, add_ip(config.stats_hosts))
495 -
496 - .block("listen", require_name)
497 - .end(func(set_listen))
498 - .value("port", simple_range(1, 65535), ignore)
499 - .value("aftype", func(v_aftype), ignore)
500 - .value("group", nonempty_qstring, ignore)
501 -
502 - .block("backend-group", require_name)
503 - .end(func(set_backend_group))
504 - .value("lb-type", func(v_lb_type), ignore)
505 - .value("hosts", func(v_hosts), ignore)
506 - .value("failover-group", nonempty_qstring, ignore)
507 -
508 - .block("backend", require_name)
509 - .end(func(set_backend))
510 - .value("port", simple_range(1, 65535), ignore)
511 - .value("aftype", func(v_aftype), ignore)
512 - .value("group", nonempty_qstring, ignore)
513 -
514 - .block("access")
515 - .end(func(set_access))
516 - .value("allow", func(radix_prefix), ignore)
517 - .value("deny", func(radix_prefix), ignore)
518 - .value("apply-at", func(v_apply_at), ignore)
519 - ;
520 -
521 - if ((t = conf::parse_file(file)) == NULL)
522 - return false;
523 - if (!conf.validate(*t))
524 - return false;
525 -
526 - /*
527 - * Defaults
528 - */
529 - stats.interval = DEFAULT_STATS_INTERVAL;
530 - config.nthreads = 1;
531 - config.admin = "nobody@example.com";
532 - poolnames["<default>"] = 0;
533 - bpools.insert(make_pair(0, backend_pool("<default>", lb_rr)));
534 - config.backend_keepalive = true;
535 - config.client_keepalive = true;
536 - config.keepalive_max = 0;
537 - config.max_redirects = 1;
538 - config.use_dio = false;
539 - config.x_follow = false;
540 - config.cache_memory = 0;
541 -
542 - conf.set(*t);
543 - whttp_reconfigure();
544 - global_conf_tree = *t;
545 - return true;
546 -}
547 -
548 -void
549 -wconfig_init(const char *file)
550 -{
551 -int nerrors = 0;
552 - if (file == NULL)
553 - file = CONFIGFILE;
554 - conf::current_file = file;
555 -
556 - wlog.notice(format("loading configuration from %s")
557 - % conf::current_file);
558 -
559 - if (!read_config(file)) {
560 - wlog.error("cannot load configuration");
561 - nerrors++;
562 - }
563 -
564 - if (!listeners.size()) {
565 - wlog.error("no listeners defined");
566 - nerrors++;
567 - }
568 - if (!bpools.size()) {
569 - wlog.error("no backends defined");
570 - nerrors++;
571 - }
572 -
573 - for (map<int, backend_pool>::iterator it = bpools.begin(), end = bpools.end();
574 - it != end; ++it) {
575 - if (!it->second.size() && used_pools.find(it->first) != used_pools.end()) {
576 - wlog.error(format(
577 - "backend group \"%s\" is used but has no backends")
578 - % it->second.name());
579 - nerrors++;
580 - }
581 - }
582 -
583 - if (nerrors) {
584 - wlog.error(format(
585 - "%d error(s) in configuration file. cannot continue.")
586 - % nerrors);
587 - exit(8);
588 - }
589 -}
Index: trunk/willow/src/willow/net.cc
@@ -0,0 +1,657 @@
 2+/* @(#) $Id: wnet.cc 17829 2006-11-21 16:41:15Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * net: Networking.
 7+ */
 8+
 9+#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Id: wnet.cc 17829 2006-11-21 16:41:15Z river $"
 11+#endif
 12+
 13+#include <sys/types.h>
 14+#include <sys/socket.h>
 15+#include <sys/ioctl.h>
 16+
 17+#include "autoconf.h"
 18+#ifdef HAVE_SYS_SENDFILE_H
 19+# include <sys/sendfile.h>
 20+#endif
 21+
 22+#include <netinet/tcp.h>
 23+#include <net/if.h>
 24+#include <arpa/inet.h>
 25+
 26+#include <cstdio>
 27+#include <cstring>
 28+#include <cstdlib>
 29+#include <cerrno>
 30+#include <csignal>
 31+#include <cassert>
 32+#include <ctime>
 33+#include <deque>
 34+using std::deque;
 35+using std::signal;
 36+
 37+#include <unistd.h>
 38+#include <fcntl.h>
 39+
 40+#include "willow.h"
 41+#include "net.h"
 42+#include "config.h"
 43+#include "log.h"
 44+#include "http.h"
 45+#include "format.h"
 46+
 47+#define RDBUF_INC 8192 /* buffer in 8 KiB incrs */
 48+
 49+struct event ev_sigint;
 50+struct event ev_sigterm;
 51+tss<event_base> evb;
 52+
 53+static void sig_exit(int, short, void *);
 54+
 55+ioloop_t *ioloop;
 56+
 57+char current_time_str[30];
 58+char current_time_short[30];
 59+time_t current_time;
 60+
 61+static void secondly_sched(void);
 62+
 63+bool wnet_exit;
 64+vector<wsocket *> awaks;
 65+size_t cawak;
 66+
 67+void
 68+wnet_add_accept_wakeup(wsocket *s)
 69+{
 70+ awaks.push_back(s);
 71+}
 72+
 73+event secondly_ev;
 74+timeval secondly_tv;
 75+
 76+static void
 77+secondly_update(int, short, void *)
 78+{
 79+ wnet_set_time();
 80+ secondly_sched();
 81+}
 82+
 83+static void
 84+secondly_sched(void)
 85+{
 86+ secondly_tv.tv_usec = 0;
 87+ secondly_tv.tv_sec = 1;
 88+ evtimer_set(&secondly_ev, secondly_update, NULL);
 89+ event_base_set(evb, &secondly_ev);
 90+ event_add(&secondly_ev, &secondly_tv);
 91+}
 92+
 93+ioloop_t::ioloop_t(void)
 94+{
 95+ prepare();
 96+}
 97+
 98+void
 99+ioloop_t::prepare(void)
 100+{
 101+size_t i;
 102+
 103+ wlog.notice(format("maximum number of open files: %d")
 104+ % getdtablesize());
 105+
 106+ signal(SIGPIPE, SIG_IGN);
 107+
 108+ for (i = 0; i < listeners.size(); ++i) {
 109+ listener *lns = listeners[i];
 110+
 111+ try {
 112+ lns->sock->reuseaddr(true);
 113+ lns->sock->bind();
 114+ lns->sock->listen();
 115+ } catch (socket_error &e) {
 116+ wlog.error(format("creating listener %s: %s")
 117+ % lns->sock->straddr() % e.what());
 118+ exit(8);
 119+ }
 120+
 121+ lns->sock->readback(polycaller<wsocket *, int>(*this, &ioloop_t::_accept), 0);
 122+ }
 123+ wlog.notice(format("wnet: initialised, using libevent %s (%s)")
 124+ % event_get_version() % event_get_method());
 125+ secondly_sched();
 126+}
 127+
 128+void
 129+ioloop_t::_accept(wsocket *s, int)
 130+{
 131+ wsocket *newe;
 132+static atomic<time_t> last_nfile = 0;
 133+ time_t now = time(NULL);
 134+
 135+ if ((newe = s->accept("HTTP client", prio_norm)) == NULL) {
 136+ if ((errno != ENFILE && errno != EMFILE) || (now - last_nfile) > 60) {
 137+ if (errno == ENFILE || errno == EMFILE)
 138+ last_nfile = now;
 139+ wlog.warn(format("accept error: %s") % strerror(errno));
 140+ }
 141+ s->readback(polycaller<wsocket *, int>(*this, &ioloop_t::_accept), 0);
 142+ return;
 143+ }
 144+
 145+ s->readback(polycaller<wsocket *, int>(*this, &ioloop_t::_accept), 0);
 146+
 147+ newe->nonblocking(true);
 148+
 149+ if (cawak == awaks.size())
 150+ cawak = 0;
 151+char buf[sizeof(wsocket *) * 2];
 152+ memcpy(buf, &newe, sizeof(newe));
 153+ memcpy(buf + sizeof(newe), &s, sizeof(s));
 154+ WDEBUG(format("_accept, lsnr=%d") % s);
 155+
 156+ if (awaks[cawak]->write(buf, sizeof(wsocket *) * 2) < 0) {
 157+ wlog.error(format("writing to thread wakeup socket: %s")
 158+ % strerror(errno));
 159+ exit(1);
 160+ }
 161+ cawak++;
 162+ return;
 163+}
 164+
 165+void
 166+wnet_set_time(void)
 167+{
 168+struct tm *now;
 169+ time_t old = current_time;
 170+ size_t n;
 171+
 172+ current_time = time(NULL);
 173+ if (current_time == old)
 174+ return;
 175+
 176+ now = gmtime(&current_time);
 177+
 178+ n = strftime(current_time_str, sizeof(current_time_str), "%a, %d %b %Y %H:%M:%S GMT", now);
 179+ assert(n);
 180+ n = strftime(current_time_short, sizeof(current_time_short), "%Y-%m-%d %H:%M:%S", now);
 181+ assert(n);
 182+}
 183+
 184+namespace wnet {
 185+
 186+void
 187+socket::_ev_callback(int fd, short ev, void *d)
 188+{
 189+wsocket *s = (wsocket *)d;
 190+
 191+ WDEBUG(format("_ev_callback: %s%son %d (%s)")
 192+ % ((ev & EV_READ) ? "read " : "")
 193+ % ((ev & EV_WRITE) ? "write " : "")
 194+ % fd % s->_desc);
 195+
 196+ if (ev & EV_READ)
 197+ s->_read_handler(s);
 198+ if (ev & EV_WRITE)
 199+ s->_write_handler(s);
 200+}
 201+
 202+void
 203+socket::_register(int what, polycallback<wsocket *> handler)
 204+{
 205+ int ev_flags = 0;
 206+
 207+ WDEBUG(format("_register: %s%son %d (%s)")
 208+ % ((what & FDE_READ) ? "read " : "")
 209+ % ((what & FDE_WRITE) ? "write " : "")
 210+ % _s % _desc);
 211+
 212+ if (event_pending(&ev, EV_READ | EV_WRITE, NULL))
 213+ event_del(&ev);
 214+
 215+ if (what & FDE_READ) {
 216+ _read_handler = handler;
 217+ ev_flags |= EV_READ;
 218+ }
 219+ if (what & FDE_WRITE) {
 220+ _write_handler = handler;
 221+ ev_flags |= EV_WRITE;
 222+ }
 223+
 224+ event_set(&ev, _s, ev_flags, _ev_callback, this);
 225+ event_base_set(evb, &ev);
 226+ event_priority_set(&ev, (int) _prio);
 227+ event_add(&ev, NULL);
 228+}
 229+
 230+address::address(void)
 231+{
 232+ memset(&_addr, 0, sizeof(_addr));
 233+ _addrlen = 0;
 234+ _fam = AF_UNSPEC;
 235+ _stype = _prot = 0;
 236+}
 237+
 238+address::address(sockaddr *sa, socklen_t len)
 239+{
 240+ memcpy(&_addr, sa, len);
 241+ _addrlen = len;
 242+ _stype = _prot = 0;
 243+ _fam = ((sockaddr_storage *)sa)->ss_family;
 244+}
 245+
 246+address::address(addrinfo *ai)
 247+{
 248+ memcpy(&_addr, ai->ai_addr, ai->ai_addrlen);
 249+ _addrlen = ai->ai_addrlen;
 250+ _fam = ai->ai_family;
 251+ _stype = ai->ai_socktype;
 252+ _prot = ai->ai_protocol;
 253+}
 254+
 255+socket *
 256+address::makesocket(char const *desc, sprio p) const
 257+{
 258+ return new socket(*this, desc, p);
 259+}
 260+
 261+address::address(address const &o)
 262+ : _addrlen(o._addrlen)
 263+ , _fam(o._fam)
 264+ , _stype(o._stype)
 265+ , _prot(o._prot) {
 266+ memcpy(&_addr, &o._addr, _addrlen);
 267+}
 268+
 269+address &
 270+address::operator= (address const &o)
 271+{
 272+ _addrlen = o._addrlen;
 273+ _fam = o._fam;
 274+ _stype = o._stype;
 275+ _prot = o._prot;
 276+ memcpy(&_addr, &o._addr, _addrlen);
 277+ return *this;
 278+}
 279+
 280+string const &
 281+address::straddr(bool lng) const
 282+{
 283+char res[NI_MAXHOST];
 284+int i;
 285+ if (!lng) {
 286+ if (_shortaddr.empty()) {
 287+ if ((i = getnameinfo((sockaddr *) &_addr, _addrlen,
 288+ res, sizeof(res), NULL, 0, NI_NUMERICHOST)) != 0)
 289+ throw resolution_error(i);
 290+ _shortaddr = res;
 291+ }
 292+ return _shortaddr;
 293+ }
 294+
 295+ if (_straddr.empty()) {
 296+ char port[NI_MAXSERV];
 297+ if ((i = getnameinfo((sockaddr *) &_addr, _addrlen,
 298+ res, sizeof(res), port, sizeof(port),
 299+ NI_NUMERICHOST | NI_NUMERICSERV)) != 0)
 300+ throw resolution_error(i);
 301+ _straddr = str(format("[%s]:%s") % res % port);
 302+ }
 303+ return _straddr;
 304+}
 305+
 306+addrlist *
 307+addrlist::resolve(string const &addr, string const &port,
 308+ enum socktype socktype, int family)
 309+{
 310+addrinfo hints, *res, *ai;
 311+int r;
 312+ memset(&hints, 0, sizeof(hints));
 313+ hints.ai_socktype = (int) socktype;
 314+ if (family != AF_UNSPEC)
 315+ hints.ai_family = family;
 316+
 317+ if ((r = getaddrinfo(addr.c_str(),
 318+ port.c_str(), &hints, &res)) != 0)
 319+ throw resolution_error(r);
 320+
 321+addrlist *al = new addrlist;
 322+ for (ai = res; ai; ai = ai->ai_next)
 323+ al->_addrs.push_back(address(ai));
 324+
 325+ freeaddrinfo(res);
 326+ return al;
 327+}
 328+
 329+address
 330+addrlist::first(string const &addr, int port,
 331+ enum socktype socktype, int family)
 332+{
 333+ return first(addr, lexical_cast<string>(port), socktype, family);
 334+}
 335+
 336+addrlist *
 337+addrlist::resolve(string const &addr, int port,
 338+ enum socktype socktype, int family)
 339+{
 340+ return resolve(addr, lexical_cast<string>(port), socktype, family);
 341+}
 342+
 343+address
 344+addrlist::first(string const &addr, string const &port,
 345+ enum socktype socktype, int family)
 346+{
 347+addrlist *r = addrlist::resolve(addr, port, socktype, family);
 348+address res;
 349+ res = *r->begin();
 350+ delete r;
 351+ return res;
 352+}
 353+
 354+addrlist::~addrlist(void)
 355+{
 356+}
 357+
 358+addrlist::iterator
 359+addrlist::begin(void) const
 360+{
 361+ return _addrs.begin();
 362+}
 363+
 364+addrlist::iterator
 365+addrlist::end(void) const
 366+{
 367+ return _addrs.end();
 368+}
 369+
 370+socket *
 371+addrlist::makesocket(char const *desc, sprio p) const
 372+{
 373+iterator it = _addrs.begin(), end = _addrs.end();
 374+ for (; it != end; ++it) {
 375+ socket *ns;
 376+ if ((ns = it->makesocket(desc, p)) != NULL)
 377+ return ns;
 378+ }
 379+ throw socket_error();
 380+}
 381+
 382+socket *
 383+socket::create(string const &addr, int port,
 384+ enum socktype socktype, char const *desc, sprio p, int family)
 385+{
 386+ return create(addr, lexical_cast<string>(port), socktype, desc, p, family);
 387+}
 388+
 389+socket *
 390+socket::create(string const &addr, string const &port,
 391+ enum socktype socktype, char const *desc, sprio p, int family)
 392+{
 393+addrlist *al = addrlist::resolve(addr, port, socktype, family);
 394+ return al->makesocket(desc, p);
 395+}
 396+
 397+pair<socket *, socket *>
 398+socket::socketpair(enum socktype st)
 399+{
 400+socket *s1 = NULL, *s2 = NULL;
 401+int sv[2];
 402+ if (::socketpair(AF_UNIX, (int) st, 0, sv) == -1)
 403+ throw socket_error();
 404+ s1 = new socket(sv[0], wnet::address(), "socketpair", prio_norm);
 405+ try {
 406+ s2 = new socket(sv[1], wnet::address(), "socketpair", prio_norm);
 407+ } catch (...) {
 408+ delete s1;
 409+ throw;
 410+ }
 411+ return make_pair(s1, s2);
 412+}
 413+
 414+connect_status
 415+socket::connect(void)
 416+{
 417+ if (::connect(_s, _addr.addr(), _addr.length()) == -1)
 418+ if (errno == EINPROGRESS)
 419+ return connect_later;
 420+ else
 421+ throw socket_error();
 422+ return connect_okay;
 423+}
 424+
 425+socket *
 426+socket::accept(char const *desc, sprio p)
 427+{
 428+int ns;
 429+sockaddr_storage addr;
 430+socklen_t addrlen = sizeof(addr);
 431+ if ((ns = ::accept(_s, (sockaddr *)&addr, &addrlen)) == -1)
 432+ return NULL;
 433+ return new socket(ns, wnet::address((sockaddr *)&addr, addrlen), desc, p);
 434+}
 435+
 436+int
 437+socket::recvfrom(char *buf, size_t count, wnet::address &addr)
 438+{
 439+sockaddr_storage saddr;
 440+socklen_t addrlen = sizeof(addr);
 441+int i;
 442+ if ((i = ::recvfrom(_s, buf, count, 0, (sockaddr *)&saddr, &addrlen)) < 0)
 443+ return i;
 444+ WDEBUG(format("recvfrom: fam=%d") % saddr.ss_family);
 445+ addr = wnet::address((sockaddr *)&saddr, addrlen);
 446+ return i;
 447+}
 448+
 449+int
 450+socket::sendto(char const *buf, size_t count, wnet::address const &addr)
 451+{
 452+ return ::sendto(_s, buf, count, 0, addr.addr(), addr.length());
 453+}
 454+
 455+void
 456+socket::nonblocking(bool v)
 457+{
 458+int val;
 459+ val = fcntl(_s, F_GETFL, 0);
 460+ if (val == -1)
 461+ throw socket_error();
 462+ if (v)
 463+ val |= O_NONBLOCK;
 464+ else val &= ~O_NONBLOCK;
 465+
 466+ if (fcntl(_s, F_SETFL, val) == -1)
 467+ throw socket_error();
 468+}
 469+
 470+void
 471+socket::reuseaddr(bool v)
 472+{
 473+int i = v;
 474+int len = sizeof(i);
 475+ setopt(SOL_SOCKET, SO_REUSEADDR, &i, len);
 476+}
 477+
 478+void
 479+socket::cork(void)
 480+{
 481+int one = 1;
 482+ setopt(IPPROTO_TCP, TCP_CORK, &one, sizeof(one));
 483+}
 484+
 485+void
 486+socket::uncork(void)
 487+{
 488+int zero = 0;
 489+ setopt(IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero));
 490+}
 491+
 492+int
 493+socket::getopt(int level, int what, void *addr, socklen_t *len) const
 494+{
 495+int i;
 496+ if ((i = getsockopt(_s, level, what, addr, len)) == -1)
 497+ throw socket_error();
 498+ return i;
 499+}
 500+
 501+int
 502+socket::setopt(int level, int what, void *addr, socklen_t len)
 503+{
 504+int i;
 505+ if ((i = setsockopt(_s, level, what, addr, len)) == -1)
 506+ throw socket_error();
 507+ return i;
 508+}
 509+
 510+int
 511+socket::error(void) const
 512+{
 513+int error = 0;
 514+socklen_t len = sizeof(error);
 515+ try {
 516+ getopt(SOL_SOCKET, SO_ERROR, &error, &len);
 517+ return error;
 518+ } catch (socket_error &) {
 519+ return 0;
 520+ }
 521+}
 522+
 523+socket::socket(int s, wnet::address const &a, char const *desc, sprio p)
 524+ : _addr(a)
 525+ , _desc(desc)
 526+ , _prio(p)
 527+{
 528+ memset(&ev, 0, sizeof(ev));
 529+ _s = s;
 530+}
 531+
 532+socket::socket(wnet::address const &a, char const *desc, sprio p)
 533+ : _addr(a)
 534+ , _desc(desc)
 535+ , _prio(p)
 536+{
 537+ memset(&ev, 0, sizeof(ev));
 538+ _s = ::socket(_addr.family(), _addr.socktype(), _addr.protocol());
 539+ if (_s == -1)
 540+ throw socket_error();
 541+}
 542+
 543+void
 544+socket::bind(void)
 545+{
 546+ if (::bind(_s, _addr.addr(), _addr.length()) == -1)
 547+ throw socket_error();
 548+}
 549+
 550+void
 551+socket::listen(int bl)
 552+{
 553+ if (::listen(_s, bl) == -1)
 554+ throw socket_error();
 555+}
 556+
 557+socket::~socket(void)
 558+{
 559+ event_del(&ev);
 560+ close(_s);
 561+}
 562+
 563+void
 564+socket::clearbacks(void)
 565+{
 566+ event_del(&ev);
 567+}
 568+
 569+void
 570+socket::mcast_join(string const &ifname)
 571+{
 572+ switch (_addr.family()) {
 573+ case AF_INET: {
 574+ struct address ifaddr = address::from_ifname(_s, ifname);
 575+ sockaddr_in *inbind = (sockaddr_in *)_addr.addr();
 576+ sockaddr_in *inif = (sockaddr_in *)ifaddr.addr();
 577+ ip_mreq mr;
 578+ memset(&mr, 0, sizeof(mr));
 579+ mr.imr_multiaddr.s_addr = inbind->sin_addr.s_addr;
 580+ mr.imr_interface.s_addr = inif->sin_addr.s_addr;
 581+ WDEBUG(format("NET: %s joins mcast on if %s")
 582+ % straddr() % ifaddr.straddr());
 583+ setopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr));
 584+ break;
 585+ }
 586+
 587+ case AF_INET6: {
 588+ u_int ifindex = address::ifname_to_index(ifname);
 589+ sockaddr_in6 *inbind = (sockaddr_in6 *)_addr.addr();
 590+ ipv6_mreq mr;
 591+ memset(&mr, 0, sizeof(mr));
 592+ memcpy(&mr.ipv6mr_multiaddr, &inbind->sin6_addr,
 593+ sizeof(mr.ipv6mr_multiaddr));
 594+ mr.ipv6mr_interface = ifindex;
 595+ setopt(IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mr, sizeof(mr));
 596+ break;
 597+ }
 598+
 599+ default:
 600+ throw socket_error("multicast join not applicable for this socket type");
 601+ }
 602+}
 603+
 604+u_int
 605+address::ifname_to_index(string const &ifname)
 606+{
 607+u_int ret = if_nametoindex(ifname.c_str());
 608+ if (ret == 0)
 609+ throw socket_error("named interface does not exist");
 610+ return ret;
 611+}
 612+
 613+address
 614+address::from_ifname(int s, string const &ifname)
 615+{
 616+ifreq ifr;
 617+ memset(&ifr, 0, sizeof(ifr));
 618+ strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
 619+ if (ioctl(s, SIOCGIFADDR, &ifr) < 0)
 620+ throw socket_error();
 621+address ret(&ifr.ifr_addr, sizeof(sockaddr_in));
 622+ return ret;
 623+}
 624+
 625+} // namespace wnet
 626+
 627+void
 628+make_event_base(void)
 629+{
 630+static lockable meb_lock;
 631+ if (evb == NULL) {
 632+ HOLDING(meb_lock);
 633+ evb = (event_base *)event_init();
 634+ event_base_priority_init(evb, prio_max);
 635+ signal_set(&ev_sigint, SIGINT, sig_exit, NULL);
 636+ signal_add(&ev_sigint, NULL);
 637+ signal_set(&ev_sigterm, SIGTERM, sig_exit, NULL);
 638+ signal_add(&ev_sigterm, NULL);
 639+ }
 640+}
 641+
 642+void
 643+sig_exit(int sig, short what, void *d)
 644+{
 645+ wnet_exit = true;
 646+}
 647+
 648+void
 649+ioloop_t::run(void)
 650+{
 651+ while (!wnet_exit) {
 652+ event_base_loop(evb, EVLOOP_ONCE);
 653+ }
 654+
 655+size_t i;
 656+ for (i = 0; i < listeners.size(); ++i)
 657+ delete listeners[i];
 658+}
Index: trunk/willow/src/willow/http_header.cc
@@ -0,0 +1,745 @@
 2+/* @(#) $Id: whttp_header.cc 17774 2006-11-18 03:48:27Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * http_header: header processing implementation.
 7+ */
 8+
 9+#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Id: whttp_header.cc 17774 2006-11-18 03:48:27Z river $"
 11+#endif
 12+
 13+#if 0
 14+# define WILLOW_DEBUG
 15+#endif
 16+
 17+#include <vector>
 18+#include <cstring>
 19+#include <cerrno>
 20+using std::strlen;
 21+using std::vector;
 22+using std::sprintf;
 23+
 24+#include <assert.h>
 25+
 26+#include "autoconf.h"
 27+#include "http_header.h"
 28+#include "net.h"
 29+#include "flowio.h"
 30+#include "config.h"
 31+#include "format.h"
 32+
 33+using namespace wnet;
 34+
 35+enum {
 36+ H_IGNORE,
 37+ H_TRANSFER_ENCODING,
 38+ H_CONTENT_LENGTH,
 39+ H_USER_AGENT,
 40+ H_HOST,
 41+ H_CONNECTION,
 42+ H_LOCATION,
 43+ H_X_WILLOW_BACKEND_GROUP,
 44+ H_X_WILLOW_FOLLOW_REDIRECT
 45+};
 46+
 47+#if 0
 48+#ifdef __GNUC__
 49+# include <ext/hash_map>
 50+typedef __gnu_cxx::hash_map<imstring,int> hmap_type;
 51+namespace __gnu_cxx {
 52+
 53+#define FNV_32_PRIME 0x01000193u
 54+
 55+template<>
 56+struct hash<imstring> {
 57+ size_t operator() (imstring const &str) const {
 58+ uint32_t hval = 0x811c9dc5u;
 59+ unsigned char *q = (unsigned char *)str.c_str() + str.size() - 1,
 60+ *s = q; /* unsigned string */
 61+
 62+ while (*s && (q - s < 4)) {
 63+#ifndef __GNUC__
 64+ hval *= FNV_32_PRIME;
 65+#else
 66+ hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24);
 67+#endif
 68+ hval ^= (uint32_t)std::tolower(*s--);
 69+ }
 70+ return hval;
 71+ }
 72+};
 73+
 74+}
 75+#else
 76+typedef map<imstring, int> hmap_type;
 77+#endif
 78+
 79+hmap_type htypemap;
 80+#endif
 81+vector<pair<char const *, int> > htypemap;
 82+
 83+static struct htypent {
 84+ char const *name;
 85+ int n;
 86+ size_t len;
 87+} list[] = {
 88+ { "transfer-encoding", H_TRANSFER_ENCODING, 0 },
 89+ { "content-length", H_CONTENT_LENGTH, 0 },
 90+ { "user-agent", H_USER_AGENT, 0 },
 91+ { "host", H_HOST, 0 },
 92+ { "connection", H_CONNECTION, 0 },
 93+ { "location", H_LOCATION, 0 },
 94+ { "x-willow-backend-group", H_X_WILLOW_BACKEND_GROUP, 0 },
 95+ { "x-willow-follow-redirect", H_X_WILLOW_FOLLOW_REDIRECT, 0 },
 96+ { "keep-alive", H_IGNORE, 0 },
 97+ { "te", H_IGNORE, 0 },
 98+ { "trailers", H_IGNORE, 0 },
 99+ { "upgrade", H_IGNORE, 0 },
 100+ { "proxy-authenticate", H_IGNORE, 0 },
 101+ { "proxy-connection", H_IGNORE, 0 },
 102+ { 0, 0, 0 }
 103+};
 104+void
 105+whttp_header_init(void)
 106+{
 107+ for (htypent *tit = list; tit->name; ++tit)
 108+ tit->len = strlen(tit->name);
 109+}
 110+
 111+static inline int
 112+find_htype(char const *s, size_t slen)
 113+{
 114+ for (htypent *tit = list; tit->name; ++tit)
 115+ if (tit->len == slen && !strncasecmp(s, tit->name, slen))
 116+ return tit->n;
 117+ return -1;
 118+}
 119+
 120+#if 0
 121+int
 122+find_htype(char const *s, size_t slen)
 123+{
 124+hmap_type::iterator it;
 125+ if (slen >= 24)
 126+ return -1;
 127+ it = htypemap.find(imstring(s, slen));
 128+ if (it == htypemap.end())
 129+ return -1;
 130+ return it->second;
 131+}
 132+#endif
 133+
 134+const char *request_string[] = {
 135+ "GET ",
 136+ "POST ",
 137+ "HEAD ",
 138+ "TRACE ",
 139+ "OPTIONS ",
 140+ "TRACE ",
 141+};
 142+
 143+struct request_type supported_reqtypes[] = {
 144+ { "GET", 3, REQTYPE_GET },
 145+ { "POST", 4, REQTYPE_POST },
 146+ { "HEAD", 4, REQTYPE_HEAD },
 147+ { "TRACE", 5, REQTYPE_TRACE },
 148+ { "OPTIONS", 7, REQTYPE_OPTIONS },
 149+ { "PURGE", 5, REQTYPE_PURGE },
 150+ { NULL, 0, REQTYPE_INVALID }
 151+};
 152+
 153+static int
 154+find_reqtype(char const *str, int len)
 155+{
 156+ for (request_type *r = supported_reqtypes; r->name; r++)
 157+ if (r->len == len && !memcmp(r->name, str, len))
 158+ return r->type;
 159+ return REQTYPE_INVALID;
 160+}
 161+
 162+pt_allocator<char> header::alloc;
 163+
 164+header::header(char const *n, size_t nlen, char const *v, size_t vlen)
 165+ : hr_allocd(0)
 166+{
 167+ assign(n, nlen, v, vlen);
 168+}
 169+
 170+header::header(header const &other)
 171+ : hr_allocd(0)
 172+{
 173+ assign(other.hr_name, strlen(other.hr_name),
 174+ other.hr_value, strlen(other.hr_value));
 175+}
 176+
 177+void
 178+header::assign(char const *n, size_t nlen, char const *v, size_t vlen)
 179+{
 180+char *buf = hr_buffer;
 181+ if (hr_allocd) {
 182+ alloc.deallocate(hr_name, hr_allocd);
 183+ hr_allocd = false;
 184+ }
 185+
 186+ if ((nlen + vlen + 2) >= HDR_BUFSZ) {
 187+ buf = alloc.allocate(nlen + vlen + 2);
 188+ hr_allocd = nlen + vlen + 2;
 189+ }
 190+
 191+ hr_name = buf;
 192+ hr_value = buf + nlen + 1;
 193+
 194+ memcpy(hr_name, n, nlen);
 195+ memcpy(hr_value, v, vlen);
 196+ hr_name[nlen] = hr_value[vlen] = '\0';
 197+}
 198+
 199+header&
 200+header::operator= (header const &other)
 201+{
 202+ assign(other.hr_name, strlen(other.hr_name),
 203+ other.hr_value, strlen(other.hr_value));
 204+ return *this;
 205+}
 206+
 207+header::~header(void)
 208+{
 209+ if (hr_allocd)
 210+ alloc.deallocate(hr_name, hr_allocd);
 211+}
 212+
 213+void
 214+header::move(header &other)
 215+{
 216+ /*
 217+ * The other header is static, just copy the string.
 218+ */
 219+ if (!other.hr_allocd) {
 220+ if (hr_allocd) {
 221+ alloc.deallocate(hr_name, hr_allocd);
 222+ hr_allocd = 0;
 223+ }
 224+
 225+ hr_name = hr_buffer;
 226+ strcpy(hr_name, other.hr_name);
 227+ hr_value = hr_buffer + strlen(hr_name) + 1;
 228+ strcpy(hr_value, other.hr_value);
 229+ return;
 230+ }
 231+
 232+ /*
 233+ * The other header is allocd, steal its buffer.
 234+ */
 235+ hr_allocd = other.hr_allocd;
 236+ hr_name = other.hr_name;
 237+ hr_value = other.hr_value;
 238+ other.hr_allocd = 0;
 239+}
 240+
 241+header_list::header_list()
 242+ : hl_len(0)
 243+{
 244+ hl_hdrs.reserve(20);
 245+}
 246+
 247+header_list::~header_list()
 248+{
 249+ for (vector<header *, pt_allocator<header *> >::iterator
 250+ it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it)
 251+ delete *it;
 252+}
 253+
 254+void
 255+header_list::add(char const *name, size_t namelen, char const *value, size_t vallen)
 256+{
 257+header *h = new header(name, namelen, value, vallen);
 258+
 259+ hl_hdrs.push_back(h);
 260+ hl_last = h;
 261+ hl_len += namelen + vallen + 4;
 262+}
 263+
 264+void
 265+header_list::add(char const *name, char const *value)
 266+{
 267+ add(name, strlen(name), value, strlen(value));
 268+}
 269+
 270+void
 271+header_list::append_last(const char *append, size_t len)
 272+{
 273+int curnlen, curvlen;
 274+ curnlen = strlen(hl_last->hr_name);
 275+ curvlen = strlen(hl_last->hr_value);
 276+
 277+ hl_len += len + 2;
 278+
 279+size_t nbufsz = curnlen + curvlen + 4 + len;
 280+ /*
 281+ * Simple case: not allocated, and new header fits in static buf.
 282+ */
 283+ if (!hl_last->hr_allocd && nbufsz < HDR_BUFSZ) {
 284+ strcat(hl_last->hr_value, ", ");
 285+ strncat(hl_last->hr_value, append, len);
 286+ return;
 287+ }
 288+
 289+ /*
 290+ * New header is too long.
 291+ */
 292+ if ((!hl_last->hr_allocd && nbufsz >= HDR_BUFSZ) ||
 293+ (hl_last->hr_allocd && nbufsz >= hl_last->hr_allocd)) {
 294+ char *nbuf = hl_last->alloc.allocate(nbufsz);
 295+ strcpy(nbuf, hl_last->hr_name);
 296+ sprintf(nbuf + curnlen + 1, "%s, %.*s",
 297+ hl_last->hr_value, (int) len, append);
 298+
 299+ if (hl_last->hr_allocd)
 300+ hl_last->alloc.deallocate(hl_last->hr_name, hl_last->hr_allocd);
 301+
 302+ hl_last->hr_name = nbuf;
 303+ hl_last->hr_value = nbuf + curnlen + 1;
 304+ hl_last->hr_allocd = nbufsz;
 305+ return;
 306+ }
 307+
 308+ /* should not get here */
 309+ abort();
 310+}
 311+
 312+void
 313+header_list::remove(const char *name)
 314+{
 315+vector<header *, pt_allocator<header *> >::iterator it, end;
 316+ for (it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it) {
 317+ if (strcasecmp((*it)->hr_name, name))
 318+ continue;
 319+ hl_len -= strlen((*it)->hr_name) + strlen((*it)->hr_value) + 4;
 320+ (*it)->move(**hl_hdrs.rbegin());
 321+ hl_hdrs.pop_back();
 322+ return;
 323+ }
 324+
 325+}
 326+
 327+struct header *
 328+header_list::find(const char *name)
 329+{
 330+vector<header *, pt_allocator<header *> >::iterator it, end;
 331+ for (it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it) {
 332+ if (strcasecmp((*it)->hr_name, name))
 333+ continue;
 334+ return *it;
 335+ }
 336+ return NULL;
 337+}
 338+
 339+header_list&
 340+header_list::operator= (header_list const &other)
 341+{
 342+ for (vector<header *, pt_allocator<header *> >::iterator
 343+ it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it)
 344+ delete *it;
 345+ hl_hdrs.clear();
 346+ for (vector<header *, pt_allocator<header *> >::const_iterator
 347+ it = other.hl_hdrs.begin(), end = other.hl_hdrs.end(); it != end; ++it)
 348+ hl_hdrs.push_back(new header(**it));
 349+ hl_len = other.hl_len;
 350+ hl_last = *hl_hdrs.rbegin();
 351+ return *this;
 352+}
 353+
 354+char *
 355+header_list::build(void)
 356+{
 357+char *buf;
 358+size_t bufsz;
 359+size_t buflen = 0;
 360+
 361+ bufsz = hl_len + 3;
 362+ buf = new char[bufsz];
 363+
 364+ *buf = '\0';
 365+vector<header *, pt_allocator<header *> >::iterator it, end;
 366+ for (it = hl_hdrs.begin(), end = hl_hdrs.end(); it != end; ++it) {
 367+ int incr;
 368+ incr = strlen((*it)->hr_name);
 369+ memcpy(buf + buflen, (*it)->hr_name, incr);
 370+ buflen += incr;
 371+ memcpy(buf + buflen, ": ", 2);
 372+ buflen += 2;
 373+ incr = strlen((*it)->hr_value);
 374+ memcpy(buf + buflen, (*it)->hr_value, incr);
 375+ buflen += incr;
 376+ memcpy(buf + buflen, "\r\n", 2);
 377+ buflen += 2;
 378+ }
 379+
 380+ memcpy(buf + buflen, "\r\n", 2);
 381+
 382+ return buf;
 383+}
 384+
 385+io::sink_result
 386+header_parser::data_ready(char const *buf, size_t len, ssize_t &discard)
 387+{
 388+static char const *msie = "MSIE";
 389+char const *rn, *value, *name, *bufp = buf;
 390+size_t vlen, nlen, rnpos;
 391+int htype;
 392+
 393+ WDEBUG(format("header parser: got [%s]") % string(buf, buf + len));
 394+ while ((rn = find_rn(bufp, bufp + len)) != NULL) {
 395+ WDEBUG(format("processing: [%s]") % string(bufp, rn));
 396+ for (char const *c = bufp; c < rn; ++c)
 397+ if (*(unsigned char *)c > 0x7f || !*c)
 398+ return io::sink_result_error;
 399+ WDEBUG("chars all okay");
 400+
 401+ if (rn == bufp) {
 402+ _sink_spigot->sp_cork();
 403+ discard += bufp - buf + 2;
 404+ /* request with no request is an error */
 405+ if (!_got_reqtype)
 406+ return io::sink_result_error;
 407+ else {
 408+ if (!_is_response && _http_host.empty()) {
 409+ if (_http_vers == http11)
 410+ return io::sink_result_error;
 411+ else if (!config.default_host.empty()) {
 412+ _headers.add("Host", config.default_host.c_str());
 413+ _http_host = config.default_host;
 414+ }
 415+ }
 416+ return io::sink_result_finished;
 417+ }
 418+ }
 419+ rnpos = rn - bufp;
 420+ name = bufp;
 421+
 422+ if (!_got_reqtype) {
 423+ if ((!_is_response && parse_reqtype(bufp, rn) == -1)
 424+ || (_is_response && parse_response(bufp, rn) == -1)) {
 425+ _sink_spigot->sp_cork();
 426+ return io::sink_result_error;
 427+ }
 428+ _got_reqtype = true;
 429+ goto next;
 430+ }
 431+
 432+ if (*name == ' ') {
 433+ const char *s = name;
 434+ /* continuation of last header */
 435+ if (!_headers.hl_len)
 436+ return io::sink_result_error;
 437+ while (*s == ' ' && s < rn)
 438+ s++;
 439+ if (s < rn)
 440+ _headers.append_last(s, rnpos - (s - name));
 441+ goto next;
 442+ }
 443+
 444+ if ((value = (const char *)memchr(name, ':', rnpos)) == NULL) {
 445+ _sink_spigot->sp_cork();
 446+ return io::sink_result_error;
 447+ }
 448+ nlen = value - name;
 449+
 450+ htype = find_htype(name, nlen);
 451+ if (htype == H_IGNORE)
 452+ goto next;
 453+
 454+ value++;
 455+ while (isspace(*value) && value < rn)
 456+ value++;
 457+ vlen = rn - value;
 458+
 459+ switch (htype) {
 460+ case H_TRANSFER_ENCODING:
 461+ if (!strncasecmp(value, "chunked", vlen))
 462+ _flags.f_chunked = 1;
 463+ break;
 464+ case H_CONTENT_LENGTH:
 465+ _content_length = str10toint(value, vlen);
 466+ break;
 467+ case H_USER_AGENT:
 468+ if (config.msie_hack &&
 469+ std::search(value, value + vlen, msie, msie + 4) != value + vlen)
 470+ _is_msie = true;
 471+ break;
 472+ case H_HOST:
 473+ _http_host.assign(value, value + vlen);
 474+ break;
 475+ case H_CONNECTION:
 476+ if (!strncasecmp(value, "close", vlen))
 477+ _no_keepalive = true;
 478+ else if (!strncasecmp(value, "keep-alive", vlen))
 479+ _force_keepalive = true;
 480+ goto next;
 481+ case H_LOCATION:
 482+ _location.assign(value, value + vlen);
 483+ break;
 484+ case H_X_WILLOW_BACKEND_GROUP:
 485+ _http_backend.assign(value, value + vlen);
 486+ break;
 487+ case H_X_WILLOW_FOLLOW_REDIRECT:
 488+ _follow_redirect = true;
 489+ break;
 490+ }
 491+
 492+ _headers.add(name, nlen, value, vlen);
 493+ next:
 494+ len -= rn - bufp + 2;
 495+ bufp = rn + 2;
 496+ }
 497+ discard += bufp - buf;
 498+ return io::sink_result_okay;
 499+}
 500+
 501+int
 502+header_parser::parse_reqtype(char const *buf, char const *endp)
 503+{
 504+char const *path, *vers;
 505+size_t plen, vlen;
 506+ if ((path = (char const *)memchr(buf, ' ', endp - buf)) == NULL)
 507+ return -1;
 508+ path++;
 509+ if ((vers = (char const *)memchr(path, ' ', endp - path)) == NULL)
 510+ return -1;
 511+ plen = vers - path;
 512+ vers++;
 513+ vlen = endp - vers;
 514+ if (vlen != 8)
 515+ return -1;
 516+ if (strncmp(vers, "HTTP/", 5))
 517+ return -1;
 518+ if (vers[5] != '1' || vers[6] != '.')
 519+ return -1;
 520+ if (vers[7] == '0')
 521+ _http_vers = http10;
 522+ else if (vers[7] == '1')
 523+ _http_vers = http11;
 524+ else return -1;
 525+ if ((_http_reqtype = find_reqtype(buf, path - buf - 1)) == REQTYPE_INVALID)
 526+ return -1;
 527+
 528+ _http_path.assign(path, path + plen);
 529+ return 0;
 530+}
 531+
 532+int
 533+header_parser::parse_response(char const *buf, char const *endp)
 534+{
 535+char const *errcode, *errdesc;
 536+int codelen, desclen;
 537+ if ((errcode = (char const *)memchr(buf, ' ', endp - buf)) == NULL)
 538+ return -1;
 539+ if (errcode - buf != 8)
 540+ return -1;
 541+ errcode++;
 542+ if ((errdesc = (char const *)memchr(errcode, ' ', endp - errcode)) == NULL)
 543+ return -1;
 544+ codelen = errdesc - errcode;
 545+ errdesc++;
 546+ desclen = endp - errdesc;
 547+ if (strncmp(buf, "HTTP/", 5))
 548+ return -1;
 549+ if (buf[5] != '1' || buf[6] != '.')
 550+ return -1;
 551+ if (buf[7] == '0')
 552+ _http_vers = http10;
 553+ else if (buf[7] == '1')
 554+ _http_vers = http11;
 555+ else return -1;
 556+
 557+ WDEBUG(format("parse_response: codelen=%d [%s] desclen=%d [%s]")
 558+ % codelen % string(errcode, errcode + codelen)
 559+ % desclen % string(errdesc, errdesc + desclen));
 560+
 561+ _response = str10toint(errcode, codelen);
 562+ _http_path.reserve(codelen + desclen + 1);
 563+ _http_path.assign(errcode, errcode + codelen);
 564+ _http_path.append(" ", 1);
 565+ _http_path.append(errdesc, errdesc + desclen);
 566+ return 0;
 567+}
 568+
 569+/*
 570+ * Should never run out of data when reading headers.
 571+ */
 572+io::sink_result
 573+header_parser::data_empty(void)
 574+{
 575+ _sink_spigot->sp_cork();
 576+ if (!_got_reqtype)
 577+ _eof = true;
 578+ return io::sink_result_error;
 579+}
 580+
 581+void
 582+header_parser::sp_cork(void)
 583+{
 584+ _corked = true;
 585+}
 586+
 587+void
 588+header_parser::sending_restart(void)
 589+{
 590+ _buf.reset();
 591+ _built = false;
 592+}
 593+
 594+void
 595+header_parser::sp_uncork(void)
 596+{
 597+ if (!_built) {
 598+ char *s;
 599+ if (!_is_response) {
 600+ string req;
 601+ req.reserve(strlen(request_string[_http_reqtype]) +
 602+ _http_path.size() + 11);
 603+
 604+ req = request_string[_http_reqtype];
 605+ req += _http_path.c_str();
 606+ req += " HTTP/1.";
 607+ if (_http_host.size())
 608+ req += "1\r\n";
 609+ else req += "0\r\n";
 610+ s = new char[req.size()];
 611+ memcpy(s, req.data(), req.size());
 612+ _buf.add(s, req.size(), true);
 613+ } else {
 614+ string req;
 615+ req.reserve(_http_path.size() + 11);
 616+ req = "HTTP/1.1 ";
 617+ req += _http_path.c_str();
 618+ req += "\r\n";
 619+ s = new char[req.size()];
 620+ memcpy(s, req.data(), req.size());
 621+ _buf.add(s, req.size(), true);
 622+ }
 623+ s = _headers.build();
 624+ _buf.add(s, _headers.hl_len, true);
 625+ _buf.add("\r\n", 2, false);
 626+ _built = true;
 627+ }
 628+
 629+ _corked = false;
 630+ while (!_corked && _buf.items.size()) {
 631+ wnet::buffer_item &b = *_buf.items.begin();
 632+ ssize_t discard = 0;
 633+ io::sink_result res;
 634+ res = _sp_sink->data_ready(b.buf + b.off, b.len - b.off, discard);
 635+ if (discard == 0)
 636+ return;
 637+ if ((size_t)discard == b.len) {
 638+ _buf.items.pop_front();
 639+ } else {
 640+ b.len -= discard;
 641+ b.off += discard;
 642+ }
 643+ switch (res) {
 644+ case io::sink_result_finished:
 645+ _sp_completed_callee();
 646+ break;
 647+ case io::sink_result_error:
 648+ _sp_error_callee();
 649+ return;
 650+ case io::sink_result_blocked:
 651+ sp_cork();
 652+ return;
 653+ case io::sink_result_okay:
 654+ continue;
 655+ }
 656+ }
 657+ this->_sp_data_empty();
 658+ _sp_completed_callee();
 659+}
 660+
 661+void
 662+header_parser::set_response(void)
 663+{
 664+ _is_response = true;
 665+}
 666+
 667+header_spigot::header_spigot(int errcode, char const *msg)
 668+ : _built(false)
 669+ , _corked(true)
 670+{
 671+char cstr[4];
 672+ sprintf(cstr, "%d", errcode);
 673+ _first = "HTTP/1.1 ";
 674+ _first += cstr;
 675+ _first += " ";
 676+ _first += msg;
 677+ _first += "\r\n";
 678+}
 679+
 680+void
 681+header_spigot::add(char const *h, char const *v)
 682+{
 683+ _headers.add(h, v);
 684+}
 685+
 686+void
 687+header_spigot::body(string const &body)
 688+{
 689+ _body = body;
 690+}
 691+
 692+void
 693+header_spigot::body(string &body)
 694+{
 695+ _body.swap(body);
 696+}
 697+
 698+void
 699+header_spigot::sp_uncork(void)
 700+{
 701+ _corked = false;
 702+
 703+ if (!_built) {
 704+ _buf.add(_first.data(), _first.size(), false);
 705+ _buf.add(_headers.build(), _headers.hl_len, true);
 706+ _buf.add("\r\n", 2, false);
 707+ _buf.add(_body.data(), _body.size(), false);
 708+ }
 709+
 710+ while (!_corked && _buf.items.size()) {
 711+ buffer_item &b = *_buf.items.begin();
 712+ ssize_t discard = 0;
 713+ io::sink_result res;
 714+
 715+ res = _sp_sink->data_ready(b.buf + b.off, b.len, discard);
 716+ if ((size_t)discard == b.len) {
 717+ _buf.items.pop_front();
 718+ } else {
 719+ b.len -= discard;
 720+ b.off += discard;
 721+ }
 722+ switch (res) {
 723+ case io::sink_result_error:
 724+ _sp_error_callee();
 725+ return;
 726+ case io::sink_result_finished:
 727+ _sp_completed_callee();
 728+ return;
 729+ case io::sink_result_okay:
 730+ continue;
 731+ case io::sink_result_blocked:
 732+ sp_cork();
 733+ return;
 734+ }
 735+ }
 736+ if (!_corked) {
 737+ sp_cork();
 738+ _sp_completed_callee();
 739+ }
 740+}
 741+
 742+void
 743+header_spigot::sp_cork(void)
 744+{
 745+ _corked = true;
 746+}
Index: trunk/willow/src/willow/backend.cc
@@ -0,0 +1,342 @@
 2+/* @(#) $Id: wbackend.cc 17869 2006-11-23 01:05:44Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * backend: HTTP backend handling.
 7+ */
 8+
 9+#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Id: wbackend.cc 17869 2006-11-23 01:05:44Z river $"
 11+#endif
 12+
 13+#include <sys/types.h>
 14+#include <sys/socket.h>
 15+
 16+#include <arpa/inet.h>
 17+
 18+#include <cstdlib>
 19+#include <cstdio>
 20+#include <cstring>
 21+#include <cerrno>
 22+#include <climits>
 23+#include <cmath>
 24+#include <ctime>
 25+#include <algorithm>
 26+using std::sort;
 27+using std::pow;
 28+using std::rotate;
 29+
 30+#include "willow.h"
 31+#include "backend.h"
 32+#include "net.h"
 33+#include "log.h"
 34+#include "confparse.h"
 35+#include "config.h"
 36+#include "format.h"
 37+
 38+map<imstring, int> host_to_bpool;
 39+map<int, backend_pool> bpools;
 40+map<string, int> poolnames;
 41+int nbpools = 1;
 42+
 43+struct backend_cb_data : freelist_allocator<backend_cb_data> {
 44+struct backend *bc_backend;
 45+ polycallback<backend *, wsocket *> bc_func;
 46+ void *bc_data;
 47+};
 48+
 49+backend::backend(
 50+ string const &name,
 51+ address const &addr)
 52+
 53+ : be_name(name)
 54+ , be_straddr(addr.straddr())
 55+ , be_addr(addr)
 56+ , be_dead(false)
 57+ , be_hash(_carp_hosthash(be_straddr))
 58+ , be_load(1.)
 59+{
 60+ WDEBUG(format("adding backend with straddr [%s], hash %s")
 61+ % be_straddr % be_hash);
 62+}
 63+
 64+backend_pool::backend_pool(string const &name_, lb_type lbt, int failgroup)
 65+ : _lbtype(lbt)
 66+ , _name(name_)
 67+ , _failgroup(failgroup)
 68+{
 69+ WDEBUG(format("creating backend_pool, lbt=%d") % (int) lbt);
 70+}
 71+
 72+void
 73+backend_pool::add(string const &addr, int port, int family)
 74+{
 75+addrlist *list;
 76+ try {
 77+ list = addrlist::resolve(addr, port, st_stream, family);
 78+ } catch (resolution_error &e) {
 79+ wlog.error(format("resolving %s: %s") % addr % e.what());
 80+ return;
 81+ }
 82+
 83+addrlist::iterator it = list->begin(), end = list->end();
 84+
 85+ for (; it != end; ++it) {
 86+ backends.push_back(new backend(addr, *it));
 87+ wlog.notice(format("backend server: %s%s")
 88+ % addr % it->straddr());
 89+ }
 90+
 91+ delete list;
 92+ _carp_calc();
 93+}
 94+
 95+int
 96+backend_list::_get_impl(polycallback<backend *, wsocket *> cb)
 97+{
 98+struct backend_cb_data *cbd;
 99+ wsocket *s = NULL;
 100+static time_t last_nfile;
 101+ time_t now = time(NULL);
 102+
 103+ /*
 104+ * If we're delegating (for failover), pass this request off.
 105+ */
 106+ if (_delegate)
 107+ return _delegate->_get_impl(cb);
 108+
 109+ cbd = new backend_cb_data;
 110+ cbd->bc_func = cb;
 111+
 112+ for (;;) {
 113+ cbd->bc_backend = _next_backend();
 114+
 115+ if (cbd->bc_backend == NULL) {
 116+ /*
 117+ * All out of backends. See if we have a failover
 118+ * group to try.
 119+ */
 120+ delete cbd;
 121+ if (_failgroup != -1) {
 122+ _delegate = bpools.find(_failgroup)->second.get_list("", "");
 123+ return _delegate->_get_impl(cb);
 124+ }
 125+ return -1;
 126+ }
 127+
 128+ try {
 129+ s = cbd->bc_backend->be_addr.makesocket(
 130+ "backend connection", prio_backend);
 131+ s->nonblocking(true);
 132+ } catch (socket_error &e) {
 133+ if (e.err() != ENFILE || now - last_nfile > 60)
 134+ wlog.warn(format("opening backend socket: %s")
 135+ % e.what());
 136+ if (e.err() == ENFILE)
 137+ last_nfile = now;
 138+ delete cbd;
 139+ delete s;
 140+ return -1;
 141+ }
 142+
 143+ connect_status cs;
 144+ try {
 145+ cs = s->connect();
 146+ } catch (socket_error &e) {
 147+ time_t retry = time(NULL) + config.backend_retry;
 148+ wlog.warn(format("%s: %s; retry in %d seconds")
 149+ % cbd->bc_backend->be_name
 150+ % e.what() % config.backend_retry);
 151+ cbd->bc_backend->be_dead = 1;
 152+ cbd->bc_backend->be_time = retry;
 153+ delete s;
 154+ continue;
 155+ }
 156+
 157+ if (cs == connect_later) {
 158+ s->writeback(
 159+ polycaller<wsocket *, backend_cb_data*>(*this,
 160+ &backend_list::_backend_read), cbd);
 161+ } else {
 162+ cb(cbd->bc_backend, s);
 163+ delete cbd;
 164+ }
 165+ return 0;
 166+ }
 167+}
 168+
 169+void
 170+backend_list::_backend_read(wsocket *s, backend_cb_data *cbd)
 171+{
 172+int error = s->error();
 173+
 174+ if (error && error != EINPROGRESS) {
 175+ time_t retry = time(NULL) + config.backend_retry;
 176+ wlog.warn(format("%s: %s; retry in %d seconds")
 177+ % cbd->bc_backend->be_name
 178+ % strerror(error)
 179+ % config.backend_retry);
 180+ cbd->bc_backend->be_dead = 1;
 181+ cbd->bc_backend->be_time = retry;
 182+ delete s;
 183+ if (_get_impl(cbd->bc_func) == -1) {
 184+ cbd->bc_func(NULL, NULL);
 185+ }
 186+ delete cbd;
 187+ return;
 188+ }
 189+
 190+ cbd->bc_func(cbd->bc_backend, s);
 191+ delete cbd;
 192+}
 193+
 194+backend_list::backend_list(
 195+ backend_pool const &bp,
 196+ imstring const &url,
 197+ imstring const &host,
 198+ int failgroup,
 199+ lb_type lbt,
 200+ int cur)
 201+
 202+ : backends(bp.backends)
 203+ , _cur(0)
 204+ , _failgroup(failgroup)
 205+ , _delegate(NULL)
 206+{
 207+ WDEBUG(format("lbt = %d") % (int)lbt);
 208+ rotate(backends.begin(), backends.begin() + cur, backends.end());
 209+ if (lbt == lb_carp || lbt == lb_carp_hostonly)
 210+ _carp_recalc(url, host, lbt);
 211+}
 212+
 213+backend_pool::~backend_pool(void)
 214+{
 215+ for (size_t i = 0; i < backends.size(); ++i)
 216+ delete backends[i];
 217+}
 218+
 219+backend_list *
 220+backend_pool::get_list(imstring const &url, imstring const &host)
 221+{
 222+ if (_cur == 0)
 223+ _cur = new size_t();
 224+ if (*_cur >= backends.size())
 225+ *_cur = 0;
 226+
 227+ return new backend_list(*this, url, host, _failgroup, _lbtype, (*_cur)++);
 228+}
 229+
 230+struct backend *
 231+backend_list::_next_backend(void)
 232+{
 233+size_t tried = 0;
 234+
 235+ while (tried++ <= backends.size()) {
 236+ time_t now = time(NULL);
 237+
 238+ if (_cur >= backends.size())
 239+ _cur = 0;
 240+
 241+ if (backends[_cur]->be_dead && now >= backends[_cur]->be_time)
 242+ backends[_cur]->be_dead = 0;
 243+
 244+ if (backends[_cur]->be_dead) {
 245+ _cur++;
 246+ continue;
 247+ }
 248+
 249+ return backends[_cur++];
 250+ }
 251+
 252+ return NULL;
 253+}
 254+
 255+void
 256+backend_pool::_carp_calc(void)
 257+{
 258+struct backend *be, *prev;
 259+ size_t i, j;
 260+
 261+ backends[0]->be_carp = (uint32_t) pow((double) (backends.size() * backends[0]->be_load), 1.0 / backends.size());
 262+ backends[0]->be_carplfm = 1.0;
 263+ for (i = 1; i < backends.size(); ++i) {
 264+ float l = 0;
 265+ be = backends[i];
 266+ prev = backends[i - 1];
 267+ be->be_carplfm = 1.0 + ((backends.size()-i+1) * (be->be_load - prev->be_load));
 268+ for (j = 0; j < i; ++j)
 269+ l *= backends[j]->be_carp;
 270+ be->be_carp = (uint32_t) (be->be_carp / l);
 271+ be->be_carp += (uint32_t) pow(prev->be_carp, (double) backends.size()-i+1);
 272+ be->be_carp = (uint32_t) pow(be->be_carp, (double) 1/(backends.size()-i+1));
 273+ }
 274+}
 275+
 276+int
 277+backend_pool::size(void) const
 278+{
 279+ return backends.size();
 280+}
 281+
 282+string const &
 283+backend_pool::name(void) const
 284+{
 285+ return _name;
 286+}
 287+
 288+void
 289+backend_pool::add_keptalive(pair<wsocket *, backend *>s)
 290+{
 291+ if (!config.backend_keepalive)
 292+ return;
 293+
 294+ if (!_keptalive)
 295+ _keptalive = new vector<pair<wsocket *, backend *> >;
 296+ else while (config.keepalive_max && (_keptalive->size() >= (size_t)config.keepalive_max)) {
 297+ delete _keptalive->begin()->first;
 298+ _keptalive->erase(_keptalive->begin());
 299+ }
 300+
 301+ _keptalive->push_back(s);
 302+}
 303+
 304+pair<wsocket *, backend *>
 305+backend_pool::get_keptalive(void)
 306+{
 307+ if (!config.backend_keepalive)
 308+ return pair<wsocket *, backend *>(0, 0);
 309+
 310+ if (!_keptalive)
 311+ _keptalive = new vector<pair<wsocket *, backend *> >;
 312+ if (_keptalive->empty())
 313+ return pair<wsocket *, backend *>(0, 0);
 314+pair<wsocket *, backend *> ret = *_keptalive->rbegin();
 315+ _keptalive->pop_back();
 316+ return ret;
 317+}
 318+
 319+void
 320+backend_list::_carp_recalc(imstring const &url, imstring const &host, lb_type lbtype)
 321+{
 322+ uint32_t hash = 0;
 323+ size_t i;
 324+ for (i = 0; i < backends.size(); ++i) {
 325+ imstring s = url;
 326+ if (lbtype == lb_carp_hostonly)
 327+ s = host;
 328+ hash = _carp_urlhash(s) ^ backends[i]->be_hash;
 329+ hash += hash * 0x62531965;
 330+ hash = rotl(hash, 21);
 331+ hash *= (uint32_t) backends[i]->be_carplfm;
 332+ backends[i]->be_carp = hash;
 333+ WDEBUG(format("host for CARP: [%s] -> %d, be hash %d")
 334+ % s % hash % backends[i]->be_hash);
 335+ }
 336+ sort(backends.begin(), backends.end(), _becarp_cmp);
 337+}
 338+
 339+int
 340+backend_list::_becarp_cmp(backend const *a, backend const *b)
 341+{
 342+ return a->be_carp < b->be_carp ? true : false;
 343+}
Index: trunk/willow/src/willow/Makefile.in
@@ -4,23 +4,23 @@
55
66 BASESRCS = \
77 a_cachedir.cc \
 8+ backend.cc \
89 cache.cc \
910 cachedentity.cc \
1011 cachedir_data_store.cc \
1112 chunking.cc \
 13+ config.cc \
1214 confparse.cc \
1315 dbwrap.cc \
1416 flowio.cc \
1517 format.cc \
1618 htcp.cc \
 19+ http.cc \
 20+ http_header.cc \
 21+ log.cc \
 22+ net.cc \
1723 radix.cc \
18 - wbackend.cc \
19 - wconfig.cc \
20 - whttp.cc \
21 - whttp_header.cc \
2224 willow.cc \
23 - wlog.cc \
24 - wnet.cc \
2525
2626 OBJS= $(BASESRCS:.cc=.o)
2727
Index: trunk/willow/src/willow/htcp.cc
@@ -13,8 +13,8 @@
1414 using std::pair;
1515
1616 #include "willow.h"
17 -#include "wnet.h"
18 -#include "wconfig.h"
 17+#include "net.h"
 18+#include "config.h"
1919 #include "mbuffer.h"
2020 #include "htcp.h"
2121 #include "cache.h"
Index: trunk/willow/src/willow/flowio.cc
@@ -22,9 +22,9 @@
2323 using std::streamsize;
2424
2525 #include "flowio.h"
26 -#include "wnet.h"
 26+#include "net.h"
2727 #include "format.h"
28 -#include "wconfig.h"
 28+#include "config.h"
2929
3030 namespace io {
3131
Index: trunk/willow/src/willow/cache.cc
@@ -16,7 +16,7 @@
1717
1818 #include "cache.h"
1919 #include "format.h"
20 -#include "wconfig.h"
 20+#include "config.h"
2121
2222 httpcache entitycache;
2323
Index: trunk/willow/src/willow/config.cc
@@ -0,0 +1,588 @@
 2+/* @(#) $Id: wconfig.cc 17855 2006-11-22 14:35:17Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * config: configuration.
 7+ */
 8+
 9+#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Id: wconfig.cc 17855 2006-11-22 14:35:17Z river $"
 11+#endif
 12+
 13+#include <sys/types.h>
 14+#include <sys/socket.h>
 15+
 16+#include <netinet/in.h>
 17+#include <arpa/inet.h>
 18+#include <syslog.h>
 19+
 20+#include <cstdlib>
 21+#include <cstdio>
 22+#include <cstring>
 23+#include <cerrno>
 24+#include <climits>
 25+#include <netdb.h>
 26+#include <pthread.h>
 27+#include <set>
 28+#include <fstream>
 29+using std::ifstream;
 30+using std::set;
 31+using std::back_inserter;
 32+
 33+#include "willow.h"
 34+#include "config.h"
 35+#include "backend.h"
 36+#include "log.h"
 37+#include "http.h"
 38+#include "net.h"
 39+#include "confparse.h"
 40+#include "radix.h"
 41+#include "format.h"
 42+
 43+using namespace conf;
 44+
 45+map<wsocket *, listener *> sock2lsn;
 46+set<int> used_pools;
 47+
 48+#define CONFIGFILE SYSCONFDIR "/willow.conf"
 49+
 50+vector<listener *> listeners;
 51+struct configuration config;
 52+
 53+static void
 54+set_backend(conf::tree_entry &e)
 55+{
 56+value const *val;
 57+int port = 80, family = AF_UNSPEC, gn = 0;
 58+string group = "<default>";
 59+map<string, int>::iterator it;
 60+
 61+ if ((val = e/"port") != NULL)
 62+ port = CONF_AINTVAL(*val);
 63+ if ((val = e/"aftype") != NULL)
 64+ if (val->cv_values[0].av_strval == "ipv6")
 65+ family = AF_INET6;
 66+ else family = AF_INET;
 67+
 68+ if ((val = e/"group") != NULL) {
 69+ group = val->cv_values[0].av_strval;
 70+
 71+ it = poolnames.find(group);
 72+ if (it == poolnames.end()) {
 73+ val->report_error("backend group %s does not exist",
 74+ group.c_str());
 75+ return;
 76+ } else
 77+ gn = it->second;
 78+ }
 79+
 80+ used_pools.insert(gn);
 81+ bpools.find(gn)->second.add(e.item_key, port, family);
 82+}
 83+
 84+static void
 85+set_listen(conf::tree_entry &e)
 86+{
 87+value const *val;
 88+int port = 80;
 89+struct listener *nl;
 90+int gn = 0;
 91+string group;
 92+int fam = AF_UNSPEC;
 93+addrlist *res;
 94+
 95+ if ((val = e/"port") != NULL)
 96+ port = CONF_AINTVAL(*val);
 97+
 98+ if ((val = e/"aftype") != NULL)
 99+ if (val->cv_values[0].av_strval == "ipv6")
 100+ fam = AF_INET6;
 101+ else fam = AF_INET;
 102+
 103+ if ((val = e/"group") != NULL) {
 104+ map<string, int>::iterator it;
 105+ group = val->cv_values[0].av_strval;
 106+
 107+ it = poolnames.find(group);
 108+ if (it == poolnames.end()) {
 109+ val->report_error("backend group %s does not exist",
 110+ group.c_str());
 111+ return;
 112+ } else
 113+ gn = it->second;
 114+ }
 115+
 116+ try {
 117+ res = addrlist::resolve(e.item_key, port, st_stream, fam);
 118+ } catch (socket_error &ex) {
 119+ wlog.error(format("resolving %s: %s") % e.item_key % ex.what());
 120+ return;
 121+ }
 122+
 123+addrlist::iterator it = res->begin(), end = res->end();
 124+ for (; it != end; ++it) {
 125+ nl = new listener;
 126+ nl->nconns = 0;
 127+
 128+ try {
 129+ nl->sock = it->makesocket("HTTP listener", prio_accept);
 130+ } catch (socket_error &ex) {
 131+ wlog.error(format("creating listener %s: %s")
 132+ % e.item_key % ex.what());
 133+ delete nl;
 134+ delete res;
 135+ return;
 136+ }
 137+ WDEBUG(format("listener %d has group %d") % nl->sock % gn);
 138+ sock2lsn[nl->sock] = nl;
 139+ used_pools.insert(gn);
 140+
 141+ nl->port = port;
 142+ nl->name = e.item_key;
 143+ nl->group = gn;
 144+ listeners.push_back(nl);
 145+ wlog.notice(format("listening on %s%s (group %d)")
 146+ % e.item_key % it->straddr() % gn);
 147+ }
 148+ delete res;
 149+}
 150+
 151+map<string, int> log_levels = map_list_of
 152+ ("auth", LOG_AUTH)
 153+ ("authpriv", LOG_AUTHPRIV)
 154+ ("cron", LOG_CRON)
 155+ ("daemon", LOG_DAEMON)
 156+ ("ftp", LOG_FTP)
 157+ ("kern", LOG_KERN)
 158+ ("local0", LOG_LOCAL0)
 159+ ("local1", LOG_LOCAL1)
 160+ ("local2", LOG_LOCAL2)
 161+ ("local3", LOG_LOCAL3)
 162+ ("local4", LOG_LOCAL4)
 163+ ("local5", LOG_LOCAL5)
 164+ ("local6", LOG_LOCAL6)
 165+ ("local7", LOG_LOCAL7)
 166+ ("lpr", LOG_LPR)
 167+ ("mail", LOG_MAIL)
 168+ ("news", LOG_NEWS)
 169+ ("syslog", LOG_SYSLOG)
 170+ ("user", LOG_USER)
 171+ ("uucp", LOG_UUCP)
 172+ ;
 173+
 174+static bool
 175+validate_log_facility(tree_entry &, value &v)
 176+{
 177+ if (!v.is_single(cv_string)) {
 178+ v.report_error("expected single unquoted string");
 179+ return false;
 180+ }
 181+
 182+ if (log_levels.find(v.cv_values[0].av_strval) == log_levels.end()) {
 183+ v.report_error("log level does not exist");
 184+ return false;
 185+ }
 186+
 187+ return true;
 188+}
 189+
 190+static void
 191+set_log_facility(tree_entry &, value &v)
 192+{
 193+ wlog.syslog(true, log_levels.find(v.cv_values[0].av_strval)->second);
 194+}
 195+
 196+static bool
 197+v_udp_log(tree_entry &e, value &v)
 198+{
 199+bool ret = true;
 200+ if (e/"udp-host" == NULL) {
 201+ v.report_error("udp-host must be specified for UDP logging");
 202+ ret = false;
 203+ }
 204+
 205+ if (!v.is_single(cv_yesno)) {
 206+ v.report_error("udp-log must be yes/no");
 207+ ret = false;
 208+ }
 209+
 210+ return ret;
 211+}
 212+
 213+static bool
 214+v_aftype(tree_entry &, value &v)
 215+{
 216+ if (!v.is_single(cv_string)) {
 217+ v.report_error("aftype must be single unquoted string");
 218+ return false;
 219+ }
 220+string &s = v.cv_values[0].av_strval;
 221+ if (s != "ipv4" && s != "ipv6") {
 222+ v.report_error("aftype must be \"ipv4\" or \"ipv6\"");
 223+ return false;
 224+ }
 225+ return true;
 226+}
 227+
 228+static void
 229+radix_from_list(tree_entry &e, access_list &rad)
 230+{
 231+value *val;
 232+int immed = 0;
 233+ if ((val = e/"apply-at") != NULL)
 234+ if (val->cv_values[0].av_strval == "connect")
 235+ immed = whttp_deny_connect;
 236+
 237+ if ((val = e/"allow") != NULL) {
 238+ vector<avalue>::iterator it = val->cv_values.begin(),
 239+ end = val->cv_values.end();
 240+ for (; it != end; ++it)
 241+ rad.allow(it->av_strval, immed);
 242+ }
 243+
 244+ if ((val = e/"deny") != NULL) {
 245+ vector<avalue>::iterator it = val->cv_values.begin(),
 246+ end = val->cv_values.end();
 247+ for (; it != end; ++it)
 248+ rad.deny(it->av_strval, immed);
 249+ }
 250+}
 251+
 252+static void
 253+set_access(tree_entry &e)
 254+{
 255+ radix_from_list(e, config.access);
 256+}
 257+
 258+static void
 259+stats_access(tree_entry &, value &v)
 260+{
 261+vector<avalue>::iterator it = v.cv_values.begin(),
 262+ end = v.cv_values.end();
 263+ for (; it != end; ++it)
 264+ stats.access.allow(it->av_strval);
 265+}
 266+
 267+static void
 268+force_backend_access(tree_entry &, value &v)
 269+{
 270+vector<avalue>::iterator it = v.cv_values.begin(),
 271+ end = v.cv_values.end();
 272+ for (; it != end; ++it)
 273+ config.force_backend.allow(it->av_strval, 1);
 274+}
 275+
 276+static bool
 277+radix_prefix(tree_entry &, value &v)
 278+{
 279+vector<avalue>::iterator it = v.cv_values.begin(),
 280+ end = v.cv_values.end();
 281+ for (; it != end; ++it) {
 282+ if (it->av_type != cv_qstring) {
 283+ v.report_error("access prefix must be a list of quoted strings");
 284+ return false;
 285+ }
 286+
 287+ try {
 288+ prefix p(it->av_strval);
 289+ } catch (invalid_prefix& e) {
 290+ v.report_error("%s: %s", it->av_strval.c_str(), e.what());
 291+ return false;
 292+ }
 293+ }
 294+ return true;
 295+}
 296+
 297+bool
 298+v_apply_at(tree_entry &, value &v)
 299+{
 300+ if (!v.is_single(cv_string)) {
 301+ v.report_error("apply-at must be single unquoted string");
 302+ return false;
 303+ }
 304+string &s = v.cv_values[0].av_strval;
 305+ if (s != "connect" && s != "request") {
 306+ v.report_error("expected \"connect\" or \"request\"");
 307+ return false;
 308+ }
 309+ return true;
 310+}
 311+
 312+bool
 313+v_lb_type(tree_entry &, value &v)
 314+{
 315+ if (!v.is_single(cv_string)) {
 316+ v.report_error("lb-type must be single unquoted string");
 317+ return false;
 318+ }
 319+string &s = v.cv_values[0].av_strval;
 320+ if (s != "rr" && s != "carp" && s != "carp-host") {
 321+ v.report_error("expected \"rr\", \"carp\" or \"carp-host\"");
 322+ return false;
 323+ }
 324+ return true;
 325+}
 326+
 327+void
 328+set_backend_group(tree_entry &e)
 329+{
 330+int gn, fogroup = -1;
 331+string group;
 332+map<string, int>::iterator it;
 333+ group = e.item_key;
 334+
 335+ it = poolnames.find(group);
 336+ if (it == poolnames.end()) {
 337+ gn = nbpools;
 338+ poolnames[group] = nbpools++;
 339+ } else
 340+ gn = it->second;
 341+
 342+lb_type lbtype = lb_rr;
 343+value *v;
 344+ if ((v = e/"lb-type") != NULL) {
 345+ string &s = v->cv_values[0].av_strval;
 346+ if (s == "rr")
 347+ lbtype = lb_rr;
 348+ else if (s == "carp")
 349+ lbtype = lb_carp;
 350+ else if (s == "carp-host")
 351+ lbtype = lb_carp_hostonly;
 352+ }
 353+
 354+ if ((v = e/"failover-group") != NULL) {
 355+ if ((it = poolnames.find(v->cv_values[0].av_strval)) == poolnames.end()) {
 356+ v->report_error("failover-group does not exist");
 357+ } else {
 358+ fogroup = it->second;
 359+ }
 360+ }
 361+
 362+ WDEBUG(format("adding backend %d type = %d") % gn % (int) lbtype);
 363+ bpools.insert(make_pair(gn, backend_pool(e.item_key, lbtype, fogroup)));
 364+
 365+ if ((v = e/"hosts") != NULL) {
 366+ vector<avalue>::iterator hit = v->cv_values.begin(), hend = v->cv_values.end();
 367+ for (; hit != hend; ++hit)
 368+ host_to_bpool[imstring(hit->av_strval)] = gn;
 369+ used_pools.insert(gn);
 370+ }
 371+}
 372+
 373+bool
 374+v_hosts(tree_entry &, value &v)
 375+{
 376+vector<avalue>::iterator it = v.cv_values.begin(), end = v.cv_values.end();
 377+ for (; it != end; ++it)
 378+ if (it->av_type != cv_qstring) {
 379+ v.report_error("hosts must be a list of quoted strings");
 380+ return false;
 381+ }
 382+ return true;
 383+}
 384+
 385+void
 386+set_cache_dir(tree_entry &e)
 387+{
 388+ config.cachedirs.push_back(cachedir(e.item_key));
 389+}
 390+
 391+void
 392+set_htcp_keys(tree_entry &e, value &v)
 393+{
 394+string file = v.cv_values[0].av_strval;
 395+ifstream f(file.c_str());
 396+ if (!f.is_open()) {
 397+ v.report_error("cannot open HTCP key file %s: %s",
 398+ file.c_str(), strerror(errno));
 399+ return;
 400+ }
 401+
 402+string s;
 403+int line = 0;
 404+ while (getline(f, s)) {
 405+ string name, key;
 406+ string::size_type i;
 407+ ++line;
 408+ if ((i = s.find(' ')) == string::npos) {
 409+ v.report_error("%s(%d): syntax error",
 410+ file.c_str(), line);
 411+ continue;
 412+ }
 413+ name = s.substr(0, i);
 414+ key = s.substr(i + 1);
 415+
 416+ if (key.size() != 683) {
 417+ v.report_error("%s(%d): key has wrong length",
 418+ file.c_str(), line);
 419+ continue;
 420+ }
 421+
 422+ ustring bkey;
 423+ unbase64_string it(key.begin());
 424+ for (size_t i = 0; i < 64; ++i) {
 425+ bkey.push_back(*it++);
 426+ }
 427+ config.htcp_keys[name] = bkey;
 428+ }
 429+}
 430+
 431+void
 432+set_log_level(tree_entry &e, value &v)
 433+{
 434+ wlog.level(log_level(v.cv_values[0].av_intval));
 435+}
 436+
 437+void
 438+set_log_file(tree_entry &e, value &v)
 439+{
 440+ wlog.file(v.cv_values[0].av_strval);
 441+}
 442+
 443+bool
 444+read_config(string const &file)
 445+{
 446+conf_definer conf;
 447+tree *t;
 448+conf
 449+ .block("log")
 450+ .value("level", simple_range(0, 3), func(set_log_level))
 451+ .value("file", nonempty_qstring, func(set_log_file))
 452+ .value("syslog-facility",
 453+ func(validate_log_facility), func(set_log_facility))
 454+ .value("access-log", nonempty_qstring, set_qstring(config.access_log))
 455+ .value("log-sample", simple_range(1, INT_MAX), set_int(config.log_sample))
 456+ .value("udp-log", func(v_udp_log), set_yesno(config.udp_log))
 457+ .value("udp-port", simple_range(0, 65535), set_int(config.udplog_port))
 458+ .value("udp-host", nonempty_qstring, set_string(config.udplog_host))
 459+
 460+ .block("cache")
 461+ .value("cache-memory", simple_time, set_long(config.cache_memory))
 462+ .value("max-entity-size", simple_time, set_long(config.max_entity_size))
 463+ .value("master-state", nonempty_qstring, set_string(config.cache_master))
 464+ .value("htcp-listen", ip_address_list, add_ip(config.htcp_hosts))
 465+ .value("htcp-keys", nonempty_qstring, func(set_htcp_keys))
 466+ .value("htcp-sig-required", simple_yesno, set_yesno(config.htcp_sigrequired))
 467+
 468+ .block("cache-dir", require_name)
 469+ .end(func(set_cache_dir))
 470+
 471+ .block("http")
 472+ .value("compress", simple_yesno, set_yesno(config.compress))
 473+ .value("compress-level", simple_range(1, 9), set_int(config.complevel))
 474+ .value("backend-retry", simple_time, set_time(config.backend_retry))
 475+ .value("cache-private", simple_yesno, set_yesno(config.cache_private))
 476+ .value("msie-http11-hack", simple_yesno, set_yesno(config.msie_hack))
 477+ .value("default-host", nonempty_qstring, set_string(config.default_host))
 478+ .value("force-backend", func(radix_prefix), func(force_backend_access))
 479+ .value("backend-keepalive", simple_yesno, set_yesno(config.backend_keepalive))
 480+ .value("client-keepalive", simple_yesno, set_yesno(config.client_keepalive))
 481+ .value("keepalive-max", simple_range(0), set_int(config.keepalive_max))
 482+ .value("x-follow-redirect", simple_yesno, set_yesno(config.x_follow))
 483+ .value("max-redirects", simple_range(1), set_int(config.max_redirects))
 484+
 485+ .block("server")
 486+ .value("threads", simple_range(1, 1024), set_int(config.nthreads))
 487+ .value("admin", nonempty_qstring, set_string(config.admin))
 488+ .value("use-dio", simple_yesno, set_yesno(config.use_dio))
 489+
 490+ .block("stats")
 491+ .value("interval", simple_range(1, INT_MAX), set_aint(stats.interval))
 492+ .value("allow", func(radix_prefix), func(stats_access))
 493+ .value("enable", simple_yesno, set_yesno(config.udp_stats))
 494+ .value("listen", ip_address_list, add_ip(config.stats_hosts))
 495+
 496+ .block("listen", require_name)
 497+ .end(func(set_listen))
 498+ .value("port", simple_range(1, 65535), ignore)
 499+ .value("aftype", func(v_aftype), ignore)
 500+ .value("group", nonempty_qstring, ignore)
 501+
 502+ .block("backend-group", require_name)
 503+ .end(func(set_backend_group))
 504+ .value("lb-type", func(v_lb_type), ignore)
 505+ .value("hosts", func(v_hosts), ignore)
 506+ .value("failover-group", nonempty_qstring, ignore)
 507+
 508+ .block("backend", require_name)
 509+ .end(func(set_backend))
 510+ .value("port", simple_range(1, 65535), ignore)
 511+ .value("aftype", func(v_aftype), ignore)
 512+ .value("group", nonempty_qstring, ignore)
 513+
 514+ .block("access")
 515+ .end(func(set_access))
 516+ .value("allow", func(radix_prefix), ignore)
 517+ .value("deny", func(radix_prefix), ignore)
 518+ .value("apply-at", func(v_apply_at), ignore)
 519+ ;
 520+
 521+ if ((t = conf::parse_file(file)) == NULL)
 522+ return false;
 523+ if (!conf.validate(*t))
 524+ return false;
 525+
 526+ /*
 527+ * Defaults
 528+ */
 529+ stats.interval = DEFAULT_STATS_INTERVAL;
 530+ config.nthreads = 1;
 531+ config.admin = "nobody@example.com";
 532+ poolnames["<default>"] = 0;
 533+ bpools.insert(make_pair(0, backend_pool("<default>", lb_rr)));
 534+ config.backend_keepalive = true;
 535+ config.client_keepalive = true;
 536+ config.keepalive_max = 0;
 537+ config.max_redirects = 1;
 538+ config.use_dio = false;
 539+ config.x_follow = false;
 540+ config.cache_memory = 0;
 541+
 542+ conf.set(*t);
 543+ whttp_reconfigure();
 544+ global_conf_tree = *t;
 545+ return true;
 546+}
 547+
 548+void
 549+wconfig_init(const char *file)
 550+{
 551+int nerrors = 0;
 552+ if (file == NULL)
 553+ file = CONFIGFILE;
 554+ conf::current_file = file;
 555+
 556+ wlog.notice(format("loading configuration from %s")
 557+ % conf::current_file);
 558+
 559+ if (!read_config(file)) {
 560+ wlog.error("cannot load configuration");
 561+ nerrors++;
 562+ }
 563+
 564+ if (!listeners.size()) {
 565+ wlog.error("no listeners defined");
 566+ nerrors++;
 567+ }
 568+ if (!bpools.size()) {
 569+ wlog.error("no backends defined");
 570+ nerrors++;
 571+ }
 572+
 573+ for (map<int, backend_pool>::iterator it = bpools.begin(), end = bpools.end();
 574+ it != end; ++it) {
 575+ if (!it->second.size() && used_pools.find(it->first) != used_pools.end()) {
 576+ wlog.error(format(
 577+ "backend group \"%s\" is used but has no backends")
 578+ % it->second.name());
 579+ nerrors++;
 580+ }
 581+ }
 582+
 583+ if (nerrors) {
 584+ wlog.error(format(
 585+ "%d error(s) in configuration file. cannot continue.")
 586+ % nerrors);
 587+ exit(8);
 588+ }
 589+}
Index: trunk/willow/src/willow/chunking.cc
@@ -14,7 +14,7 @@
1515 using std::min;
1616
1717 #include "chunking.h"
18 -#include "whttp_header.h"
 18+#include "http_header.h"
1919 #include "flowio.h"
2020 #include "util.h"
2121
Index: trunk/willow/src/willow/willow.cc
@@ -26,11 +26,11 @@
2727 #include <iostream>
2828 using std::streamsize;
2929
30 -#include "wlog.h"
31 -#include "wnet.h"
32 -#include "wconfig.h"
 30+#include "log.h"
 31+#include "net.h"
 32+#include "config.h"
3333 #include "willow.h"
34 -#include "whttp.h"
 34+#include "http.h"
3535 #include "cache.h"
3636 #include "confparse.h"
3737 #include "radix.h"
Index: trunk/willow/src/willow/http.cc
@@ -0,0 +1,1211 @@
 2+/* @(#) $Id: whttp.cc 17805 2006-11-20 14:07:17Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * http: HTTP implementation.
 7+ */
 8+
 9+#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Id: whttp.cc 17805 2006-11-20 14:07:17Z river $"
 11+#endif
 12+
 13+/*
 14+ * The logic of whttp is explained in whttp_entity.c
 15+ */
 16+
 17+#ifndef _GNU_SOURCE
 18+# define _GNU_SOURCE /* glibc strptime */
 19+#endif
 20+
 21+#include <sys/types.h>
 22+#include <sys/stat.h>
 23+#include <sys/param.h>
 24+
 25+#include <cstdlib>
 26+#include <cstdio>
 27+#include <cstring>
 28+#include <unistd.h>
 29+#include <cerrno>
 30+#include <netdb.h>
 31+#include <fcntl.h>
 32+#include <cassert>
 33+#include <ctime>
 34+#include <fstream>
 35+#include <pthread.h>
 36+
 37+#include <utility>
 38+#include <deque>
 39+using std::deque;
 40+using std::min;
 41+using std::ofstream;
 42+using std::endl;
 43+
 44+#include "willow.h"
 45+#include "http.h"
 46+#include "net.h"
 47+#include "backend.h"
 48+#include "config.h"
 49+#include "log.h"
 50+#include "radix.h"
 51+#include "chunking.h"
 52+#include "flowio.h"
 53+#include "format.h"
 54+#include "cache.h"
 55+
 56+using namespace wnet;
 57+
 58+#ifndef MAXHOSTNAMELEN
 59+# define MAXHOSTNAMELEN HOST_NAME_MAX /* SysV / BSD disagreement */
 60+#endif
 61+
 62+/*
 63+ * Error handling.
 64+ */
 65+#define ERR_NONE -1 /* error with no body */
 66+#define ERR_GENERAL 0 /* unspecified error */
 67+#define ERR_BADREQUEST 1 /* client request invalid */
 68+#define ERR_BADRESPONSE 2 /* backend response invalid */
 69+#define ERR_CACHE_IO 3 /* i/o failure reading cache */
 70+#define ERR_BLOCKED 4 /* client denied by configuration */
 71+
 72+static const char *error_files[] = {
 73+ /* ERR_GENERAL */ DATADIR "/errors/ERR_GENERAL",
 74+ /* ERR_BADREQUEST */ DATADIR "/errors/ERR_BADREQUEST",
 75+ /* ERR_BADRESPONSE */ DATADIR "/errors/ERR_BADRESPONSE",
 76+ /* ERR_CACHE_IO */ DATADIR "/errors/ERR_CACHE_IO",
 77+ /* ERR_BLOCKED */ DATADIR "/errors/ERR_BLOCKED",
 78+};
 79+
 80+static void *client_thread(void *);
 81+static void stats_merge(int, short, void *);
 82+
 83+char via_hdr[1024];
 84+char *cache_hit_hdr;
 85+char *cache_miss_hdr;
 86+
 87+tss<event> merge_ev;
 88+
 89+char my_hostname[MAXHOSTNAMELEN + 1];
 90+static char my_version[64];
 91+static ofstream alf;
 92+lockable alf_lock;
 93+
 94+static int const default_udplog_port = 4445;
 95+wnet::socket *udplog_sock;
 96+static atomic<int> log_count;
 97+static bool do_udplog;
 98+
 99+struct error_transform_filter : io::buffering_filter, freelist_allocator<error_transform_filter>
 100+{
 101+ string const _url;
 102+ string const _errdata;
 103+ string const _statstr;
 104+ int _status;
 105+
 106+ error_transform_filter(
 107+ string const &url,
 108+ string const &errdata,
 109+ string const &statstr,
 110+ int status);
 111+
 112+ io::sink_result bf_transform(char const *, size_t, ssize_t &);
 113+};
 114+
 115+struct httpcllr : freelist_allocator<httpcllr> {
 116+ /* Accept a new client and start processing it. */
 117+ httpcllr(wsocket *, int);
 118+ ~httpcllr();
 119+
 120+ void start_request (void);
 121+ void end_request (bool = true);
 122+ void force_end_request (void);
 123+
 124+ void start_backend_request (imstring const &host, imstring const &path);
 125+ void start_backend_request (imstring const &url);
 126+
 127+ /* reading request from client */
 128+ void header_read_complete (void);
 129+ void header_read_error (void);
 130+ /* sending request to backend */
 131+ void backend_ready (backend *, wsocket *, int);
 132+ void backend_write_headers_done (void);
 133+ void backend_write_body_done (void);
 134+ void backend_write_error (void);
 135+ /* reading request from backend */
 136+ void backend_read_headers_done (void);
 137+ void backend_read_headers_error (void);
 138+ /* sending request to client */
 139+ void send_headers_to_client_done (void);
 140+ void send_headers_to_client_error (void);
 141+ void send_body_to_client_done (void);
 142+ void send_body_to_client_error (void);
 143+ /* sending errors to the client */
 144+ void send_error_to_client (void);
 145+ void error_send_headers_done (void);
 146+ void error_send_done (void);
 147+
 148+ void send_error(int, char const *, int, char const *);
 149+ void send_cached(void);
 150+ void log_request (void);
 151+
 152+ wsocket *_client_socket;
 153+ backend *_backend;
 154+ wsocket *_backend_socket;
 155+
 156+ io::socket_spigot *_client_spigot;
 157+ io::socket_spigot *_backend_spigot;
 158+ io::socket_sink *_backend_sink,
 159+ *_client_sink;
 160+ header_parser *_header_parser,
 161+ *_backend_headers;
 162+ dechunking_filter *_dechunking_filter;
 163+ header_spigot *_error_headers;
 164+ io::file_spigot *_error_body;
 165+ error_transform_filter *_error_filter;
 166+ chunking_filter *_chunking_filter;
 167+ io::size_limiting_filter *_size_limit;
 168+ shared_ptr<cachedentity> _cachedent;
 169+ caching_filter *_cache_filter;
 170+ cached_spigot *_cache_spigot;
 171+
 172+ backend_list *_blist;
 173+ bool _denied;
 174+ int _group;
 175+ int _response;
 176+ imstring _request_host;
 177+ imstring _request_path;
 178+ int _nredir;
 179+ bool _validating;
 180+
 181+private:
 182+ httpcllr(const httpcllr &);
 183+};
 184+
 185+httpcllr::httpcllr(wsocket *s, int gr)
 186+ : _client_socket(s)
 187+ , _backend(NULL)
 188+ , _backend_socket(NULL)
 189+ , _client_spigot(NULL)
 190+ , _backend_spigot(NULL)
 191+ , _backend_sink(NULL)
 192+ , _client_sink(NULL)
 193+ , _header_parser(NULL)
 194+ , _backend_headers(NULL)
 195+ , _dechunking_filter(NULL)
 196+ , _error_headers(NULL)
 197+ , _error_body(NULL)
 198+ , _error_filter(NULL)
 199+ , _chunking_filter(NULL)
 200+ , _size_limit(NULL)
 201+ , _cache_filter(NULL)
 202+ , _cache_spigot(NULL)
 203+ , _blist(NULL)
 204+ , _denied(false)
 205+ , _group(gr)
 206+ , _response(0)
 207+ , _nredir(0)
 208+ , _validating(false)
 209+{
 210+ /*
 211+ * Check access controls.
 212+ */
 213+pair<bool, uint16_t> acc = config.access.allowed(s->address().addr());
 214+ if (!acc.first) {
 215+ if (acc.second & whttp_deny_connect) {
 216+ delete this;
 217+ return;
 218+ }
 219+ _denied = true;
 220+ }
 221+
 222+ _client_spigot = new io::socket_spigot(_client_socket);
 223+ start_request();
 224+}
 225+
 226+void
 227+httpcllr::start_request(void)
 228+{
 229+ /*
 230+ * Start by reading headers.
 231+ */
 232+ _header_parser = new header_parser;
 233+
 234+ _client_spigot->completed_callee(this, &httpcllr::header_read_complete);
 235+ _client_spigot->error_callee(this, &httpcllr::header_read_error);
 236+
 237+ _client_spigot->sp_connect(_header_parser);
 238+ _client_spigot->sp_uncork();
 239+}
 240+
 241+void
 242+httpcllr::force_end_request(void)
 243+{
 244+ end_request(false);
 245+}
 246+
 247+void
 248+httpcllr::end_request(bool tryke)
 249+{
 250+bool can_keepalive = false;
 251+ if (tryke && ((_header_parser->_http_vers == http11 &&
 252+ !_header_parser->_no_keepalive) || _header_parser->_force_keepalive)) {
 253+ can_keepalive = true;
 254+ }
 255+
 256+ delete _backend_spigot;
 257+ _backend_spigot = NULL;
 258+ delete _backend_sink;
 259+ _backend_sink = NULL;
 260+ delete _client_sink;
 261+ _client_sink = NULL;
 262+ delete _dechunking_filter;
 263+ _dechunking_filter = NULL;
 264+ delete _error_headers;
 265+ _error_headers = NULL;
 266+ delete _error_filter;
 267+ _error_filter = NULL;
 268+ delete _error_body;
 269+ _error_body = NULL;
 270+ delete _chunking_filter;
 271+ _chunking_filter = NULL;
 272+ delete _size_limit;
 273+ _size_limit = NULL;
 274+ delete _header_parser;
 275+ _header_parser = NULL;
 276+ delete _cache_filter;
 277+ _cache_filter = NULL;
 278+ delete _cache_spigot;
 279+ _cache_spigot = NULL;
 280+ if (_cachedent)
 281+ entitycache.release(_cachedent);
 282+ _cachedent.reset();
 283+
 284+ /*
 285+ * Return the backend to the keepalive pool, if we can.
 286+ */
 287+ if (_backend_socket && !_backend_headers->_no_keepalive &&
 288+ _backend_headers->_http_vers == http11 && (!_blist || !_blist->failed())) {
 289+ bpools.find(_group)->second.add_keptalive(
 290+ make_pair(_backend_socket, _backend));
 291+ } else {
 292+ delete _backend_socket;
 293+ }
 294+
 295+ delete _blist;
 296+ _blist = NULL;
 297+ _backend_socket = NULL;
 298+ delete _backend_headers;
 299+ _backend_headers = NULL;
 300+
 301+ _client_spigot->sp_disconnect();
 302+ _client_spigot->sp_cork();
 303+
 304+ if (can_keepalive && config.client_keepalive) {
 305+ /*
 306+ * leave the connection open, assuming they will send another
 307+ * request (keep-alive).
 308+ */
 309+ start_request();
 310+ return;
 311+ }
 312+
 313+ /*
 314+ * No keep alive, close the connection.
 315+ */
 316+ delete this;
 317+}
 318+
 319+httpcllr::~httpcllr(void)
 320+{
 321+ delete _client_spigot;
 322+ delete _client_socket;
 323+}
 324+
 325+void
 326+httpcllr::send_cached(void)
 327+{
 328+ if (_header_parser->_http_reqtype == REQTYPE_PURGE) {
 329+ entitycache.purge(_cachedent);
 330+ send_error(ERR_NONE, NULL, 200, "Object removed from cache");
 331+ return;
 332+ }
 333+
 334+ _response = _cachedent->status_code();
 335+
 336+ _cache_spigot = new cached_spigot(_cachedent);
 337+ if (_header_parser->_force_keepalive)
 338+ _cache_spigot->keepalive(true);
 339+
 340+ if (!_client_sink)
 341+ _client_sink = new io::socket_sink(_client_socket);
 342+
 343+ _client_socket->cork();
 344+ _cache_spigot->error_callee(this, &httpcllr::send_body_to_client_error);
 345+ _cache_spigot->completed_callee(this, &httpcllr::send_body_to_client_done);
 346+ _cache_spigot->sp_connect(_client_sink);
 347+ _cache_spigot->sp_uncork();
 348+ return;
 349+}
 350+
 351+void
 352+httpcllr::header_read_complete(void)
 353+{
 354+ _request_host = _header_parser->_http_host;
 355+ _request_path = _header_parser->_http_path;
 356+
 357+ if (_denied) {
 358+ send_error(ERR_BLOCKED, "You are not permitted to access this server.",
 359+ 403, "Forbidden");
 360+ return;
 361+ }
 362+
 363+ /*
 364+ * Now parse the client's headers and decide what to do with
 365+ * the request.
 366+ */
 367+ _header_parser->_headers.add("X-Forwarded-For", _client_socket->straddr(false).c_str());
 368+
 369+ if (_header_parser->_http_reqtype == REQTYPE_POST) {
 370+ if (_header_parser->_content_length == -1) {
 371+ send_error(ERR_BADREQUEST, "POST request without content length",
 372+ 400, "Bad request");
 373+ return;
 374+ }
 375+ }
 376+
 377+ _client_spigot->sp_disconnect();
 378+
 379+ /*
 380+ * See if this entity has been cached.
 381+ */
 382+ if (_header_parser->_http_reqtype != REQTYPE_POST) {
 383+ bool created = false;
 384+ string url = str(format("http://%s%s") % _header_parser->_http_host
 385+ % _header_parser->_http_path);
 386+ _cachedent = entitycache.find_cached(imstring(url), true, created);
 387+ if (_cachedent) {
 388+ if (_cachedent->complete()) {
 389+ /* yes - complete object is available */
 390+ if (_cachedent->expired()) {
 391+ char dstr[64];
 392+ struct tm tm;
 393+ time_t mod = _cachedent->modified();
 394+ WDEBUG(format(
 395+ "HTTP: need to revalidate, %d refs")
 396+ % _cachedent->refs());
 397+ gmtime_r(&mod, &tm);
 398+ /* need to revalidate */
 399+ if (_cachedent->refs() == 1) {
 400+ _validating = true;
 401+ strftime(dstr, sizeof(dstr),
 402+ "%a, %d %b %Y %H:%M:%S GMT", &tm);
 403+ _header_parser->_headers.add(
 404+ "If-Modified-Since", dstr);
 405+ } else {
 406+ entitycache.release(_cachedent);
 407+ _cachedent.reset();
 408+ }
 409+ } else {
 410+ send_cached();
 411+ return;
 412+ }
 413+ } else if (!created) {
 414+ /*
 415+ * If the entity already exists but is not complete, we don't care
 416+ * about it.
 417+ */
 418+ entitycache.release(_cachedent);
 419+ _cachedent.reset();
 420+ }
 421+ }
 422+ }
 423+
 424+ /*
 425+ * If we get here for a PURGE request, it means that the requested
 426+ * object was not cached. So, send an error reply.
 427+ */
 428+ if (_header_parser->_http_reqtype == REQTYPE_PURGE) {
 429+ send_error(ERR_NONE, NULL, 404, "Object not cached");
 430+ return;
 431+ }
 432+
 433+map<string,int>::iterator it;
 434+map<imstring,int>::iterator mit;
 435+pair<bool, uint16_t> acheck;
 436+
 437+ if ((mit = host_to_bpool.find(_header_parser->_http_host)) !=
 438+ host_to_bpool.end())
 439+ _group = mit->second;
 440+
 441+ if (!_header_parser->_http_backend.empty()) {
 442+ acheck = config.force_backend.allowed(_client_socket->address().addr());
 443+ if (acheck.first && acheck.second) {
 444+ if ((it = poolnames.find(_header_parser->_http_backend))
 445+ != poolnames.end()) {
 446+ _group = it->second;
 447+ }
 448+ }
 449+ }
 450+
 451+ start_backend_request(_header_parser->_http_host,
 452+ _header_parser->_http_path);
 453+}
 454+
 455+void
 456+httpcllr::start_backend_request(imstring const &url_)
 457+{
 458+char *url = url_.c_str(), *slash;
 459+ if (strncasecmp(url, "http://", 7)) {
 460+ send_error(ERR_BADRESPONSE,
 461+ "Could not parse server redirect location",
 462+ 503, "Internal server error");
 463+ return;
 464+ }
 465+ url += 7;
 466+ slash = strchr(url, '/');
 467+ if (slash == NULL) {
 468+ start_backend_request(url, "/");
 469+ return;
 470+ }
 471+ start_backend_request(imstring(url, slash - url), slash);
 472+}
 473+
 474+void
 475+httpcllr::start_backend_request(imstring const &host, imstring const &path)
 476+{
 477+pair<wsocket *, backend *> ke;
 478+
 479+ /*
 480+ * Never try to send a POST request over keepalive - if the connection
 481+ * breaks we don't have the POST data anymore so we can't retry.
 482+ */
 483+ if (_header_parser->_http_reqtype != REQTYPE_POST)
 484+ ke = bpools.find(_group)->second.get_keptalive();
 485+
 486+ _request_host = host;
 487+ _request_path = path;
 488+
 489+ delete _backend_sink;
 490+ _backend_sink = NULL;
 491+ delete _backend_spigot;
 492+ _backend_spigot = NULL;
 493+ delete _size_limit;
 494+ _size_limit = NULL;
 495+ delete _dechunking_filter;
 496+ _dechunking_filter = NULL;
 497+ delete _chunking_filter;
 498+ _chunking_filter = NULL;
 499+ delete _blist;
 500+ _blist = NULL;
 501+ delete _backend_headers;
 502+ _backend_headers = NULL;
 503+
 504+ if (ke.first) {
 505+ backend_ready(ke.second, ke.first, 0);
 506+ return;
 507+ }
 508+
 509+ if (!_blist)
 510+ _blist = bpools.find(_group)->second.get_list(
 511+ _request_path, _request_host);
 512+
 513+ if (_blist->get(polycaller<backend *, wsocket *, int>(*this,
 514+ &httpcllr::backend_ready), 0) == -1)
 515+ backend_ready(NULL, NULL, 0);
 516+}
 517+
 518+void
 519+httpcllr::header_read_error(void)
 520+{
 521+ WDEBUG(format("header read error errno=%s") % strerror(errno));
 522+
 523+ if (_header_parser->_eof) {
 524+ force_end_request();
 525+ return;
 526+ }
 527+
 528+ stats.tcur->n_httpreq_fail++;
 529+ send_error(ERR_BADREQUEST, "Could not parse client headers", 400, "Bad request");
 530+}
 531+
 532+void
 533+httpcllr::backend_ready(backend *be, wsocket *s, int)
 534+{
 535+ if (be == NULL) {
 536+ stats.tcur->n_httpreq_fail++;
 537+ send_error(ERR_GENERAL, "No backends were available to serve your request",
 538+ 503, "Internal server error");
 539+ return;
 540+ }
 541+
 542+ /*
 543+ * Create the backend socket_sink, connect the header parser to it
 544+ * and start sending headers.
 545+ */
 546+ s->cork();
 547+ _backend_socket = s;
 548+ _backend = be;
 549+ _backend_sink = new io::socket_sink(s);
 550+
 551+ if (_request_host.size())
 552+ _header_parser->_http_host = _request_host;
 553+ if (_request_path.size())
 554+ _header_parser->_http_path = _request_path;
 555+
 556+ _header_parser->sending_restart();
 557+ _header_parser->completed_callee(this, &httpcllr::backend_write_headers_done);
 558+ _header_parser->error_callee(this, &httpcllr::backend_write_error);
 559+ _header_parser->sp_connect(_backend_sink);
 560+ _header_parser->sp_uncork();
 561+}
 562+
 563+void
 564+httpcllr::backend_write_error(void)
 565+{
 566+ start_backend_request(_request_host, _request_path);
 567+}
 568+
 569+void
 570+httpcllr::backend_write_headers_done(void)
 571+{
 572+ if (_header_parser->_http_reqtype == REQTYPE_POST) {
 573+ /*
 574+ * Connect the client to the backend and read the POST data.
 575+ */
 576+ _size_limit = new io::size_limiting_filter(_header_parser->_content_length);
 577+ _client_spigot->sp_connect(_size_limit);
 578+ _size_limit->sp_connect(_backend_sink);
 579+
 580+ _client_spigot->completed_callee(this, &httpcllr::backend_write_body_done);
 581+ _client_spigot->error_callee(this, &httpcllr::backend_write_error);
 582+
 583+ _client_spigot->sp_uncork();
 584+ return;
 585+ }
 586+ backend_write_body_done();
 587+}
 588+
 589+void
 590+httpcllr::backend_write_body_done(void)
 591+{
 592+ _backend_socket->uncork();
 593+
 594+ /*
 595+ * Detach the backend sink and create a spigot to read the reply.
 596+ */
 597+ _header_parser->sp_disconnect();
 598+
 599+ _backend_headers = new header_parser;
 600+ _backend_headers->set_response();
 601+
 602+ _backend_spigot = new io::socket_spigot(_backend_socket);
 603+ _backend_spigot->completed_callee(this, &httpcllr::backend_read_headers_done);
 604+ _backend_spigot->error_callee(this, &httpcllr::backend_read_headers_error);
 605+ _backend_spigot->sp_connect(_backend_headers);
 606+ _backend_spigot->sp_uncork();
 607+}
 608+
 609+void
 610+httpcllr::backend_read_headers_done(void)
 611+{
 612+ _response = _backend_headers->_response;
 613+
 614+ if (_validating && _response == 304) {
 615+ /* Our cached entity was still valid */
 616+ _cachedent->revalidated();
 617+ send_cached();
 618+ return;
 619+ }
 620+
 621+ /*
 622+ * Check for X-Willow-Follow-Redirect header, which means we should
 623+ * follow the redirect.
 624+ */
 625+ if (config.x_follow &&
 626+ _backend_headers->_follow_redirect &&
 627+ _backend_headers->_location.size() &&
 628+ (_backend_headers->_response >= 300 && _backend_headers->_response < 400)) {
 629+ if (config.max_redirects && (_nredir++ == config.max_redirects)) {
 630+ send_error(ERR_BADRESPONSE, "Too many redirects in server reply",
 631+ 503, "Internal server error");
 632+ return;
 633+ }
 634+
 635+ start_backend_request(_backend_headers->_location);
 636+ return;
 637+ }
 638+
 639+ /*
 640+ * If we're caching this entity, store the headers.
 641+ */
 642+ if (_cachedent) {
 643+ string status = str(format("HTTP/1.1 %s\r\n")
 644+ % _backend_headers->_http_path);
 645+ _cachedent->store_status(status, _backend_headers->_response);
 646+ _cachedent->store_headers(_backend_headers->_headers);
 647+ }
 648+
 649+ /*
 650+ * If the client is HTTP/1.0 or MSIE, we need to dechunk.
 651+ */
 652+ if (_backend_headers->_flags.f_chunked && _header_parser->_http_vers == http10)
 653+ _backend_headers->_headers.remove("Transfer-Encoding");
 654+ else if (_backend_headers->_flags.f_chunked && config.msie_hack && _header_parser->_is_msie)
 655+ _backend_headers->_headers.remove("Transfer-Encoding");
 656+
 657+ /*
 658+ * Insert HTTP/1.0 keep-alive headers.
 659+ */
 660+ if (_header_parser->_force_keepalive) {
 661+ _backend_headers->_headers.add("Keep-Alive", "300");
 662+ }
 663+
 664+ _backend_headers->_headers.add("X-Cache", cache_miss_hdr);
 665+ _backend_headers->_headers.add("Via", via_hdr);
 666+
 667+ /*
 668+ * Send the headers to the client.
 669+ */
 670+ _backend_spigot->sp_disconnect();
 671+
 672+ _client_socket->cork();
 673+ _client_sink = new io::socket_sink(_client_socket);
 674+ _backend_headers->completed_callee(this, &httpcllr::send_headers_to_client_done);
 675+ _backend_headers->error_callee(this, &httpcllr::send_headers_to_client_error);
 676+
 677+ _backend_headers->sp_connect(_client_sink);
 678+ _backend_headers->sp_uncork();
 679+}
 680+
 681+void
 682+httpcllr::backend_read_headers_error(void)
 683+{
 684+ /*
 685+ * Try another backend...
 686+ */
 687+ start_backend_request(_request_host, _request_path);
 688+}
 689+
 690+void
 691+httpcllr::send_headers_to_client_done(void)
 692+{
 693+bool cache = false;
 694+io::spigot *cur = _backend_spigot;
 695+bool rechunk = false;
 696+bool chunked;
 697+
 698+ /*
 699+ * Now connect the backend directly to the client.
 700+ */
 701+ _backend_spigot->error_callee(this, &httpcllr::send_body_to_client_error);
 702+ _backend_spigot->completed_callee(this, &httpcllr::send_body_to_client_done);
 703+
 704+ /*
 705+ * See if we can cache this entity.
 706+ */
 707+ if (_cachedent) {
 708+ cache = true;
 709+ _cache_filter = new caching_filter(_cachedent);
 710+ }
 711+
 712+ _backend_spigot->sp_disconnect();
 713+
 714+ if (_backend_headers->_response == 304 && _backend_headers->_content_length < 0) {
 715+ /* No body */
 716+ send_body_to_client_done();
 717+ return;
 718+ }
 719+
 720+ cur = _backend_spigot;
 721+ chunked = _backend_headers->_flags.f_chunked;
 722+
 723+ /*
 724+ * If the client is HTTP/1.0 or MSIE, we need to dechunk.
 725+ */
 726+ if (chunked) {
 727+ if (_header_parser->_http_vers == http10 ||
 728+ (config.msie_hack && _header_parser->_is_msie)) {
 729+ _dechunking_filter = new dechunking_filter;
 730+ cur = &(*cur >> *_dechunking_filter);
 731+ } else if (cache) {
 732+ _dechunking_filter = new dechunking_filter;
 733+ cur = &(*cur >> *_dechunking_filter);
 734+ rechunk = true;
 735+ }
 736+ }
 737+
 738+ /* if we got a content-length, insert a size limit */
 739+ if (!chunked && _backend_headers->_content_length != -1) {
 740+ _size_limit = new io::size_limiting_filter(
 741+ _backend_headers->_content_length);
 742+ cur = &(*cur >> *_size_limit);
 743+ }
 744+
 745+ /* if we're caching, insert the caching filter */
 746+ if (cache) {
 747+ cur = &(*cur >> *_cache_filter);
 748+ /* if we dechunked it to cache, rechunk now */
 749+ if (rechunk) {
 750+ _chunking_filter = new chunking_filter;
 751+ cur = &(*cur >> *_chunking_filter);
 752+ }
 753+ }
 754+
 755+ /* finally, connect to the client */
 756+ *cur >> *_client_sink;
 757+
 758+ _client_sink->_counter = 0;
 759+ _backend_spigot->sp_uncork();
 760+}
 761+
 762+
 763+void
 764+httpcllr::send_body_to_client_done(void)
 765+{
 766+ _client_socket->uncork();
 767+ stats.tcur->n_httpreq_ok++;
 768+
 769+ log_request();
 770+ end_request();
 771+}
 772+
 773+void
 774+httpcllr::send_body_to_client_error(void)
 775+{
 776+ stats.tcur->n_httpreq_fail++;
 777+ end_request();
 778+}
 779+
 780+void
 781+httpcllr::send_headers_to_client_error(void)
 782+{
 783+ stats.tcur->n_httpreq_fail++;
 784+ end_request();
 785+}
 786+
 787+/*
 788+ * Initialize whttp, start loggers.
 789+ */
 790+struct http_thread : freelist_allocator<http_thread> {
 791+ pthread_t thr;
 792+ pair<wnet::socket *, wnet::socket *>
 793+ sv;
 794+
 795+ void execute (void);
 796+ void accept_wakeup (wsocket *, int);
 797+};
 798+vector<http_thread *> threads;
 799+
 800+void
 801+whttp_init(void)
 802+{
 803+ int hsize;
 804+
 805+ if (gethostname(my_hostname, MAXHOSTNAMELEN) < 0) {
 806+ perror("gethostname");
 807+ exit(8);
 808+ }
 809+
 810+ (void)strlcpy(my_version, "Willow/" PACKAGE_VERSION, 64);
 811+ snprintf(via_hdr, sizeof(via_hdr), "1.1 %s (%s)", my_hostname, my_version);
 812+
 813+ hsize = sizeof("MISS from ") + strlen(my_hostname);
 814+ cache_hit_hdr = (char *)wmalloc(hsize + 1);
 815+ cache_miss_hdr = (char *)wmalloc(hsize + 1);
 816+
 817+ if (cache_hit_hdr == NULL || cache_miss_hdr == NULL)
 818+ outofmemory();
 819+
 820+ snprintf(cache_hit_hdr, hsize, "HIT from %s", my_hostname);
 821+ snprintf(cache_miss_hdr, hsize, "MISS from %s", my_hostname);
 822+
 823+ wlog.notice(format("whttp: starting %d worker threads")
 824+ % config.nthreads);
 825+ for (int i = 0; i < config.nthreads; ++i) {
 826+ http_thread *t = new http_thread;
 827+ pthread_attr_t attr;
 828+ pthread_attr_init(&attr);
 829+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 830+ t->sv = wnet::socket::socketpair(st_dgram);
 831+ wnet_add_accept_wakeup(t->sv.first);
 832+ threads.push_back(t);
 833+ pthread_create(&t->thr, &attr, client_thread, t);
 834+ }
 835+ whttp_header_init();
 836+}
 837+
 838+void
 839+http_thread::accept_wakeup(wsocket *s, int)
 840+{
 841+wsocket *socks[2];
 842+map<wsocket *, listener *>::iterator lsnit;
 843+
 844+ if (s->read((char *)socks, sizeof(socks)) < (int)sizeof(socks)) {
 845+ wlog.error(format("accept_wakeup: reading fd: %s")
 846+ % strerror(errno));
 847+ exit(1);
 848+ }
 849+ WDEBUG(format("accept_wakeup, lsnr = %d") % socks[1]);
 850+ s->readback(polycaller<wsocket *, int>(*this,
 851+ &http_thread::accept_wakeup), 0);
 852+ if ((lsnit = sock2lsn.find(socks[1])) == sock2lsn.end())
 853+ throw runtime_error("listener not found");
 854+
 855+ ++lsnit->second->nconns;
 856+ new httpcllr(socks[0], lsnit->second->group);
 857+}
 858+
 859+static
 860+void merge_sched(void)
 861+{
 862+timeval tv;
 863+ tv.tv_usec = 250000;
 864+ tv.tv_sec = 0;
 865+ evtimer_set(merge_ev, stats_merge, NULL);
 866+ event_base_set(evb, merge_ev);
 867+ event_add(merge_ev, &tv);
 868+}
 869+
 870+static void *
 871+client_thread(void *arg)
 872+{
 873+http_thread *t = (http_thread *)arg;
 874+ t->execute();
 875+ return NULL;
 876+}
 877+
 878+void
 879+http_thread::execute(void)
 880+{
 881+ make_event_base();
 882+ stats.tcur = new stats_stru::abs_t;
 883+ merge_ev = new event;
 884+ memset(merge_ev, 0, sizeof(*merge_ev));
 885+ merge_sched();
 886+
 887+ sv.second->readback(polycaller<wsocket *, int>(*this,
 888+ &http_thread::accept_wakeup), 0);
 889+ event_base_loop(evb, 0);
 890+ delete merge_ev;
 891+ delete stats.tcur;
 892+ return;
 893+}
 894+
 895+static void
 896+stats_merge(int, short, void *)
 897+{
 898+timeval tv = {0, 0};
 899+ if (wnet_exit) {
 900+ event_base_loopexit(evb, &tv);
 901+ return;
 902+ }
 903+
 904+ { HOLDING(stats.cur_lock);
 905+ stats.cur.n_httpreq_ok += stats.tcur->n_httpreq_ok;
 906+ stats.tcur->n_httpreq_ok = 0;
 907+ stats.cur.n_httpreq_fail += stats.tcur->n_httpreq_fail;
 908+ stats.tcur->n_httpreq_fail = 0;
 909+ stats.cur.n_httpresp_ok += stats.tcur->n_httpresp_ok;
 910+ stats.tcur->n_httpreq_ok = 0;
 911+ stats.cur.n_httpresp_fail += stats.tcur->n_httpresp_fail;
 912+ stats.tcur->n_httpresp_fail = 0;
 913+ }
 914+ merge_sched();
 915+}
 916+
 917+void
 918+whttp_reconfigure(void)
 919+{
 920+ /* file logging */
 921+ if (config.access_log.size()) {
 922+ alf.open(config.access_log.c_str(), ofstream::app);
 923+ if (!alf.good()) {
 924+ wlog.warn(format("opening %s: %s")
 925+ % config.access_log % strerror(errno));
 926+ }
 927+ }
 928+
 929+ /* UDP logging */
 930+ if (config.udp_log) {
 931+ if (config.udplog_port == 0)
 932+ config.udplog_port = default_udplog_port;
 933+
 934+ try {
 935+ udplog_sock = wnet::socket::create(config.udplog_host,
 936+ config.udplog_port, st_dgram, "UDP logger", prio_norm);
 937+ udplog_sock->connect();
 938+ } catch (socket_error &e) {
 939+ wlog.warn(format(
 940+ "connecting to UDP log host %s: %s; disabling UDP logging")
 941+ % config.udplog_host % e.what());
 942+ return;
 943+ }
 944+
 945+ do_udplog = true;
 946+ wlog.notice(format("UDP logging to %s%s, sample rate 1/%d")
 947+ % config.udplog_host
 948+ % udplog_sock->straddr()
 949+ % config.log_sample);
 950+ }
 951+
 952+}
 953+
 954+void
 955+whttp_shutdown(void)
 956+{
 957+ wfree(cache_hit_hdr);
 958+ wfree(cache_miss_hdr);
 959+}
 960+
 961+static string
 962+errsafe(string const &s)
 963+{
 964+string::const_iterator it = s.begin(), end = s.end();
 965+string res;
 966+ res.reserve((long) (s.size() * 1.2));
 967+ for (; it != end; ++it)
 968+ switch (*it) {
 969+ case '<':
 970+ res += "&lt;";
 971+ break;
 972+ case '>':
 973+ res += "&gt;";
 974+ break;
 975+ case '"':
 976+ res += "&quot;";
 977+ break;
 978+ case '\'':
 979+ res += "&apos;";
 980+ break;
 981+ default:
 982+ res += *it;
 983+ }
 984+ return res;
 985+}
 986+
 987+error_transform_filter::error_transform_filter(
 988+ string const &url,
 989+ string const &errdata,
 990+ string const &statstr,
 991+ int status)
 992+ : _url(url)
 993+ , _errdata(errdata)
 994+ , _statstr(statstr)
 995+ , _status(status) {
 996+}
 997+
 998+io::sink_result
 999+error_transform_filter::bf_transform(char const *buf, size_t len, ssize_t &discard)
 1000+{
 1001+string errtxt;
 1002+char const *p = buf;
 1003+ errtxt.reserve((int) (len * 1.2));
 1004+ while (p < buf + len) {
 1005+ switch(*p) {
 1006+ case '%':
 1007+ if (p + 1 < buf + len) {
 1008+ switch (*++p) {
 1009+ case 'A':
 1010+ errtxt += errsafe(config.admin);
 1011+ break;
 1012+ case 'U':
 1013+ errtxt += _url;
 1014+ break;
 1015+ case 'D':
 1016+ errtxt += current_time_str;
 1017+ break;
 1018+ case 'H':
 1019+ errtxt += my_hostname;
 1020+ break;
 1021+ case 'E':
 1022+ errtxt += errsafe(_errdata);
 1023+ break;
 1024+ case 'V':
 1025+ errtxt += my_version;
 1026+ break;
 1027+ case 'C': {
 1028+ char s[4];
 1029+ sprintf(s, "%d", _status);
 1030+ errtxt += s;
 1031+ break;
 1032+ }
 1033+ case 'S':
 1034+ errtxt += errsafe(_statstr);
 1035+ break;
 1036+ default:
 1037+ errtxt += *p;
 1038+ break;
 1039+ }
 1040+ p++;
 1041+ continue;
 1042+ }
 1043+ break;
 1044+ default:
 1045+ errtxt += *p;
 1046+ break;
 1047+ }
 1048+ ++p;
 1049+ }
 1050+char *r;
 1051+ r = new char[errtxt.size()];
 1052+ memcpy(r, errtxt.data(), errtxt.size());
 1053+ _buf.add(r, errtxt.size(), true);
 1054+ discard += len;
 1055+ return io::sink_result_okay;
 1056+}
 1057+
 1058+
 1059+void
 1060+httpcllr::send_error(int errnum, char const *errdata, int status, char const *statstr)
 1061+{
 1062+string url = "NONE";
 1063+ _response = status;
 1064+
 1065+ if (_request_path.size())
 1066+ url = errsafe(_request_path.c_str());
 1067+
 1068+ _error_headers = new header_spigot(status, statstr);
 1069+ if (!_client_sink)
 1070+ _client_sink = new io::socket_sink(_client_socket);
 1071+
 1072+ _error_headers->add("Date", current_time_str);
 1073+ _error_headers->add("Expires", current_time_str);
 1074+ _error_headers->add("Server", my_version);
 1075+
 1076+ _error_headers->add("Content-Type", "text/html;charset=UTF-8");
 1077+
 1078+ if (errnum != ERR_NONE) {
 1079+ _error_body = io::file_spigot::from_path(error_files[errnum], true);
 1080+ if (_error_body == NULL) {
 1081+ delete this;
 1082+ return;
 1083+ }
 1084+ _error_filter = new error_transform_filter(url, errdata, statstr, status);
 1085+ }
 1086+
 1087+ _error_headers->completed_callee(this, &httpcllr::error_send_headers_done);
 1088+ _error_headers->error_callee(this, &httpcllr::error_send_done);
 1089+
 1090+ /*
 1091+ * Can we chunk the error?
 1092+ */
 1093+ if (_error_body) {
 1094+ if (_header_parser->_http_vers != http11)
 1095+ _error_headers->add("Connection", "close");
 1096+ else
 1097+ _error_headers->add("Transfer-Encoding", "chunked");
 1098+ } else
 1099+ _error_headers->add("Content-Length", "0");
 1100+
 1101+ _error_headers->sp_connect(_client_sink);
 1102+ _error_headers->sp_uncork();
 1103+}
 1104+
 1105+void
 1106+httpcllr::error_send_headers_done(void)
 1107+{
 1108+ if (!_error_body) {
 1109+ error_send_done();
 1110+ return;
 1111+ }
 1112+
 1113+ _error_headers->sp_disconnect();
 1114+ _error_body->completed_callee(this, &httpcllr::error_send_done);
 1115+ _error_body->error_callee(this, &httpcllr::error_send_done);
 1116+
 1117+ _error_body->sp_connect(_error_filter);
 1118+ if (_header_parser->_http_vers == http11) {
 1119+ _chunking_filter = new chunking_filter;
 1120+ _error_filter->sp_connect(_chunking_filter);
 1121+ _chunking_filter->sp_connect(_client_sink);
 1122+ } else {
 1123+ _header_parser->_no_keepalive = true;
 1124+ _header_parser->_force_keepalive = false;
 1125+ _error_filter->sp_connect(_client_sink);
 1126+ }
 1127+
 1128+ _error_body->sp_uncork();
 1129+}
 1130+
 1131+void
 1132+httpcllr::error_send_done(void)
 1133+{
 1134+ log_request();
 1135+ end_request();
 1136+}
 1137+
 1138+void
 1139+httpcllr::log_request(void)
 1140+{
 1141+size_t size;
 1142+
 1143+ if (_header_parser->_http_reqtype == REQTYPE_INVALID)
 1144+ return;
 1145+
 1146+ if (++log_count != config.log_sample)
 1147+ return;
 1148+ log_count = 0;
 1149+
 1150+ if (_chunking_filter)
 1151+ size = _chunking_filter->_counter;
 1152+ else if (_dechunking_filter)
 1153+ size = _dechunking_filter->_counter;
 1154+ else
 1155+ size = _client_sink->_counter;
 1156+
 1157+ if (alf.is_open()) {
 1158+ string line;
 1159+ line = str(format("[%s] %s %s\"%s\" %d %d %s %s")
 1160+ % (char *)current_time_short
 1161+ % _client_socket->straddr(false)
 1162+ % request_string[_header_parser->_http_reqtype]
 1163+ % _request_path
 1164+ % size
 1165+ % _response
 1166+ % (_backend ? _backend->be_name : string("-"))
 1167+ % ((_cachedent && !_backend) ? "HIT" : "MISS"));
 1168+
 1169+ HOLDING(alf_lock);
 1170+
 1171+ if (!(alf << line << endl)) {
 1172+ wlog.error(format(
 1173+ "writing access log: %s; log will be closed")
 1174+ % strerror(errno));
 1175+ alf.close();
 1176+ }
 1177+ }
 1178+
 1179+ if (do_udplog) {
 1180+ char buf[65535];
 1181+ char *bufp = buf, *endp = buf + sizeof(buf);
 1182+ /*
 1183+ * The log format is a packed binary strucure laid out like this:
 1184+ *
 1185+ * <curtime><addrlen><straddr><reqtype><pathlen><reqpath><status>
 1186+ * <belen><bestr><cached><docsize>
 1187+ *
 1188+ * curtime is a 32-bit Unix timestamp. *len are the length in bytes
 1189+ * of the next element. straddr is the ASCII IP address of the client.
 1190+ * reqtype is an 8-bit integer:
 1191+ * 0 - GET
 1192+ * 1 - POST
 1193+ * 2 - HEAD
 1194+ * 3 - TRACE
 1195+ * 4 - OPTIONS
 1196+ * reqpath is the request path, including "http://" and the host.
 1197+ * status is a 16-bit HTTP status code for the response.
 1198+ * bestr is the ASCII IP address of the backend. cached is an
 1199+ * 8-bit value, 1 if the request was served from the cache and 0 if not.
 1200+ * docsize is the size of the response object, excluding headers.
 1201+ */
 1202+ ADD_UINT32(bufp, (uint32_t)time(NULL), endp);
 1203+ ADD_STRING(bufp, _client_socket->straddr(false), endp);
 1204+ ADD_UINT8(bufp, _header_parser->_http_reqtype, endp);
 1205+ ADD_STRING(bufp, _request_path, endp);
 1206+ ADD_UINT16(bufp, _response, endp);
 1207+ ADD_STRING(bufp, string(_backend ? _backend->be_name : "-"), endp);
 1208+ ADD_UINT8(bufp, (_cachedent && !_backend) ? 1 : 0, endp);
 1209+ ADD_UINT32(bufp, size, endp);
 1210+ udplog_sock->write(buf, bufp - buf);
 1211+ }
 1212+}
Index: trunk/willow/src/willow/confparse.cc
@@ -22,9 +22,9 @@
2323
2424 #include "confparse.h"
2525 #include "willow.h"
26 -#include "wlog.h"
27 -#include "wbackend.h"
28 -#include "wconfig.h"
 26+#include "log.h"
 27+#include "backend.h"
 28+#include "config.h"
2929 #include "format.h"
3030 #include "expr.h"
3131
Index: trunk/willow/src/willow/log.cc
@@ -0,0 +1,134 @@
 2+/* @(#) $Id: wlog.cc 17805 2006-11-20 14:07:17Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * log: logging.
 7+ */
 8+
 9+#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Id: wlog.cc 17805 2006-11-20 14:07:17Z river $"
 11+#endif
 12+
 13+#include <boost/format.hpp>
 14+
 15+#include <cstdio>
 16+#include <cstdarg>
 17+#include <cstdlib>
 18+#include <cstring>
 19+#include <syslog.h>
 20+#include <cerrno>
 21+#include <iostream>
 22+using std::cout;
 23+using std::fopen;
 24+using std::fprintf;
 25+using std::fclose;
 26+
 27+#include "autoconf.h"
 28+
 29+#include "log.h"
 30+#include "net.h"
 31+#include "config.h"
 32+#include "format.h"
 33+
 34+logger wlog;
 35+
 36+static const char *sev_names[] = {
 37+ "Debug",
 38+ "Notice",
 39+ "Warning",
 40+ "Error",
 41+};
 42+
 43+static const int syslog_pri[] = {
 44+ LOG_INFO,
 45+ LOG_INFO,
 46+ LOG_WARNING,
 47+ LOG_ERR,
 48+};
 49+
 50+bool
 51+logger::open(void)
 52+{
 53+ if (_syslog)
 54+ openlog("willow", LOG_PID, _facility);
 55+
 56+ if (_file.empty())
 57+ return true;
 58+
 59+ _fp = new ofstream(_file.c_str(), ios::app);
 60+ if (!_fp->is_open()) {
 61+ wlog.error(format("cannot open error log file %s: %s")
 62+ % _file % strerror(errno));
 63+ delete _fp;
 64+ _fp = NULL;
 65+ return false;
 66+ }
 67+
 68+ return true;
 69+}
 70+
 71+void
 72+logger::_log(log_level sev, string const &e)
 73+{
 74+string r;
 75+
 76+ if (sev < _level)
 77+ return;
 78+
 79+ r = str(format("%s| %s: %s") % current_time_short % sev_names[sev] % e);
 80+
 81+ HOLDING(_lock);
 82+ if (_syslog)
 83+ ::syslog(syslog_pri[sev], "%s", e.c_str());
 84+
 85+ if (_fp) {
 86+ if (!(*_fp << r << '\n')) {
 87+ _fp->close();
 88+ wlog.error(format("writing to logfile: %s")
 89+ % strerror(errno));
 90+ exit(8);
 91+ }
 92+ }
 93+
 94+ if (config.foreground)
 95+ cout << r << '\n';
 96+}
 97+
 98+void
 99+logger::close(void)
 100+{
 101+ HOLDING(_lock);
 102+
 103+ if (_fp)
 104+ _fp->close();
 105+
 106+ if (_syslog)
 107+ closelog();
 108+}
 109+
 110+void
 111+logger::syslog(bool do_, int facility)
 112+{
 113+ _syslog = do_;
 114+ _facility = facility;
 115+}
 116+
 117+bool
 118+logger::file(string const &file)
 119+{
 120+ _file = file;
 121+ return true;
 122+}
 123+
 124+void
 125+logger::level(log_level l)
 126+{
 127+ _level = l;
 128+}
 129+
 130+logger::logger(void)
 131+ : _syslog(false)
 132+ , _facility(0)
 133+ , _level(ll_notice)
 134+{
 135+}
Index: trunk/willow/src/include/wlog.h
@@ -1,71 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wlog: logging.
7 - */
8 -
9 -#ifndef WLOG_H
10 -#define WLOG_H
11 -
12 -#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
13 -# pragma ident "@(#)$Id$"
14 -#endif
15 -
16 -#include <cstdio>
17 -#include <string>
18 -#include <fstream>
19 -using std::ofstream;
20 -
21 -#include "willow.h"
22 -#include "config.h"
23 -#include "ptalloc.h"
24 -
25 -enum log_level {
26 - ll_debug,
27 - ll_notice,
28 - ll_warn,
29 - ll_error
30 -};
31 -
32 -struct logger : noncopyable {
33 - logger();
34 -
35 - void syslog (bool, int facility = 0);
36 - void level (log_level);
37 - bool file (string const &fname);
38 -
39 - bool open(void);
40 - void close(void);
41 -
42 - void debug (format const &f) { _log(ll_debug, str(f)); }
43 - void notice(format const &f) { _log(ll_notice, str(f));}
44 - void warn (format const &f) { _log(ll_warn, str(f)); }
45 - void error (format const &f) { _log(ll_error, str(f)); }
46 -
47 - void debug (string const &s) { _log(ll_debug, s); }
48 - void notice(string const &s) { _log(ll_notice, s); }
49 - void warn (string const &s) { _log(ll_warn, s); }
50 - void error (string const &s) { _log(ll_error, s); }
51 -
52 -private:
53 - bool _syslog;
54 - int _facility;
55 - log_level _level;
56 - string _file;
57 -
58 - ofstream *_fp;
59 - lockable _lock;
60 -
61 - void _log(log_level l, string const &);
62 -};
63 -
64 -extern logger wlog;
65 -
66 -#ifndef WILLOW_DEBUG
67 -# define WDEBUG(x) ((void)0)
68 -#else
69 -# define WDEBUG(x) wlog.debug(x)
70 -#endif
71 -
72 -#endif
Index: trunk/willow/src/include/wnet.h
@@ -1,357 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wnet: Networking.
7 - */
8 -
9 -#ifndef WNET_H
10 -#define WNET_H
11 -
12 -#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
13 -# pragma ident "@(#)$Id$"
14 -#endif
15 -
16 -#if defined __digital__ && defined __unix__
17 -/* sendfile prototype is missing on Tru64 UNIX */
18 -# include <sys/uio.h>
19 -
20 -ssize_t sendfile(int, int, off_t, size_t, const struct iovec *, int);
21 -#endif
22 -
23 -#include <sys/types.h>
24 -#include <sys/socket.h>
25 -
26 -#include <netinet/in.h>
27 -#include <netdb.h>
28 -#include <unistd.h>
29 -
30 -#include "config.h"
31 -#include <sys/time.h>
32 -#include <sys/fcntl.h>
33 -#include <sys/sendfile.h>
34 -
35 -/*
36 - * libevent needs these
37 - */
38 -#ifndef HAVE_U_INT8_T
39 -typedef uint8_t u_int8_t;
40 -#endif
41 -
42 -#ifndef HAVE_U_INT16_T
43 -typedef uint16_t u_int16_t;
44 -#endif
45 -
46 -#ifndef HAVE_U_INT32_T
47 -typedef uint32_t u_int32_t;
48 -#endif
49 -
50 -#ifndef HAVE_U_INT64_T
51 -typedef uint64_t u_int64_t;
52 -#endif
53 -
54 -#include <event.h>
55 -#include <pthread.h>
56 -#include <vector>
57 -#include <deque>
58 -#include <cassert>
59 -#include <stdexcept>
60 -#include <cerrno>
61 -#include <utility>
62 -using std::deque;
63 -using std::vector;
64 -using std::runtime_error;
65 -using std::pair;
66 -using std::make_pair;
67 -using std::memcpy;
68 -
69 -#include "willow.h"
70 -#include "polycaller.h"
71 -
72 -extern bool wnet_exit;
73 -
74 -namespace wnet {
75 - struct addrlist;
76 - struct address;
77 - struct socket;
78 - typedef socket wsocket;
79 -}
80 -
81 -enum sprio {
82 - prio_stats = 0,
83 - prio_backend,
84 - prio_norm,
85 - prio_accept,
86 - prio_max
87 -};
88 -
89 -#define FDE_READ 0x1
90 -#define FDE_WRITE 0x2
91 -
92 -extern struct ioloop_t {
93 - ioloop_t();
94 -
95 - void prepare (void);
96 - void run (void);
97 -
98 - void _accept (wnet::socket *, int);
99 -} *ioloop;
100 -
101 -/*
102 - * For working with packed UDP data.
103 - */
104 -#define HAS_SPACE(b,l,endp) (((b) + (l)) < endp)
105 -#define ADD_UINT32(b,i,endp) if (HAS_SPACE(b,4,endp)) { *(uint32_t*)b = i; b += 4; }
106 -#define ADD_UINT16(b,i,endp) if (HAS_SPACE(b,2,endp)) { *(uint16_t*)b = i; b += 2; }
107 -#define ADD_UINT8(b,i,endp) if (HAS_SPACE(b,1,endp)) { *(uint8_t*)b = i; b += 1; }
108 -#define ADD_STRING(b,s,endp) do { uint16_t len = s.size(); \
109 - if (HAS_SPACE(b,2 + len,endp)) { \
110 - ADD_UINT16(b,len,endp); \
111 - memcpy(b, s.data(), len); \
112 - b += len; \
113 - } \
114 - } while (0)
115 -
116 -namespace wnet {
117 -
118 -struct buffer;
119 -
120 -struct buffer_item : freelist_allocator<buffer_item> {
121 - buffer_item(char const *buf_, size_t len_, bool free_)
122 - : buf(buf_)
123 - , len(len_)
124 - , off(0)
125 - , free(free_) {
126 - }
127 - buffer_item(buffer_item const &o)
128 - : buf(NULL)
129 - , len(o.len)
130 - , off(o.off)
131 - , free(true) {
132 - buf = (const char *)memcpy(new char[len], o.buf, len);
133 - }
134 - buffer_item&
135 - operator=(buffer_item const &other) {
136 - buf = NULL;
137 - len = other.len;
138 - off = other.off;
139 - free = true;
140 - buf = (const char *)memcpy(new char[len], other.buf, len);
141 - return *this;
142 - }
143 -
144 - ~buffer_item() {
145 - if (free)
146 - delete[] buf;
147 - }
148 -
149 - char const *buf;
150 - size_t len;
151 - size_t off;
152 - bool free;
153 -};
154 -
155 -struct buffer {
156 - void add(char const *buf, size_t len, bool free) {
157 - items.push_back(buffer_item(buf, len, free));
158 - }
159 -
160 - void reset(void) {
161 - items.clear();
162 - }
163 -
164 - deque<buffer_item, pt_allocator<buffer_item> > items;
165 -};
166 -
167 -} // namespace wnet
168 -
169 -extern lockable acceptq_lock;
170 -extern tss<event_base> evb;
171 -
172 -struct client_data {
173 - sockaddr_storage cdat_addr;
174 - socklen_t cdat_addrlen;
175 -};
176 -
177 -extern char current_time_str[];
178 -extern char current_time_short[];
179 -extern time_t current_time;
180 -
181 - void wnet_add_accept_wakeup (wnet::socket *);
182 - void wnet_set_time (void);
183 - void make_event_base (void);
184 -
185 -namespace wnet { /* things above should move here eventually */
186 -
187 -struct socket_error : runtime_error {
188 - int _err;
189 - int err(void) const {
190 - return _err;
191 - }
192 - socket_error(int i) : runtime_error(strerror(i)), _err(i) {}
193 - socket_error(char const *s) : runtime_error(s), _err(0) {}
194 - socket_error() : runtime_error(strerror(errno)), _err(errno) {}
195 -};
196 -
197 -struct resolution_error : socket_error {
198 - resolution_error(int i) : socket_error(gai_strerror(i)) {}
199 -};
200 -
201 -enum connect_status {
202 - connect_okay,
203 - connect_later
204 -};
205 -
206 -enum socktype {
207 - st_stream = SOCK_STREAM,
208 - st_dgram = SOCK_DGRAM
209 -};
210 -
211 -struct address : freelist_allocator<address> {
212 - address(void);
213 - address(const address &o);
214 - address(sockaddr *, socklen_t);
215 -
216 - address& operator= (const address &o);
217 -
218 - string const &straddr(bool lng = true) const;
219 -
220 - socket *makesocket (char const *, sprio) const;
221 - int length (void) const { return _addrlen; }
222 - sockaddr *addr (void) const { return (sockaddr *)&_addr; }
223 - int family (void) const { return _fam; }
224 - int socktype(void) const { return _stype; }
225 - int protocol(void) const { return _prot; }
226 -
227 - static address from_ifname(int s, string const &ifname);
228 - static u_int ifname_to_index(string const &ifname);
229 -
230 -private:
231 - friend struct addrlist;
232 - address(addrinfo *ai);
233 -
234 - sockaddr_storage _addr;
235 - socklen_t _addrlen;
236 - int _fam, _stype, _prot;
237 - mutable string _straddr, _shortaddr;
238 -};
239 -
240 -struct addrlist : freelist_allocator<addrlist> {
241 - typedef address value_type;
242 - typedef vector<value_type>::const_iterator
243 - iterator, const_iterator;
244 -
245 - ~addrlist();
246 -
247 - iterator begin (void) const;
248 - iterator end (void) const;
249 - socket *makesocket (char const *, sprio) const;
250 - static addrlist *resolve (string const &,
251 - string const &,
252 - enum socktype, int = AF_UNSPEC);
253 - static addrlist *resolve (string const &, int ,
254 - enum socktype, int = AF_UNSPEC);
255 -
256 - static address first (string const &,
257 - string const &,
258 - enum socktype, int = AF_UNSPEC);
259 - static address first (string const &, int,
260 - enum socktype, int = AF_UNSPEC);
261 -
262 -private:
263 - addrlist() {};
264 -
265 - vector<value_type> _addrs;
266 -};
267 -
268 -struct socket : noncopyable, freelist_allocator<socket> {
269 - ~socket();
270 -
271 - static socket *create(string const &addr, int port,
272 - enum socktype, char const *, sprio,
273 - int = AF_UNSPEC);
274 - static socket *create(string const &addr, string const &port,
275 - enum socktype, char const *, sprio,
276 - int = AF_UNSPEC);
277 - static pair<socket *, socket *> socketpair(enum socktype);
278 -
279 - socket *accept (char const *, sprio);
280 - connect_status connect (void);
281 - int read (char *buf, size_t count) {
282 - return ::read(_s, buf, count);
283 - }
284 - int recvfrom (char *, size_t, wnet::address &);
285 - int sendto (char const *, size_t, wnet::address const &);
286 - int write (char const *buf, size_t count) {
287 - return ::write(_s, buf, count);
288 - }
289 - int sendfile (int to, off_t *off, size_t n) {
290 - return ::sendfile(_s, to, off, n);
291 - }
292 -
293 - void nonblocking (bool);
294 - void reuseaddr (bool);
295 - void bind (void);
296 - void listen (int bl = 25);
297 - int getopt (int, int, void *, socklen_t *) const;
298 - int setopt (int, int, void *, socklen_t);
299 - void cork (void);
300 - void uncork (void);
301 - int error (void) const;
302 - char const *description (void) const;
303 -
304 - void mcast_join (string const &ifname);
305 - void mcast_leave (string const &ifname);
306 -
307 - wnet::address const &address (void) const {
308 - return _addr;
309 - }
310 - string const &straddr (bool lng = true) const {
311 - return _addr.straddr(lng);
312 - }
313 -
314 - template<typename T>
315 - void readback (polycaller<wnet::socket *, T> cb, T ud);
316 -
317 - template<typename T>
318 - void writeback (polycaller<wnet::socket *, T> cb, T ud);
319 -
320 - void clearbacks (void);
321 -
322 -protected:
323 - friend struct wnet::address;
324 - friend struct ::ioloop_t;
325 -
326 - explicit socket (int, wnet::address const &, char const *, sprio);
327 - explicit socket (wnet::address const &, char const *, sprio);
328 -
329 - void _register (int, polycallback<wsocket *>);
330 - static void _ev_callback (int fd, short ev, void *d);
331 -
332 - polycallback<wsocket *> _read_handler, _write_handler;
333 -
334 - int _s;
335 - wnet::address _addr;
336 - char const *_desc;
337 - sprio _prio;
338 - event ev;
339 -};
340 -
341 - vector<addrinfo> nametoaddrs(string const &name, int port);
342 - string reserror(int);
343 -
344 -template<typename T>
345 -void
346 -socket::readback (polycaller<wnet::socket *, T> cb, T ud) {
347 - _register(FDE_READ, polycallback<wnet::socket *>(cb, ud));
348 -}
349 -
350 -template<typename T>
351 -void
352 -socket::writeback (polycaller<wnet::socket *, T> cb, T ud) {
353 - _register(FDE_WRITE, polycallback<wnet::socket *>(cb, ud));
354 -}
355 -
356 -} // namespace wnet
357 -
358 -#endif
Index: trunk/willow/src/include/wbackend.h
@@ -1,140 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wbackend: HTTP backend handling.
7 - */
8 -
9 -#ifndef WBACKEND_H
10 -#define WBACKEND_H
11 -
12 -#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
13 -# pragma ident "@(#)$Id$"
14 -#endif
15 -
16 -#include <sys/types.h>
17 -#include <netinet/in.h>
18 -
19 -#include <string>
20 -#include <vector>
21 -#include <map>
22 -using std::vector;
23 -using std::map;
24 -
25 -#include "polycaller.h"
26 -#include "wnet.h"
27 -using namespace wnet;
28 -
29 -enum lb_type {
30 - lb_rr,
31 - lb_carp,
32 - lb_carp_hostonly
33 -};
34 -
35 -struct backend_list;
36 -struct backend_pool;
37 -struct backend_cb_data;
38 -
39 -struct backend : freelist_allocator<backend> {
40 - backend(string const &, address const &);
41 -
42 - string be_name; /* IP as specified in config */
43 - int be_group; /* group */
44 - int be_port; /* port number */
45 - string be_straddr; /* formatted address */
46 - address be_addr; /* address */
47 - int be_dead; /* 0 if okay, 1 if unavailable */
48 - time_t be_time; /* If dead, time to retry */
49 - uint32_t be_hash; /* constant carp "host" hash */
50 - uint32_t be_carp; /* carp hash for the last url */
51 - float be_load; /* carp load factor */
52 - float be_carplfm; /* carp LFM after calculation */
53 -
54 - template<typename stringT>
55 - static uint32_t _carp_hosthash(stringT const &str);
56 -};
57 -
58 -struct backend_list : freelist_allocator<backend_list> {
59 - backend_list( backend_pool const &pool,
60 - imstring const &url,
61 - imstring const &host,
62 - int failgroup,
63 - lb_type, int start);
64 -
65 - int _get_impl (polycallback<backend *, wsocket *>);
66 - void _backend_read (wsocket *e, backend_cb_data *);
67 - struct backend *_next_backend (void);
68 - void _carp_recalc (imstring const &, imstring const &, lb_type);
69 - static int _becarp_cmp (backend const *a, backend const *b);
70 - bool failed (void) const {
71 - return _delegate;
72 - }
73 -
74 - template<typename stringT>
75 - static uint32_t _carp_urlhash(stringT const &str) {
76 - uint32_t h = 0;
77 - for (typename stringT::const_iterator it = str.begin(), end = str.end();
78 - it != end; ++it)
79 - h += rotl(h, 19) + *it;
80 - return h;
81 - }
82 -
83 -
84 - template<typename T>
85 - int get (polycaller<backend *, wsocket *, T> cb, T t) {
86 - return _get_impl(polycallback<backend *, wsocket *>(cb, t));
87 - }
88 -
89 - ~backend_list() {
90 - if (_delegate)
91 - delete _delegate;
92 - }
93 -
94 -private:
95 - vector<backend *, pt_allocator<backend *> > backends;
96 - size_t _cur;
97 - int _failgroup;
98 - struct backend_list *_delegate;
99 -};
100 -
101 -template<typename stringT>
102 -uint32_t
103 -backend::_carp_hosthash(stringT const &str)
104 -{
105 - uint32_t h = backend_list::_carp_urlhash(str) * 0x62531965;
106 - return rotl(h, 21);
107 -}
108 -
109 -struct backend_pool {
110 - backend_pool(string const &name, lb_type, int failgroup = -1);
111 - ~backend_pool();
112 -
113 - void add (string const &, int, int);
114 - backend_list *get_list (imstring const & url, imstring const &host);
115 -
116 - int size (void) const;
117 - string const &name (void) const;
118 -
119 - void add_keptalive (pair<wsocket *, backend *>);
120 - pair<wsocket *, backend *>
121 - get_keptalive (void);
122 -
123 -private:
124 - friend class backend_list;
125 -
126 - void _carp_calc (void);
127 -
128 - vector<backend *, pt_allocator<backend *> > backends;
129 - tss<vector<pair<wsocket *, backend *> > > _keptalive;
130 - tss<size_t> _cur;
131 - lb_type _lbtype;
132 - string _name;
133 - int _failgroup;
134 -};
135 -
136 -extern map<int, backend_pool> bpools;
137 -extern map<imstring, int> host_to_bpool;
138 -extern map<string, int> poolnames;
139 -extern int nbpools;
140 -
141 -#endif
Index: trunk/willow/src/include/whttp_header.h
@@ -1,247 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * whttp_header: header processing implementation.
7 - */
8 -
9 -#ifndef WHTTP_HEADER
10 -#define WHTTP_HEADER
11 -
12 -#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
13 -# pragma ident "@(#)$Id$"
14 -#endif
15 -
16 -#include "config.h"
17 -#include "whttp.h"
18 -#include "willow.h"
19 -#include "wnet.h"
20 -#include "flowio.h"
21 -
22 -/*
23 - * Do NOT change these values, they are used in the UDP
24 - * log packets.
25 - */
26 -#define REQTYPE_GET 0
27 -#define REQTYPE_POST 1
28 -#define REQTYPE_HEAD 2
29 -#define REQTYPE_TRACE 3
30 -#define REQTYPE_OPTIONS 4
31 -#define REQTYPE_PURGE 5
32 -#define REQTYPE_INVALID -1
33 -
34 -#define HDR_BUFSZ 256
35 -
36 -extern struct request_type {
37 - const char *name;
38 - int len;
39 - int type;
40 -} supported_reqtypes[];
41 -
42 -/*
43 - * A single header. Usually stored inside a header_list. For speed,
44 - * we assume the length of one header line is no more than HDX_BUFSZ-1
45 - * characters, and pre-allocate a buffer of that size. In case the
46 - * header is longer, hr_buffer is ignored, and a new buffer is allocated
47 - * from the pt_allocator. hr_free==true indicated that the buffer should
48 - * be freed on destruction.
49 - */
50 -struct header : freelist_allocator<header> {
51 - /*
52 - * Construct a new header from the given name and value, which need
53 - * not be null terminated.
54 - */
55 - header( char const *name, size_t namelen,
56 - char const *value, size_t valuelen);
57 -
58 - ~header();
59 - header(header const &);
60 - header& operator= (header const &);
61 -
62 - /*
63 - * Assign new values to this header.
64 - */
65 - void assign( char const *name, size_t namelen,
66 - char const *value, size_t valuelen);
67 -
68 - /*
69 - * Destructively assign the contents of other to *this. The state
70 - * of other is undefined until assign() or ~header is called.
71 - */
72 - void move (header &other);
73 -
74 - /* return the header's name */
75 - char const *name (void) const { return hr_name; }
76 - /* return the header's value */
77 - char const *value (void) const { return hr_value; }
78 -
79 -private:
80 - friend struct header_list;
81 -
82 - char hr_buffer[HDR_BUFSZ];
83 - char *hr_name; /* name, == hr_buffer or else alloc'd data */
84 - char *hr_value; /* value */
85 - size_t hr_allocd; /* size of buffer alloced if any */
86 - static pt_allocator<char> alloc;
87 -};
88 -
89 -/*
90 - * A list of headers found in a request.
91 - */
92 -struct header_list {
93 - /* Construct an empty header list. */
94 - header_list();
95 - ~header_list();
96 - header_list &operator= (header_list const &other);
97 -
98 - /*
99 - * Add a new (header,value) pair to the list. name and value must be
100 - * nul-terminated.
101 - */
102 - void add (char const *name, char const *value);
103 -
104 - /*
105 - * As above, but nul terminated is not required.
106 - */
107 - void add (char const *name, size_t namelen,
108 - char const *value, size_t valuelen);
109 -
110 - /*
111 - * Append ", app" to the end of the previous header. app need not
112 - * be terminated.
113 - */
114 - void append_last (const char *app, size_t applen);
115 -
116 - /*
117 - * Remove the named header from the list.
118 - */
119 - void remove (const char *);
120 -
121 - /*
122 - * Return a string (allocated with new char[]) containing the request
123 - * headers in a form suitable for use in an HTTP request or response.
124 - * The caller is expected to delete the returned value.
125 - */
126 - char *build (void);
127 -
128 - /*
129 - * Return the length of the string build() would return.
130 - */
131 - int length (void) const {
132 - return hl_len;
133 - }
134 -
135 - /*
136 - * Find a specific header in the list. Returns NULL if no such header
137 - * exists.
138 - */
139 -struct header *find (const char *name);
140 -
141 -private:
142 - friend struct header_spigot;
143 - friend struct header_parser;
144 -
145 - header *hl_last;
146 - vector<header *, pt_allocator<header *> > hl_hdrs;
147 - int hl_len;
148 -};
149 -
150 -struct qvalue {
151 - float val;
152 -const char *name;
153 -
154 - bool operator< (qvalue const &rhs) const {
155 - return val < rhs.val;
156 - }
157 -};
158 -
159 -/*
160 - * header_spigot: output an HTTP response built from a pre-defined list
161 - * of headers. used for responses generated by us.
162 - */
163 -struct header_spigot : io::spigot
164 -{
165 - header_spigot(int, char const *);
166 -
167 - void add (char const *, char const *);
168 - void body (string const &);
169 - void body (string &);
170 - void sp_uncork (void);
171 - void sp_cork (void);
172 -
173 -private:
174 - header_list _headers;
175 - bool _built;
176 - string _first;
177 - string _body;
178 - wnet::buffer _buf;
179 - bool _corked;
180 -};
181 -
182 -/*
183 - * header_parser: parse and store headers. This is both a sink (for
184 - * reading headers and parsing them) and a spigot (for sending headers
185 - * to a backend or client).
186 - */
187 -struct header_parser : io::sink, io::spigot
188 -{
189 - header_parser()
190 - : _got_reqtype(false)
191 - , _built(false)
192 - , _corked(true)
193 - , _is_response(false)
194 - , _content_length(-1)
195 - , _response(0)
196 - , _is_msie(false)
197 - , _http_reqtype(REQTYPE_INVALID)
198 - , _no_keepalive(false)
199 - , _force_keepalive(false)
200 - , _follow_redirect(false)
201 - , _eof(false)
202 - {
203 - _flags.f_chunked = 0;
204 - }
205 -
206 - io::sink_result data_ready (char const *buf, size_t len, ssize_t&);
207 - io::sink_result data_empty (void);
208 -
209 - int parse_reqtype (char const *buf, char const *endp);
210 - int parse_response (char const *buf, char const *endp);
211 - void completed_callback (void);
212 - void error_callback (void);
213 - void sp_cork (void);
214 - void sp_uncork (void);
215 - void set_response (void);
216 - void sending_restart (void);
217 -
218 - polycaller<> _completed_callee;
219 - polycaller<> _error_callee;
220 - header_list _headers;
221 - bool _got_reqtype;
222 - http_version _http_vers;
223 - imstring _http_path;
224 - bool _built;
225 - bool _corked;
226 - bool _is_response;
227 - ssize_t _content_length;
228 - int _response;
229 - bool _is_msie;
230 - int _http_reqtype;
231 - imstring _http_host;
232 - string _http_backend;
233 - bool _no_keepalive;
234 - bool _force_keepalive;
235 - bool _follow_redirect;
236 - imstring _location;
237 -
238 - bool _eof;
239 - wnet::buffer _buf;
240 -
241 - struct {
242 - unsigned int f_chunked:1;
243 - } _flags;
244 -};
245 -
246 -void whttp_header_init(void);
247 -
248 -#endif
Index: trunk/willow/src/include/wconfig.h
@@ -1,99 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * wconfig: configuration.
7 - */
8 -
9 -#ifndef WCONFIG_H
10 -#define WCONFIG_H
11 -
12 -#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
13 -# pragma ident "@(#)$Id$"
14 -#endif
15 -
16 -#include <sys/types.h>
17 -#include <netinet/in.h>
18 -
19 -#include <string>
20 -#include <vector>
21 -#include <utility>
22 -#include <map>
23 -using std::pair;
24 -using std::vector;
25 -using std::map;
26 -
27 -#include "wnet.h"
28 -using namespace wnet;
29 -
30 -struct listener {
31 - string name;
32 - string host;
33 - int port;
34 - int group;
35 - wnet::socket *sock;
36 - atomic<uint64_t> nconns;
37 -
38 - ~listener() {
39 - delete sock;
40 - }
41 -};
42 -extern vector<listener *> listeners;
43 -extern map<wsocket *, listener *> sock2lsn;
44 -
45 -struct cachedir {
46 - cachedir(string const &dir_)
47 - : dir(dir_)
48 - {}
49 -
50 - string dir;
51 -};
52 -
53 -#define DEFAULT_STATS_INTERVAL 300
54 -#define DEFAULT_STATS_PORT "4446"
55 -#define DEFAULT_HTCP_PORT "4827"
56 -
57 -struct radix;
58 -extern struct configuration : noncopyable {
59 - string admin;
60 - int foreground;
61 - int nthreads;
62 - int log_sample;
63 - string access_log;
64 - bool udp_log;
65 - string udplog_host;
66 - int udplog_port;
67 - string suid, sgid;
68 - bool compress;
69 - int complevel;
70 - time_t backend_retry;
71 - bool cache_private;
72 - bool msie_hack;
73 - string default_host;
74 - access_list access;
75 - access_list force_backend;
76 - bool udp_stats;
77 - bool backend_keepalive;
78 - bool client_keepalive;
79 - int keepalive_max;
80 - bool x_follow;
81 - int max_redirects;
82 - bool use_dio;
83 - long cache_memory;
84 - long max_entity_size;
85 - string cache_master;
86 - bool htcp_sigrequired;
87 -
88 - vector<cachedir> cachedirs;
89 - vector<pair<string, string> > stats_hosts;
90 - vector<pair<string, string> > htcp_hosts;
91 - vector<pair<string, string> > htcp_maddrs;
92 - map<string, ustring> htcp_keys;
93 -} config;
94 -
95 -void wconfig_init(char const *);
96 -
97 -int add_listener(string const &, int);
98 -int add_cachedir(string const &, int);
99 -
100 -#endif
Index: trunk/willow/src/include/whttp.h
@@ -1,36 +0,0 @@
2 -/* @(#) $Id$ */
3 -/* This source code is in the public domain. */
4 -/*
5 - * Willow: Lightweight HTTP reverse-proxy.
6 - * whttp: HTTP implementation.
7 - */
8 -
9 -#ifndef WHTTP_H
10 -#define WHTTP_H
11 -
12 -#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
13 -# pragma ident "@(#)$Id$"
14 -#endif
15 -
16 -static const int whttp_deny_connect = 0x1;
17 -
18 -struct fde;
19 -struct event_base;
20 -
21 -void http_new(struct fde *);
22 -void whttp_init(void);
23 -void whttp_shutdown(void);
24 -void whttp_reconfigure(void);
25 -
26 -extern const char *request_string[];
27 -extern char my_hostname[];
28 -extern char *cache_miss_hdr;
29 -extern char *cache_hit_hdr;
30 -extern char via_hdr[];
31 -
32 -enum http_version {
33 - http10,
34 - http11
35 -};
36 -
37 -#endif
Index: trunk/willow/src/include/config.h
@@ -0,0 +1,99 @@
 2+/* @(#) $Id: wconfig.h 17765 2006-11-17 19:12:23Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wconfig: configuration.
 7+ */
 8+
 9+#ifndef WCONFIG_H
 10+#define WCONFIG_H
 11+
 12+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 13+# pragma ident "@(#)$Id: wconfig.h 17765 2006-11-17 19:12:23Z river $"
 14+#endif
 15+
 16+#include <sys/types.h>
 17+#include <netinet/in.h>
 18+
 19+#include <string>
 20+#include <vector>
 21+#include <utility>
 22+#include <map>
 23+using std::pair;
 24+using std::vector;
 25+using std::map;
 26+
 27+#include "net.h"
 28+using namespace wnet;
 29+
 30+struct listener {
 31+ string name;
 32+ string host;
 33+ int port;
 34+ int group;
 35+ wnet::socket *sock;
 36+ atomic<uint64_t> nconns;
 37+
 38+ ~listener() {
 39+ delete sock;
 40+ }
 41+};
 42+extern vector<listener *> listeners;
 43+extern map<wsocket *, listener *> sock2lsn;
 44+
 45+struct cachedir {
 46+ cachedir(string const &dir_)
 47+ : dir(dir_)
 48+ {}
 49+
 50+ string dir;
 51+};
 52+
 53+#define DEFAULT_STATS_INTERVAL 300
 54+#define DEFAULT_STATS_PORT "4446"
 55+#define DEFAULT_HTCP_PORT "4827"
 56+
 57+struct radix;
 58+extern struct configuration : noncopyable {
 59+ string admin;
 60+ int foreground;
 61+ int nthreads;
 62+ int log_sample;
 63+ string access_log;
 64+ bool udp_log;
 65+ string udplog_host;
 66+ int udplog_port;
 67+ string suid, sgid;
 68+ bool compress;
 69+ int complevel;
 70+ time_t backend_retry;
 71+ bool cache_private;
 72+ bool msie_hack;
 73+ string default_host;
 74+ access_list access;
 75+ access_list force_backend;
 76+ bool udp_stats;
 77+ bool backend_keepalive;
 78+ bool client_keepalive;
 79+ int keepalive_max;
 80+ bool x_follow;
 81+ int max_redirects;
 82+ bool use_dio;
 83+ long cache_memory;
 84+ long max_entity_size;
 85+ string cache_master;
 86+ bool htcp_sigrequired;
 87+
 88+ vector<cachedir> cachedirs;
 89+ vector<pair<string, string> > stats_hosts;
 90+ vector<pair<string, string> > htcp_hosts;
 91+ vector<pair<string, string> > htcp_maddrs;
 92+ map<string, ustring> htcp_keys;
 93+} config;
 94+
 95+void wconfig_init(char const *);
 96+
 97+int add_listener(string const &, int);
 98+int add_cachedir(string const &, int);
 99+
 100+#endif
Index: trunk/willow/src/include/flalloc.h
@@ -15,7 +15,7 @@
1616 #include <cstdlib>
1717 using std::memset;
1818
19 -#include "wthread.h"
 19+#include "thread.h"
2020
2121 #ifndef __SUNPRO_CC
2222 template<typename T>
Index: trunk/willow/src/include/willow.h
@@ -12,7 +12,7 @@
1313 # pragma ident "@(#)$Id$"
1414 #endif
1515
16 -#include "config.h"
 16+#include "autoconf.h"
1717
1818 #include <sstream>
1919 #include <cstddef>
@@ -30,11 +30,11 @@
3131 using std::basic_ostream;
3232 using std::istream;
3333
34 -#include "wlog.h"
 34+#include "log.h"
3535 #include "radix.h"
3636 #include "ptalloc.h"
3737 #include "format.h"
38 -#include "wconfig.h"
 38+#include "config.h"
3939
4040 typedef unsigned long long w_size_t;
4141
Index: trunk/willow/src/include/http.h
@@ -0,0 +1,36 @@
 2+/* @(#) $Id: whttp.h 17692 2006-11-15 05:12:33Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * whttp: HTTP implementation.
 7+ */
 8+
 9+#ifndef WHTTP_H
 10+#define WHTTP_H
 11+
 12+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 13+# pragma ident "@(#)$Id: whttp.h 17692 2006-11-15 05:12:33Z river $"
 14+#endif
 15+
 16+static const int whttp_deny_connect = 0x1;
 17+
 18+struct fde;
 19+struct event_base;
 20+
 21+void http_new(struct fde *);
 22+void whttp_init(void);
 23+void whttp_shutdown(void);
 24+void whttp_reconfigure(void);
 25+
 26+extern const char *request_string[];
 27+extern char my_hostname[];
 28+extern char *cache_miss_hdr;
 29+extern char *cache_hit_hdr;
 30+extern char via_hdr[];
 31+
 32+enum http_version {
 33+ http10,
 34+ http11
 35+};
 36+
 37+#endif
Index: trunk/willow/src/include/log.h
@@ -0,0 +1,71 @@
 2+/* @(#) $Id: wlog.h 17869 2006-11-23 01:05:44Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * log: logging.
 7+ */
 8+
 9+#ifndef WLOG_H
 10+#define WLOG_H
 11+
 12+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 13+# pragma ident "@(#)$Id: wlog.h 17869 2006-11-23 01:05:44Z river $"
 14+#endif
 15+
 16+#include <cstdio>
 17+#include <string>
 18+#include <fstream>
 19+using std::ofstream;
 20+
 21+#include "willow.h"
 22+#include "autoconf.h"
 23+#include "ptalloc.h"
 24+
 25+enum log_level {
 26+ ll_debug,
 27+ ll_notice,
 28+ ll_warn,
 29+ ll_error
 30+};
 31+
 32+struct logger : noncopyable {
 33+ logger();
 34+
 35+ void syslog (bool, int facility = 0);
 36+ void level (log_level);
 37+ bool file (string const &fname);
 38+
 39+ bool open(void);
 40+ void close(void);
 41+
 42+ void debug (format const &f) { _log(ll_debug, str(f)); }
 43+ void notice(format const &f) { _log(ll_notice, str(f));}
 44+ void warn (format const &f) { _log(ll_warn, str(f)); }
 45+ void error (format const &f) { _log(ll_error, str(f)); }
 46+
 47+ void debug (string const &s) { _log(ll_debug, s); }
 48+ void notice(string const &s) { _log(ll_notice, s); }
 49+ void warn (string const &s) { _log(ll_warn, s); }
 50+ void error (string const &s) { _log(ll_error, s); }
 51+
 52+private:
 53+ bool _syslog;
 54+ int _facility;
 55+ log_level _level;
 56+ string _file;
 57+
 58+ ofstream *_fp;
 59+ lockable _lock;
 60+
 61+ void _log(log_level l, string const &);
 62+};
 63+
 64+extern logger wlog;
 65+
 66+#ifndef WILLOW_DEBUG
 67+# define WDEBUG(x) ((void)0)
 68+#else
 69+# define WDEBUG(x) wlog.debug(x)
 70+#endif
 71+
 72+#endif
Index: trunk/willow/src/include/net.h
@@ -0,0 +1,357 @@
 2+/* @(#) $Id: wnet.h 17869 2006-11-23 01:05:44Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * net: Networking.
 7+ */
 8+
 9+#ifndef WNET_H
 10+#define WNET_H
 11+
 12+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 13+# pragma ident "@(#)$Id: wnet.h 17869 2006-11-23 01:05:44Z river $"
 14+#endif
 15+
 16+#if defined __digital__ && defined __unix__
 17+/* sendfile prototype is missing on Tru64 UNIX */
 18+# include <sys/uio.h>
 19+
 20+ssize_t sendfile(int, int, off_t, size_t, const struct iovec *, int);
 21+#endif
 22+
 23+#include <sys/types.h>
 24+#include <sys/socket.h>
 25+
 26+#include <netinet/in.h>
 27+#include <netdb.h>
 28+#include <unistd.h>
 29+
 30+#include "autoconf.h"
 31+#include <sys/time.h>
 32+#include <sys/fcntl.h>
 33+#include <sys/sendfile.h>
 34+
 35+/*
 36+ * libevent needs these
 37+ */
 38+#ifndef HAVE_U_INT8_T
 39+typedef uint8_t u_int8_t;
 40+#endif
 41+
 42+#ifndef HAVE_U_INT16_T
 43+typedef uint16_t u_int16_t;
 44+#endif
 45+
 46+#ifndef HAVE_U_INT32_T
 47+typedef uint32_t u_int32_t;
 48+#endif
 49+
 50+#ifndef HAVE_U_INT64_T
 51+typedef uint64_t u_int64_t;
 52+#endif
 53+
 54+#include <event.h>
 55+#include <pthread.h>
 56+#include <vector>
 57+#include <deque>
 58+#include <cassert>
 59+#include <stdexcept>
 60+#include <cerrno>
 61+#include <utility>
 62+using std::deque;
 63+using std::vector;
 64+using std::runtime_error;
 65+using std::pair;
 66+using std::make_pair;
 67+using std::memcpy;
 68+
 69+#include "willow.h"
 70+#include "polycaller.h"
 71+
 72+extern bool wnet_exit;
 73+
 74+namespace wnet {
 75+ struct addrlist;
 76+ struct address;
 77+ struct socket;
 78+ typedef socket wsocket;
 79+}
 80+
 81+enum sprio {
 82+ prio_stats = 0,
 83+ prio_backend,
 84+ prio_norm,
 85+ prio_accept,
 86+ prio_max
 87+};
 88+
 89+#define FDE_READ 0x1
 90+#define FDE_WRITE 0x2
 91+
 92+extern struct ioloop_t {
 93+ ioloop_t();
 94+
 95+ void prepare (void);
 96+ void run (void);
 97+
 98+ void _accept (wnet::socket *, int);
 99+} *ioloop;
 100+
 101+/*
 102+ * For working with packed UDP data.
 103+ */
 104+#define HAS_SPACE(b,l,endp) (((b) + (l)) < endp)
 105+#define ADD_UINT32(b,i,endp) if (HAS_SPACE(b,4,endp)) { *(uint32_t*)b = i; b += 4; }
 106+#define ADD_UINT16(b,i,endp) if (HAS_SPACE(b,2,endp)) { *(uint16_t*)b = i; b += 2; }
 107+#define ADD_UINT8(b,i,endp) if (HAS_SPACE(b,1,endp)) { *(uint8_t*)b = i; b += 1; }
 108+#define ADD_STRING(b,s,endp) do { uint16_t len = s.size(); \
 109+ if (HAS_SPACE(b,2 + len,endp)) { \
 110+ ADD_UINT16(b,len,endp); \
 111+ memcpy(b, s.data(), len); \
 112+ b += len; \
 113+ } \
 114+ } while (0)
 115+
 116+namespace wnet {
 117+
 118+struct buffer;
 119+
 120+struct buffer_item : freelist_allocator<buffer_item> {
 121+ buffer_item(char const *buf_, size_t len_, bool free_)
 122+ : buf(buf_)
 123+ , len(len_)
 124+ , off(0)
 125+ , free(free_) {
 126+ }
 127+ buffer_item(buffer_item const &o)
 128+ : buf(NULL)
 129+ , len(o.len)
 130+ , off(o.off)
 131+ , free(true) {
 132+ buf = (const char *)memcpy(new char[len], o.buf, len);
 133+ }
 134+ buffer_item&
 135+ operator=(buffer_item const &other) {
 136+ buf = NULL;
 137+ len = other.len;
 138+ off = other.off;
 139+ free = true;
 140+ buf = (const char *)memcpy(new char[len], other.buf, len);
 141+ return *this;
 142+ }
 143+
 144+ ~buffer_item() {
 145+ if (free)
 146+ delete[] buf;
 147+ }
 148+
 149+ char const *buf;
 150+ size_t len;
 151+ size_t off;
 152+ bool free;
 153+};
 154+
 155+struct buffer {
 156+ void add(char const *buf, size_t len, bool free) {
 157+ items.push_back(buffer_item(buf, len, free));
 158+ }
 159+
 160+ void reset(void) {
 161+ items.clear();
 162+ }
 163+
 164+ deque<buffer_item, pt_allocator<buffer_item> > items;
 165+};
 166+
 167+} // namespace wnet
 168+
 169+extern lockable acceptq_lock;
 170+extern tss<event_base> evb;
 171+
 172+struct client_data {
 173+ sockaddr_storage cdat_addr;
 174+ socklen_t cdat_addrlen;
 175+};
 176+
 177+extern char current_time_str[];
 178+extern char current_time_short[];
 179+extern time_t current_time;
 180+
 181+ void wnet_add_accept_wakeup (wnet::socket *);
 182+ void wnet_set_time (void);
 183+ void make_event_base (void);
 184+
 185+namespace wnet { /* things above should move here eventually */
 186+
 187+struct socket_error : runtime_error {
 188+ int _err;
 189+ int err(void) const {
 190+ return _err;
 191+ }
 192+ socket_error(int i) : runtime_error(strerror(i)), _err(i) {}
 193+ socket_error(char const *s) : runtime_error(s), _err(0) {}
 194+ socket_error() : runtime_error(strerror(errno)), _err(errno) {}
 195+};
 196+
 197+struct resolution_error : socket_error {
 198+ resolution_error(int i) : socket_error(gai_strerror(i)) {}
 199+};
 200+
 201+enum connect_status {
 202+ connect_okay,
 203+ connect_later
 204+};
 205+
 206+enum socktype {
 207+ st_stream = SOCK_STREAM,
 208+ st_dgram = SOCK_DGRAM
 209+};
 210+
 211+struct address : freelist_allocator<address> {
 212+ address(void);
 213+ address(const address &o);
 214+ address(sockaddr *, socklen_t);
 215+
 216+ address& operator= (const address &o);
 217+
 218+ string const &straddr(bool lng = true) const;
 219+
 220+ socket *makesocket (char const *, sprio) const;
 221+ int length (void) const { return _addrlen; }
 222+ sockaddr *addr (void) const { return (sockaddr *)&_addr; }
 223+ int family (void) const { return _fam; }
 224+ int socktype(void) const { return _stype; }
 225+ int protocol(void) const { return _prot; }
 226+
 227+ static address from_ifname(int s, string const &ifname);
 228+ static u_int ifname_to_index(string const &ifname);
 229+
 230+private:
 231+ friend struct addrlist;
 232+ address(addrinfo *ai);
 233+
 234+ sockaddr_storage _addr;
 235+ socklen_t _addrlen;
 236+ int _fam, _stype, _prot;
 237+ mutable string _straddr, _shortaddr;
 238+};
 239+
 240+struct addrlist : freelist_allocator<addrlist> {
 241+ typedef address value_type;
 242+ typedef vector<value_type>::const_iterator
 243+ iterator, const_iterator;
 244+
 245+ ~addrlist();
 246+
 247+ iterator begin (void) const;
 248+ iterator end (void) const;
 249+ socket *makesocket (char const *, sprio) const;
 250+ static addrlist *resolve (string const &,
 251+ string const &,
 252+ enum socktype, int = AF_UNSPEC);
 253+ static addrlist *resolve (string const &, int ,
 254+ enum socktype, int = AF_UNSPEC);
 255+
 256+ static address first (string const &,
 257+ string const &,
 258+ enum socktype, int = AF_UNSPEC);
 259+ static address first (string const &, int,
 260+ enum socktype, int = AF_UNSPEC);
 261+
 262+private:
 263+ addrlist() {};
 264+
 265+ vector<value_type> _addrs;
 266+};
 267+
 268+struct socket : noncopyable, freelist_allocator<socket> {
 269+ ~socket();
 270+
 271+ static socket *create(string const &addr, int port,
 272+ enum socktype, char const *, sprio,
 273+ int = AF_UNSPEC);
 274+ static socket *create(string const &addr, string const &port,
 275+ enum socktype, char const *, sprio,
 276+ int = AF_UNSPEC);
 277+ static pair<socket *, socket *> socketpair(enum socktype);
 278+
 279+ socket *accept (char const *, sprio);
 280+ connect_status connect (void);
 281+ int read (char *buf, size_t count) {
 282+ return ::read(_s, buf, count);
 283+ }
 284+ int recvfrom (char *, size_t, wnet::address &);
 285+ int sendto (char const *, size_t, wnet::address const &);
 286+ int write (char const *buf, size_t count) {
 287+ return ::write(_s, buf, count);
 288+ }
 289+ int sendfile (int to, off_t *off, size_t n) {
 290+ return ::sendfile(_s, to, off, n);
 291+ }
 292+
 293+ void nonblocking (bool);
 294+ void reuseaddr (bool);
 295+ void bind (void);
 296+ void listen (int bl = 25);
 297+ int getopt (int, int, void *, socklen_t *) const;
 298+ int setopt (int, int, void *, socklen_t);
 299+ void cork (void);
 300+ void uncork (void);
 301+ int error (void) const;
 302+ char const *description (void) const;
 303+
 304+ void mcast_join (string const &ifname);
 305+ void mcast_leave (string const &ifname);
 306+
 307+ wnet::address const &address (void) const {
 308+ return _addr;
 309+ }
 310+ string const &straddr (bool lng = true) const {
 311+ return _addr.straddr(lng);
 312+ }
 313+
 314+ template<typename T>
 315+ void readback (polycaller<wnet::socket *, T> cb, T ud);
 316+
 317+ template<typename T>
 318+ void writeback (polycaller<wnet::socket *, T> cb, T ud);
 319+
 320+ void clearbacks (void);
 321+
 322+protected:
 323+ friend struct wnet::address;
 324+ friend struct ::ioloop_t;
 325+
 326+ explicit socket (int, wnet::address const &, char const *, sprio);
 327+ explicit socket (wnet::address const &, char const *, sprio);
 328+
 329+ void _register (int, polycallback<wsocket *>);
 330+ static void _ev_callback (int fd, short ev, void *d);
 331+
 332+ polycallback<wsocket *> _read_handler, _write_handler;
 333+
 334+ int _s;
 335+ wnet::address _addr;
 336+ char const *_desc;
 337+ sprio _prio;
 338+ event ev;
 339+};
 340+
 341+ vector<addrinfo> nametoaddrs(string const &name, int port);
 342+ string reserror(int);
 343+
 344+template<typename T>
 345+void
 346+socket::readback (polycaller<wnet::socket *, T> cb, T ud) {
 347+ _register(FDE_READ, polycallback<wnet::socket *>(cb, ud));
 348+}
 349+
 350+template<typename T>
 351+void
 352+socket::writeback (polycaller<wnet::socket *, T> cb, T ud) {
 353+ _register(FDE_WRITE, polycallback<wnet::socket *>(cb, ud));
 354+}
 355+
 356+} // namespace wnet
 357+
 358+#endif
Index: trunk/willow/src/include/backend.h
@@ -0,0 +1,140 @@
 2+/* @(#) $Id: wbackend.h 17684 2006-11-14 22:40:31Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wbackend: HTTP backend handling.
 7+ */
 8+
 9+#ifndef WBACKEND_H
 10+#define WBACKEND_H
 11+
 12+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 13+# pragma ident "@(#)$Id: wbackend.h 17684 2006-11-14 22:40:31Z river $"
 14+#endif
 15+
 16+#include <sys/types.h>
 17+#include <netinet/in.h>
 18+
 19+#include <string>
 20+#include <vector>
 21+#include <map>
 22+using std::vector;
 23+using std::map;
 24+
 25+#include "polycaller.h"
 26+#include "net.h"
 27+using namespace wnet;
 28+
 29+enum lb_type {
 30+ lb_rr,
 31+ lb_carp,
 32+ lb_carp_hostonly
 33+};
 34+
 35+struct backend_list;
 36+struct backend_pool;
 37+struct backend_cb_data;
 38+
 39+struct backend : freelist_allocator<backend> {
 40+ backend(string const &, address const &);
 41+
 42+ string be_name; /* IP as specified in config */
 43+ int be_group; /* group */
 44+ int be_port; /* port number */
 45+ string be_straddr; /* formatted address */
 46+ address be_addr; /* address */
 47+ int be_dead; /* 0 if okay, 1 if unavailable */
 48+ time_t be_time; /* If dead, time to retry */
 49+ uint32_t be_hash; /* constant carp "host" hash */
 50+ uint32_t be_carp; /* carp hash for the last url */
 51+ float be_load; /* carp load factor */
 52+ float be_carplfm; /* carp LFM after calculation */
 53+
 54+ template<typename stringT>
 55+ static uint32_t _carp_hosthash(stringT const &str);
 56+};
 57+
 58+struct backend_list : freelist_allocator<backend_list> {
 59+ backend_list( backend_pool const &pool,
 60+ imstring const &url,
 61+ imstring const &host,
 62+ int failgroup,
 63+ lb_type, int start);
 64+
 65+ int _get_impl (polycallback<backend *, wsocket *>);
 66+ void _backend_read (wsocket *e, backend_cb_data *);
 67+ struct backend *_next_backend (void);
 68+ void _carp_recalc (imstring const &, imstring const &, lb_type);
 69+ static int _becarp_cmp (backend const *a, backend const *b);
 70+ bool failed (void) const {
 71+ return _delegate;
 72+ }
 73+
 74+ template<typename stringT>
 75+ static uint32_t _carp_urlhash(stringT const &str) {
 76+ uint32_t h = 0;
 77+ for (typename stringT::const_iterator it = str.begin(), end = str.end();
 78+ it != end; ++it)
 79+ h += rotl(h, 19) + *it;
 80+ return h;
 81+ }
 82+
 83+
 84+ template<typename T>
 85+ int get (polycaller<backend *, wsocket *, T> cb, T t) {
 86+ return _get_impl(polycallback<backend *, wsocket *>(cb, t));
 87+ }
 88+
 89+ ~backend_list() {
 90+ if (_delegate)
 91+ delete _delegate;
 92+ }
 93+
 94+private:
 95+ vector<backend *, pt_allocator<backend *> > backends;
 96+ size_t _cur;
 97+ int _failgroup;
 98+ struct backend_list *_delegate;
 99+};
 100+
 101+template<typename stringT>
 102+uint32_t
 103+backend::_carp_hosthash(stringT const &str)
 104+{
 105+ uint32_t h = backend_list::_carp_urlhash(str) * 0x62531965;
 106+ return rotl(h, 21);
 107+}
 108+
 109+struct backend_pool {
 110+ backend_pool(string const &name, lb_type, int failgroup = -1);
 111+ ~backend_pool();
 112+
 113+ void add (string const &, int, int);
 114+ backend_list *get_list (imstring const & url, imstring const &host);
 115+
 116+ int size (void) const;
 117+ string const &name (void) const;
 118+
 119+ void add_keptalive (pair<wsocket *, backend *>);
 120+ pair<wsocket *, backend *>
 121+ get_keptalive (void);
 122+
 123+private:
 124+ friend class backend_list;
 125+
 126+ void _carp_calc (void);
 127+
 128+ vector<backend *, pt_allocator<backend *> > backends;
 129+ tss<vector<pair<wsocket *, backend *> > > _keptalive;
 130+ tss<size_t> _cur;
 131+ lb_type _lbtype;
 132+ string _name;
 133+ int _failgroup;
 134+};
 135+
 136+extern map<int, backend_pool> bpools;
 137+extern map<imstring, int> host_to_bpool;
 138+extern map<string, int> poolnames;
 139+extern int nbpools;
 140+
 141+#endif
Index: trunk/willow/src/include/http_header.h
@@ -0,0 +1,247 @@
 2+/* @(#) $Id: whttp_header.h 17805 2006-11-20 14:07:17Z river $ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * http_header: header processing implementation.
 7+ */
 8+
 9+#ifndef WHTTP_HEADER
 10+#define WHTTP_HEADER
 11+
 12+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 13+# pragma ident "@(#)$Id: whttp_header.h 17805 2006-11-20 14:07:17Z river $"
 14+#endif
 15+
 16+#include "autoconf.h"
 17+#include "http.h"
 18+#include "willow.h"
 19+#include "net.h"
 20+#include "flowio.h"
 21+
 22+/*
 23+ * Do NOT change these values, they are used in the UDP
 24+ * log packets.
 25+ */
 26+#define REQTYPE_GET 0
 27+#define REQTYPE_POST 1
 28+#define REQTYPE_HEAD 2
 29+#define REQTYPE_TRACE 3
 30+#define REQTYPE_OPTIONS 4
 31+#define REQTYPE_PURGE 5
 32+#define REQTYPE_INVALID -1
 33+
 34+#define HDR_BUFSZ 256
 35+
 36+extern struct request_type {
 37+ const char *name;
 38+ int len;
 39+ int type;
 40+} supported_reqtypes[];
 41+
 42+/*
 43+ * A single header. Usually stored inside a header_list. For speed,
 44+ * we assume the length of one header line is no more than HDX_BUFSZ-1
 45+ * characters, and pre-allocate a buffer of that size. In case the
 46+ * header is longer, hr_buffer is ignored, and a new buffer is allocated
 47+ * from the pt_allocator. hr_free==true indicated that the buffer should
 48+ * be freed on destruction.
 49+ */
 50+struct header : freelist_allocator<header> {
 51+ /*
 52+ * Construct a new header from the given name and value, which need
 53+ * not be null terminated.
 54+ */
 55+ header( char const *name, size_t namelen,
 56+ char const *value, size_t valuelen);
 57+
 58+ ~header();
 59+ header(header const &);
 60+ header& operator= (header const &);
 61+
 62+ /*
 63+ * Assign new values to this header.
 64+ */
 65+ void assign( char const *name, size_t namelen,
 66+ char const *value, size_t valuelen);
 67+
 68+ /*
 69+ * Destructively assign the contents of other to *this. The state
 70+ * of other is undefined until assign() or ~header is called.
 71+ */
 72+ void move (header &other);
 73+
 74+ /* return the header's name */
 75+ char const *name (void) const { return hr_name; }
 76+ /* return the header's value */
 77+ char const *value (void) const { return hr_value; }
 78+
 79+private:
 80+ friend struct header_list;
 81+
 82+ char hr_buffer[HDR_BUFSZ];
 83+ char *hr_name; /* name, == hr_buffer or else alloc'd data */
 84+ char *hr_value; /* value */
 85+ size_t hr_allocd; /* size of buffer alloced if any */
 86+ static pt_allocator<char> alloc;
 87+};
 88+
 89+/*
 90+ * A list of headers found in a request.
 91+ */
 92+struct header_list {
 93+ /* Construct an empty header list. */
 94+ header_list();
 95+ ~header_list();
 96+ header_list &operator= (header_list const &other);
 97+
 98+ /*
 99+ * Add a new (header,value) pair to the list. name and value must be
 100+ * nul-terminated.
 101+ */
 102+ void add (char const *name, char const *value);
 103+
 104+ /*
 105+ * As above, but nul terminated is not required.
 106+ */
 107+ void add (char const *name, size_t namelen,
 108+ char const *value, size_t valuelen);
 109+
 110+ /*
 111+ * Append ", app" to the end of the previous header. app need not
 112+ * be terminated.
 113+ */
 114+ void append_last (const char *app, size_t applen);
 115+
 116+ /*
 117+ * Remove the named header from the list.
 118+ */
 119+ void remove (const char *);
 120+
 121+ /*
 122+ * Return a string (allocated with new char[]) containing the request
 123+ * headers in a form suitable for use in an HTTP request or response.
 124+ * The caller is expected to delete the returned value.
 125+ */
 126+ char *build (void);
 127+
 128+ /*
 129+ * Return the length of the string build() would return.
 130+ */
 131+ int length (void) const {
 132+ return hl_len;
 133+ }
 134+
 135+ /*
 136+ * Find a specific header in the list. Returns NULL if no such header
 137+ * exists.
 138+ */
 139+struct header *find (const char *name);
 140+
 141+private:
 142+ friend struct header_spigot;
 143+ friend struct header_parser;
 144+
 145+ header *hl_last;
 146+ vector<header *, pt_allocator<header *> > hl_hdrs;
 147+ int hl_len;
 148+};
 149+
 150+struct qvalue {
 151+ float val;
 152+const char *name;
 153+
 154+ bool operator< (qvalue const &rhs) const {
 155+ return val < rhs.val;
 156+ }
 157+};
 158+
 159+/*
 160+ * header_spigot: output an HTTP response built from a pre-defined list
 161+ * of headers. used for responses generated by us.
 162+ */
 163+struct header_spigot : io::spigot
 164+{
 165+ header_spigot(int, char const *);
 166+
 167+ void add (char const *, char const *);
 168+ void body (string const &);
 169+ void body (string &);
 170+ void sp_uncork (void);
 171+ void sp_cork (void);
 172+
 173+private:
 174+ header_list _headers;
 175+ bool _built;
 176+ string _first;
 177+ string _body;
 178+ wnet::buffer _buf;
 179+ bool _corked;
 180+};
 181+
 182+/*
 183+ * header_parser: parse and store headers. This is both a sink (for
 184+ * reading headers and parsing them) and a spigot (for sending headers
 185+ * to a backend or client).
 186+ */
 187+struct header_parser : io::sink, io::spigot
 188+{
 189+ header_parser()
 190+ : _got_reqtype(false)
 191+ , _built(false)
 192+ , _corked(true)
 193+ , _is_response(false)
 194+ , _content_length(-1)
 195+ , _response(0)
 196+ , _is_msie(false)
 197+ , _http_reqtype(REQTYPE_INVALID)
 198+ , _no_keepalive(false)
 199+ , _force_keepalive(false)
 200+ , _follow_redirect(false)
 201+ , _eof(false)
 202+ {
 203+ _flags.f_chunked = 0;
 204+ }
 205+
 206+ io::sink_result data_ready (char const *buf, size_t len, ssize_t&);
 207+ io::sink_result data_empty (void);
 208+
 209+ int parse_reqtype (char const *buf, char const *endp);
 210+ int parse_response (char const *buf, char const *endp);
 211+ void completed_callback (void);
 212+ void error_callback (void);
 213+ void sp_cork (void);
 214+ void sp_uncork (void);
 215+ void set_response (void);
 216+ void sending_restart (void);
 217+
 218+ polycaller<> _completed_callee;
 219+ polycaller<> _error_callee;
 220+ header_list _headers;
 221+ bool _got_reqtype;
 222+ http_version _http_vers;
 223+ imstring _http_path;
 224+ bool _built;
 225+ bool _corked;
 226+ bool _is_response;
 227+ ssize_t _content_length;
 228+ int _response;
 229+ bool _is_msie;
 230+ int _http_reqtype;
 231+ imstring _http_host;
 232+ string _http_backend;
 233+ bool _no_keepalive;
 234+ bool _force_keepalive;
 235+ bool _follow_redirect;
 236+ imstring _location;
 237+
 238+ bool _eof;
 239+ wnet::buffer _buf;
 240+
 241+ struct {
 242+ unsigned int f_chunked:1;
 243+ } _flags;
 244+};
 245+
 246+void whttp_header_init(void);
 247+
 248+#endif
Index: trunk/willow/src/include/flowio.h
@@ -23,7 +23,7 @@
2424 using std::min;
2525
2626 #include "willow.h"
27 -#include "wnet.h"
 27+#include "net.h"
2828 using namespace wnet;
2929
3030 namespace io {
Index: trunk/willow/src/include/cache.h
@@ -23,9 +23,9 @@
2424 using std::fstream;
2525
2626 #include "willow.h"
27 -#include "wthread.h"
 27+#include "thread.h"
2828 #include "flowio.h"
29 -#include "whttp_header.h"
 29+#include "http_header.h"
3030 #include "dbwrap.h"
3131
3232 struct caching_filter;
Index: trunk/willow/src/libwillow/libwillow.cc
@@ -6,7 +6,7 @@
77 */
88
99 #include "ptalloc.h"
10 -#include "wthread.h"
 10+#include "thread.h"
1111
1212 void
1313 pttsswrapdtor(void *p)