Index: trunk/willow/configure.in |
— | — | @@ -4,7 +4,7 @@ |
5 | 5 | |
6 | 6 | USER_CXXFLAGS="$CXXFLAGS" |
7 | 7 | CPPFLAGS="-D_REENTRANT $CPPFLAGS" |
8 | | -AC_CONFIG_HEADER(src/include/config.h) |
| 8 | +AC_CONFIG_HEADER(src/include/autoconf.h) |
9 | 9 | |
10 | 10 | echo "" |
11 | 11 | 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 += "<"; |
973 | | - break; |
974 | | - case '>': |
975 | | - res += ">"; |
976 | | - break; |
977 | | - case '"': |
978 | | - res += """; |
979 | | - break; |
980 | | - case '\'': |
981 | | - res += "'"; |
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(¤t_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(¤t_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 @@ |
5 | 5 | |
6 | 6 | BASESRCS = \ |
7 | 7 | a_cachedir.cc \ |
| 8 | + backend.cc \ |
8 | 9 | cache.cc \ |
9 | 10 | cachedentity.cc \ |
10 | 11 | cachedir_data_store.cc \ |
11 | 12 | chunking.cc \ |
| 13 | + config.cc \ |
12 | 14 | confparse.cc \ |
13 | 15 | dbwrap.cc \ |
14 | 16 | flowio.cc \ |
15 | 17 | format.cc \ |
16 | 18 | htcp.cc \ |
| 19 | + http.cc \ |
| 20 | + http_header.cc \ |
| 21 | + log.cc \ |
| 22 | + net.cc \ |
17 | 23 | radix.cc \ |
18 | | - wbackend.cc \ |
19 | | - wconfig.cc \ |
20 | | - whttp.cc \ |
21 | | - whttp_header.cc \ |
22 | 24 | willow.cc \ |
23 | | - wlog.cc \ |
24 | | - wnet.cc \ |
25 | 25 | |
26 | 26 | OBJS= $(BASESRCS:.cc=.o) |
27 | 27 | |
Index: trunk/willow/src/willow/htcp.cc |
— | — | @@ -13,8 +13,8 @@ |
14 | 14 | using std::pair; |
15 | 15 | |
16 | 16 | #include "willow.h" |
17 | | -#include "wnet.h" |
18 | | -#include "wconfig.h" |
| 17 | +#include "net.h" |
| 18 | +#include "config.h" |
19 | 19 | #include "mbuffer.h" |
20 | 20 | #include "htcp.h" |
21 | 21 | #include "cache.h" |
Index: trunk/willow/src/willow/flowio.cc |
— | — | @@ -22,9 +22,9 @@ |
23 | 23 | using std::streamsize; |
24 | 24 | |
25 | 25 | #include "flowio.h" |
26 | | -#include "wnet.h" |
| 26 | +#include "net.h" |
27 | 27 | #include "format.h" |
28 | | -#include "wconfig.h" |
| 28 | +#include "config.h" |
29 | 29 | |
30 | 30 | namespace io { |
31 | 31 | |
Index: trunk/willow/src/willow/cache.cc |
— | — | @@ -16,7 +16,7 @@ |
17 | 17 | |
18 | 18 | #include "cache.h" |
19 | 19 | #include "format.h" |
20 | | -#include "wconfig.h" |
| 20 | +#include "config.h" |
21 | 21 | |
22 | 22 | httpcache entitycache; |
23 | 23 | |
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 @@ |
15 | 15 | using std::min; |
16 | 16 | |
17 | 17 | #include "chunking.h" |
18 | | -#include "whttp_header.h" |
| 18 | +#include "http_header.h" |
19 | 19 | #include "flowio.h" |
20 | 20 | #include "util.h" |
21 | 21 | |
Index: trunk/willow/src/willow/willow.cc |
— | — | @@ -26,11 +26,11 @@ |
27 | 27 | #include <iostream> |
28 | 28 | using std::streamsize; |
29 | 29 | |
30 | | -#include "wlog.h" |
31 | | -#include "wnet.h" |
32 | | -#include "wconfig.h" |
| 30 | +#include "log.h" |
| 31 | +#include "net.h" |
| 32 | +#include "config.h" |
33 | 33 | #include "willow.h" |
34 | | -#include "whttp.h" |
| 34 | +#include "http.h" |
35 | 35 | #include "cache.h" |
36 | 36 | #include "confparse.h" |
37 | 37 | #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 += "<"; |
| 971 | + break; |
| 972 | + case '>': |
| 973 | + res += ">"; |
| 974 | + break; |
| 975 | + case '"': |
| 976 | + res += """; |
| 977 | + break; |
| 978 | + case '\'': |
| 979 | + res += "'"; |
| 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 @@ |
23 | 23 | |
24 | 24 | #include "confparse.h" |
25 | 25 | #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" |
29 | 29 | #include "format.h" |
30 | 30 | #include "expr.h" |
31 | 31 | |
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 @@ |
16 | 16 | #include <cstdlib> |
17 | 17 | using std::memset; |
18 | 18 | |
19 | | -#include "wthread.h" |
| 19 | +#include "thread.h" |
20 | 20 | |
21 | 21 | #ifndef __SUNPRO_CC |
22 | 22 | template<typename T> |
Index: trunk/willow/src/include/willow.h |
— | — | @@ -12,7 +12,7 @@ |
13 | 13 | # pragma ident "@(#)$Id$" |
14 | 14 | #endif |
15 | 15 | |
16 | | -#include "config.h" |
| 16 | +#include "autoconf.h" |
17 | 17 | |
18 | 18 | #include <sstream> |
19 | 19 | #include <cstddef> |
— | — | @@ -30,11 +30,11 @@ |
31 | 31 | using std::basic_ostream; |
32 | 32 | using std::istream; |
33 | 33 | |
34 | | -#include "wlog.h" |
| 34 | +#include "log.h" |
35 | 35 | #include "radix.h" |
36 | 36 | #include "ptalloc.h" |
37 | 37 | #include "format.h" |
38 | | -#include "wconfig.h" |
| 38 | +#include "config.h" |
39 | 39 | |
40 | 40 | typedef unsigned long long w_size_t; |
41 | 41 | |
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 @@ |
24 | 24 | using std::min; |
25 | 25 | |
26 | 26 | #include "willow.h" |
27 | | -#include "wnet.h" |
| 27 | +#include "net.h" |
28 | 28 | using namespace wnet; |
29 | 29 | |
30 | 30 | namespace io { |
Index: trunk/willow/src/include/cache.h |
— | — | @@ -23,9 +23,9 @@ |
24 | 24 | using std::fstream; |
25 | 25 | |
26 | 26 | #include "willow.h" |
27 | | -#include "wthread.h" |
| 27 | +#include "thread.h" |
28 | 28 | #include "flowio.h" |
29 | | -#include "whttp_header.h" |
| 29 | +#include "http_header.h" |
30 | 30 | #include "dbwrap.h" |
31 | 31 | |
32 | 32 | struct caching_filter; |
Index: trunk/willow/src/libwillow/libwillow.cc |
— | — | @@ -6,7 +6,7 @@ |
7 | 7 | */ |
8 | 8 | |
9 | 9 | #include "ptalloc.h" |
10 | | -#include "wthread.h" |
| 10 | +#include "thread.h" |
11 | 11 | |
12 | 12 | void |
13 | 13 | pttsswrapdtor(void *p) |