1 /*
2 * Copyright (C) 2010-2014 Weibin Yao (yaoweibin@gmail.com)
3 * Copyright (C) 2010-2014 Alibaba Group Holding Limited
4 */
5
6
7 #include <nginx.h>
8 #include "ngx_http_upstream_check_module.h"
9
10
11 typedef struct ngx_http_upstream_check_peer_s ngx_http_upstream_check_peer_t;
12 typedef struct ngx_http_upstream_check_srv_conf_s
13 ngx_http_upstream_check_srv_conf_t;
14
15
16 #pragma pack(push, 1)
17
18 typedef struct {
19 u_char major;
20 u_char minor;
21 } ngx_ssl_protocol_version_t;
22
23
24 typedef struct {
25 u_char msg_type;
26 ngx_ssl_protocol_version_t version;
27 uint16_t length;
28
29 u_char handshake_type;
30 u_char handshake_length[3];
31 ngx_ssl_protocol_version_t hello_version;
32
33 time_t time;
34 u_char random[28];
35
36 u_char others[0];
37 } ngx_ssl_server_hello_t;
38
39
40 typedef struct {
41 u_char packet_length[3];
42 u_char packet_number;
43
44 u_char protocol_version;
45 u_char others[0];
46 } ngx_mysql_handshake_init_t;
47
48
49 typedef struct {
50 uint16_t preamble;
51 uint16_t length;
52 u_char type;
53 } ngx_ajp_raw_packet_t;
54
55 #pragma pack()
56
57
58 typedef struct {
59 ngx_buf_t send;
60 ngx_buf_t recv;
61
62 ngx_uint_t state;
63 ngx_http_status_t status;
64
65 size_t padding;
66 size_t length;
67 } ngx_http_upstream_check_ctx_t;
68
69
70 typedef struct {
71 ngx_shmtx_t mutex;
72 #if (nginx_version >= 1002000)
73 ngx_shmtx_sh_t lock;
74 #else
75 ngx_atomic_t lock;
76 #endif
77
78 ngx_pid_t owner;
79
80 ngx_msec_t access_time;
81
82 ngx_uint_t fall_count;
83 ngx_uint_t rise_count;
84
85 ngx_uint_t busyness;
86 ngx_uint_t access_count;
87
88 struct sockaddr *sockaddr;
89 socklen_t socklen;
90
91 ngx_atomic_t down;
92
93 u_char padding[64];
94 } ngx_http_upstream_check_peer_shm_t;
95
96
97 typedef struct {
98 ngx_uint_t generation;
99 ngx_uint_t checksum;
100 ngx_uint_t number;
101
102 /* ngx_http_upstream_check_status_peer_t */
103 ngx_http_upstream_check_peer_shm_t peers[1];
104 } ngx_http_upstream_check_peers_shm_t;
105
106
107 #define NGX_HTTP_CHECK_CONNECT_DONE 0x0001
108 #define NGX_HTTP_CHECK_SEND_DONE 0x0002
109 #define NGX_HTTP_CHECK_RECV_DONE 0x0004
110 #define NGX_HTTP_CHECK_ALL_DONE 0x0008
111
112
113 typedef ngx_int_t (*ngx_http_upstream_check_packet_init_pt)
114 (ngx_http_upstream_check_peer_t *peer);
115 typedef ngx_int_t (*ngx_http_upstream_check_packet_parse_pt)
116 (ngx_http_upstream_check_peer_t *peer);
117 typedef void (*ngx_http_upstream_check_packet_clean_pt)
118 (ngx_http_upstream_check_peer_t *peer);
119
120 struct ngx_http_upstream_check_peer_s {
121 ngx_flag_t state;
122 ngx_pool_t *pool;
123 ngx_uint_t index;
124 ngx_uint_t max_busy;
125 ngx_str_t *upstream_name;
126 ngx_addr_t *check_peer_addr;
127 ngx_addr_t *peer_addr;
128 ngx_event_t check_ev;
129 ngx_event_t check_timeout_ev;
130 ngx_peer_connection_t pc;
131
132 void *check_data;
133 ngx_event_handler_pt send_handler;
134 ngx_event_handler_pt recv_handler;
135
136 ngx_http_upstream_check_packet_init_pt init;
137 ngx_http_upstream_check_packet_parse_pt parse;
138 ngx_http_upstream_check_packet_clean_pt reinit;
139
140 ngx_http_upstream_check_peer_shm_t *shm;
141 ngx_http_upstream_check_srv_conf_t *conf;
142 };
143
144
145 typedef struct {
146 ngx_str_t check_shm_name;
147 ngx_uint_t checksum;
148 ngx_array_t peers;
149
150 ngx_http_upstream_check_peers_shm_t *peers_shm;
151 } ngx_http_upstream_check_peers_t;
152
153
154 #define NGX_HTTP_CHECK_TCP 0x0001
155 #define NGX_HTTP_CHECK_HTTP 0x0002
156 #define NGX_HTTP_CHECK_SSL_HELLO 0x0004
157 #define NGX_HTTP_CHECK_MYSQL 0x0008
158 #define NGX_HTTP_CHECK_AJP 0x0010
159
160 #define NGX_CHECK_HTTP_2XX 0x0002
161 #define NGX_CHECK_HTTP_3XX 0x0004
162 #define NGX_CHECK_HTTP_4XX 0x0008
163 #define NGX_CHECK_HTTP_5XX 0x0010
164 #define NGX_CHECK_HTTP_ERR 0x8000
165
166 typedef struct {
167 ngx_uint_t type;
168
169 ngx_str_t name;
170
171 ngx_str_t default_send;
172
173 /* HTTP */
174 ngx_uint_t default_status_alive;
175
176 ngx_event_handler_pt send_handler;
177 ngx_event_handler_pt recv_handler;
178
179 ngx_http_upstream_check_packet_init_pt init;
180 ngx_http_upstream_check_packet_parse_pt parse;
181 ngx_http_upstream_check_packet_clean_pt reinit;
182
183 unsigned need_pool;
184 unsigned need_keepalive;
185 } ngx_check_conf_t;
186
187
188 typedef void (*ngx_http_upstream_check_status_format_pt) (ngx_buf_t *b,
189 ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);
190
191 typedef struct {
192 ngx_str_t format;
193 ngx_str_t content_type;
194
195 ngx_http_upstream_check_status_format_pt output;
196 } ngx_check_status_conf_t;
197
198
199 #define NGX_CHECK_STATUS_DOWN 0x0001
200 #define NGX_CHECK_STATUS_UP 0x0002
201
202 typedef struct {
203 ngx_check_status_conf_t *format;
204 ngx_flag_t flag;
205 } ngx_http_upstream_check_status_ctx_t;
206
207
208 typedef ngx_int_t (*ngx_http_upstream_check_status_command_pt)
209 (ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);
210
211 typedef struct {
212 ngx_str_t name;
213 ngx_http_upstream_check_status_command_pt handler;
214 } ngx_check_status_command_t;
215
216
217 typedef struct {
218 ngx_uint_t check_shm_size;
219 ngx_http_upstream_check_peers_t *peers;
220 } ngx_http_upstream_check_main_conf_t;
221
222
223 struct ngx_http_upstream_check_srv_conf_s {
224 ngx_uint_t port;
225 ngx_uint_t fall_count;
226 ngx_uint_t rise_count;
227 ngx_msec_t check_interval;
228 ngx_msec_t check_timeout;
229 ngx_uint_t check_keepalive_requests;
230
231 ngx_check_conf_t *check_type_conf;
232 ngx_str_t send;
233
234 union {
235 ngx_uint_t return_code;
236 ngx_uint_t status_alive;
237 } code;
238
239 ngx_array_t *fastcgi_params;
240
241 ngx_uint_t default_down;
242 };
243
244
245 typedef struct {
246 ngx_check_status_conf_t *format;
247 } ngx_http_upstream_check_loc_conf_t;
248
249
250 typedef struct {
251 u_char version;
252 u_char type;
253 u_char request_id_hi;
254 u_char request_id_lo;
255 u_char content_length_hi;
256 u_char content_length_lo;
257 u_char padding_length;
258 u_char reserved;
259 } ngx_http_fastcgi_header_t;
260
261
262 typedef struct {
263 u_char role_hi;
264 u_char role_lo;
265 u_char flags;
266 u_char reserved[5];
267 } ngx_http_fastcgi_begin_request_t;
268
269
270 typedef struct {
271 u_char version;
272 u_char type;
273 u_char request_id_hi;
274 u_char request_id_lo;
275 } ngx_http_fastcgi_header_small_t;
276
277
278 typedef struct {
279 ngx_http_fastcgi_header_t h0;
280 ngx_http_fastcgi_begin_request_t br;
281 ngx_http_fastcgi_header_small_t h1;
282 } ngx_http_fastcgi_request_start_t;
283
284
285 #define NGX_HTTP_FASTCGI_RESPONDER 1
286
287 #define NGX_HTTP_FASTCGI_KEEP_CONN 1
288
289 #define NGX_HTTP_FASTCGI_BEGIN_REQUEST 1
290 #define NGX_HTTP_FASTCGI_ABORT_REQUEST 2
291 #define NGX_HTTP_FASTCGI_END_REQUEST 3
292 #define NGX_HTTP_FASTCGI_PARAMS 4
293 #define NGX_HTTP_FASTCGI_STDIN 5
294 #define NGX_HTTP_FASTCGI_STDOUT 6
295 #define NGX_HTTP_FASTCGI_STDERR 7
296 #define NGX_HTTP_FASTCGI_DATA 8
297
298
299 typedef enum {
300 ngx_http_fastcgi_st_version = 0,
301 ngx_http_fastcgi_st_type,
302 ngx_http_fastcgi_st_request_id_hi,
303 ngx_http_fastcgi_st_request_id_lo,
304 ngx_http_fastcgi_st_content_length_hi,
305 ngx_http_fastcgi_st_content_length_lo,
306 ngx_http_fastcgi_st_padding_length,
307 ngx_http_fastcgi_st_reserved,
308 ngx_http_fastcgi_st_data,
309 ngx_http_fastcgi_st_padding
310 } ngx_http_fastcgi_state_e;
311
312
313 static ngx_http_fastcgi_request_start_t ngx_http_fastcgi_request_start = {
314 { 1, /* version */
315 NGX_HTTP_FASTCGI_BEGIN_REQUEST, /* type */
316 0, /* request_id_hi */
317 1, /* request_id_lo */
318 0, /* content_length_hi */
319 sizeof(ngx_http_fastcgi_begin_request_t), /* content_length_lo */
320 0, /* padding_length */
321 0 }, /* reserved */
322
323 { 0, /* role_hi */
324 NGX_HTTP_FASTCGI_RESPONDER, /* role_lo */
325 0, /* NGX_HTTP_FASTCGI_KEEP_CONN */ /* flags */
326 { 0, 0, 0, 0, 0 } }, /* reserved[5] */
327
328 { 1, /* version */
329 NGX_HTTP_FASTCGI_PARAMS, /* type */
330 0, /* request_id_hi */
331 1 }, /* request_id_lo */
332
333 };
334
335
336 static ngx_int_t ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle);
337
338 static ngx_int_t ngx_http_upstream_check_peek_one_byte(ngx_connection_t *c);
339
340 static void ngx_http_upstream_check_begin_handler(ngx_event_t *event);
341 static void ngx_http_upstream_check_connect_handler(ngx_event_t *event);
342
343 static void ngx_http_upstream_check_peek_handler(ngx_event_t *event);
344
345 static void ngx_http_upstream_check_send_handler(ngx_event_t *event);
346 static void ngx_http_upstream_check_recv_handler(ngx_event_t *event);
347
348 static void ngx_http_upstream_check_discard_handler(ngx_event_t *event);
349 static void ngx_http_upstream_check_dummy_handler(ngx_event_t *event);
350
351 static ngx_int_t ngx_http_upstream_check_http_init(
352 ngx_http_upstream_check_peer_t *peer);
353 static ngx_int_t ngx_http_upstream_check_http_parse(
354 ngx_http_upstream_check_peer_t *peer);
355 static ngx_int_t ngx_http_upstream_check_parse_status_line(
356 ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,
357 ngx_http_status_t *status);
358 static void ngx_http_upstream_check_http_reinit(
359 ngx_http_upstream_check_peer_t *peer);
360
361 static ngx_buf_t *ngx_http_upstream_check_create_fastcgi_request(
362 ngx_pool_t *pool, ngx_str_t *params, ngx_uint_t num);
363
364 static ngx_int_t ngx_http_upstream_check_fastcgi_parse(
365 ngx_http_upstream_check_peer_t *peer);
366 static ngx_int_t ngx_http_upstream_check_fastcgi_process_record(
367 ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,
368 ngx_http_status_t *status);
369 static ngx_int_t ngx_http_upstream_check_parse_fastcgi_status(
370 ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,
371 ngx_http_status_t *status);
372
373 static ngx_int_t ngx_http_upstream_check_ssl_hello_init(
374 ngx_http_upstream_check_peer_t *peer);
375 static ngx_int_t ngx_http_upstream_check_ssl_hello_parse(
376 ngx_http_upstream_check_peer_t *peer);
377 static void ngx_http_upstream_check_ssl_hello_reinit(
378 ngx_http_upstream_check_peer_t *peer);
379
380 static ngx_int_t ngx_http_upstream_check_mysql_init(
381 ngx_http_upstream_check_peer_t *peer);
382 static ngx_int_t ngx_http_upstream_check_mysql_parse(
383 ngx_http_upstream_check_peer_t *peer);
384 static void ngx_http_upstream_check_mysql_reinit(
385 ngx_http_upstream_check_peer_t *peer);
386
387 static ngx_int_t ngx_http_upstream_check_ajp_init(
388 ngx_http_upstream_check_peer_t *peer);
389 static ngx_int_t ngx_http_upstream_check_ajp_parse(
390 ngx_http_upstream_check_peer_t *peer);
391 static void ngx_http_upstream_check_ajp_reinit(
392 ngx_http_upstream_check_peer_t *peer);
393
394 static void ngx_http_upstream_check_status_update(
395 ngx_http_upstream_check_peer_t *peer,
396 ngx_int_t result);
397
398 static void ngx_http_upstream_check_clean_event(
399 ngx_http_upstream_check_peer_t *peer);
400
401 static void ngx_http_upstream_check_timeout_handler(ngx_event_t *event);
402 static void ngx_http_upstream_check_finish_handler(ngx_event_t *event);
403
404 static ngx_int_t ngx_http_upstream_check_need_exit();
405 static void ngx_http_upstream_check_clear_all_events();
406
407 static ngx_int_t ngx_http_upstream_check_status_handler(
408 ngx_http_request_t *r);
409
410 static void ngx_http_upstream_check_status_parse_args(ngx_http_request_t *r,
411 ngx_http_upstream_check_status_ctx_t *ctx);
412
413 static ngx_int_t ngx_http_upstream_check_status_command_format(
414 ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);
415 static ngx_int_t ngx_http_upstream_check_status_command_status(
416 ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);
417
418 static void ngx_http_upstream_check_status_html_format(ngx_buf_t *b,
419 ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);
420 static void ngx_http_upstream_check_status_csv_format(ngx_buf_t *b,
421 ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);
422 static void ngx_http_upstream_check_status_json_format(ngx_buf_t *b,
423 ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);
424
425 static ngx_int_t ngx_http_upstream_check_addr_change_port(ngx_pool_t *pool,
426 ngx_addr_t *dst, ngx_addr_t *src, ngx_uint_t port);
427
428 static ngx_check_conf_t *ngx_http_get_check_type_conf(ngx_str_t *str);
429
430 static char *ngx_http_upstream_check(ngx_conf_t *cf,
431 ngx_command_t *cmd, void *conf);
432 static char *ngx_http_upstream_check_keepalive_requests(ngx_conf_t *cf,
433 ngx_command_t *cmd, void *conf);
434 static char *ngx_http_upstream_check_http_send(ngx_conf_t *cf,
435 ngx_command_t *cmd, void *conf);
436 static char *ngx_http_upstream_check_http_expect_alive(ngx_conf_t *cf,
437 ngx_command_t *cmd, void *conf);
438
439 static char *ngx_http_upstream_check_fastcgi_params(ngx_conf_t *cf,
440 ngx_command_t *cmd, void *conf);
441
442 static char *ngx_http_upstream_check_shm_size(ngx_conf_t *cf,
443 ngx_command_t *cmd, void *conf);
444
445 static ngx_check_status_conf_t *ngx_http_get_check_status_format_conf(
446 ngx_str_t *str);
447 static char *ngx_http_upstream_check_status(ngx_conf_t *cf,
448 ngx_command_t *cmd, void *conf);
449
450 static void *ngx_http_upstream_check_create_main_conf(ngx_conf_t *cf);
451 static char *ngx_http_upstream_check_init_main_conf(ngx_conf_t *cf,
452 void *conf);
453
454 static void *ngx_http_upstream_check_create_srv_conf(ngx_conf_t *cf);
455 static char *ngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf);
456
457 static void *ngx_http_upstream_check_create_loc_conf(ngx_conf_t *cf);
458 static char * ngx_http_upstream_check_merge_loc_conf(ngx_conf_t *cf,
459 void *parent, void *child);
460
461 #define SHM_NAME_LEN 256
462
463 static char *ngx_http_upstream_check_init_shm(ngx_conf_t *cf, void *conf);
464
465 static ngx_int_t ngx_http_upstream_check_get_shm_name(ngx_str_t *shm_name,
466 ngx_pool_t *pool, ngx_uint_t generation);
467 static ngx_shm_zone_t *ngx_shared_memory_find(ngx_cycle_t *cycle,
468 ngx_str_t *name, void *tag);
469 static ngx_http_upstream_check_peer_shm_t *
470 ngx_http_upstream_check_find_shm_peer(ngx_http_upstream_check_peers_shm_t *peers_shm,
471 ngx_addr_t *addr);
472
473 static ngx_int_t ngx_http_upstream_check_init_shm_peer(
474 ngx_http_upstream_check_peer_shm_t *peer_shm,
475 ngx_http_upstream_check_peer_shm_t *opeer_shm,
476 ngx_uint_t init_down, ngx_pool_t *pool, ngx_str_t *peer_name);
477
478 static ngx_int_t ngx_http_upstream_check_init_shm_zone(
479 ngx_shm_zone_t *shm_zone, void *data);
480
481
482 static ngx_int_t ngx_http_upstream_check_init_process(ngx_cycle_t *cycle);
483
484
485 static ngx_conf_bitmask_t ngx_check_http_expect_alive_masks[] = {
486 { ngx_string("http_2xx"), NGX_CHECK_HTTP_2XX },
487 { ngx_string("http_3xx"), NGX_CHECK_HTTP_3XX },
488 { ngx_string("http_4xx"), NGX_CHECK_HTTP_4XX },
489 { ngx_string("http_5xx"), NGX_CHECK_HTTP_5XX },
490 { ngx_null_string, 0 }
491 };
492
493
494 static ngx_command_t ngx_http_upstream_check_commands[] = {
495
496 { ngx_string("check"),
497 NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
498 ngx_http_upstream_check,
499 0,
500 0,
501 NULL },
502
503 { ngx_string("check_keepalive_requests"),
504 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
505 ngx_http_upstream_check_keepalive_requests,
506 0,
507 0,
508 NULL },
509
510 { ngx_string("check_http_send"),
511 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,
512 ngx_http_upstream_check_http_send,
513 0,
514 0,
515 NULL },
516
517 { ngx_string("check_http_expect_alive"),
518 NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,
519 ngx_http_upstream_check_http_expect_alive,
520 0,
521 0,
522 NULL },
523
524 { ngx_string("check_fastcgi_param"),
525 NGX_HTTP_UPS_CONF|NGX_CONF_TAKE2,
526 ngx_http_upstream_check_fastcgi_params,
527 0,
528 0,
529 NULL },
530
531 { ngx_string("check_shm_size"),
532 NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,
533 ngx_http_upstream_check_shm_size,
534 0,
535 0,
536 NULL },
537
538 { ngx_string("check_status"),
539 NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1|NGX_CONF_NOARGS,
540 ngx_http_upstream_check_status,
541 0,
542 0,
543 NULL },
544
545 ngx_null_command
546 };
547
548
549 static ngx_http_module_t ngx_http_upstream_check_module_ctx = {
550 NULL, /* preconfiguration */
551 NULL, /* postconfiguration */
552
553 ngx_http_upstream_check_create_main_conf,/* create main configuration */
554 ngx_http_upstream_check_init_main_conf, /* init main configuration */
555
556 ngx_http_upstream_check_create_srv_conf, /* create server configuration */
557 NULL, /* merge server configuration */
558
559 ngx_http_upstream_check_create_loc_conf, /* create location configuration */
560 ngx_http_upstream_check_merge_loc_conf /* merge location configuration */
561 };
562
563
564 ngx_module_t ngx_http_upstream_check_module = {
565 NGX_MODULE_V1,
566 &ngx_http_upstream_check_module_ctx, /* module context */
567 ngx_http_upstream_check_commands, /* module directives */
568 NGX_HTTP_MODULE, /* module type */
569 NULL, /* init master */
570 NULL, /* init module */
571 ngx_http_upstream_check_init_process, /* init process */
572 NULL, /* init thread */
573 NULL, /* exit thread */
574 NULL, /* exit process */
575 NULL, /* exit master */
576 NGX_MODULE_V1_PADDING
577 };
578
579
580 static ngx_str_t fastcgi_default_request;
581 static ngx_str_t fastcgi_default_params[] = {
582 ngx_string("REQUEST_METHOD"), ngx_string("GET"),
583 ngx_string("REQUEST_URI"), ngx_string("/"),
584 ngx_string("SCRIPT_FILENAME"), ngx_string("index.php"),
585 };
586
587
588 #define NGX_SSL_RANDOM "NGX_HTTP_CHECK_SSL_HELLO\n\n\n\n"
589
590 /*
591 * This is the SSLv3 CLIENT HELLO packet used in conjunction with the
592 * check type of ssl_hello to ensure that the remote server speaks SSL.
593 *
594 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
595 */
596 static char sslv3_client_hello_pkt[] = {
597 "\x16" /* ContentType : 0x16 = Hanshake */
598 "\x03\x01" /* ProtocolVersion : 0x0301 = TLSv1.0 */
599 "\x00\x6f" /* ContentLength : 0x6f bytes after this one */
600 "\x01" /* HanshakeType : 0x01 = CLIENT HELLO */
601 "\x00\x00\x6b" /* HandshakeLength : 0x6b bytes after this one */
602 "\x03\x03" /* Hello Version : 0x0303 = TLSv1.2 */
603 "\x00\x00\x00\x00" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
604 NGX_SSL_RANDOM /* Random : must be exactly 28 bytes */
605 "\x00" /* Session ID length : empty (no session ID) */
606 "\x00\x1a" /* Cipher Suite Length : \x1a bytes after this one */
607 "\xc0\x2b" "\xc0\x2f" "\xcc\xa9" "\xcc\xa8" /* 13 modern ciphers */
608 "\xc0\x0a" "\xc0\x09" "\xc0\x13" "\xc0\x14"
609 "\x00\x33" "\x00\x39" "\x00\x2f" "\x00\x35"
610 "\x00\x0a"
611 "\x01" /* Compression Length : 0x01 = 1 byte for types */
612 "\x00" /* Compression Type : 0x00 = NULL compression */
613 "\x00\x28" /* Extensions length */
614 "\x00\x0a" /* EC extension */
615 "\x00\x08" /* extension length */
616 "\x00\x06" /* curves length */
617 "\x00\x17" "\x00\x18" "\x00\x19" /* Three curves */
618 "\x00\x0d" /* Signature extension */
619 "\x00\x18" /* extension length */
620 "\x00\x16" /* hash list length */
621 "\x04\x01" "\x05\x01" "\x06\x01" "\x02\x01" /* 11 hash algorithms */
622 "\x04\x03" "\x05\x03" "\x06\x03" "\x02\x03"
623 "\x05\x02" "\x04\x02" "\x02\x02"
624 };
625
626
627 #define NGX_SSL_HANDSHAKE 0x16
628 #define NGX_SSL_SERVER_HELLO 0x02
629
630
631 #define NGX_AJP_CPING 0x0a
632 #define NGX_AJP_CPONG 0x09
633
634
635 static char ngx_ajp_cping_packet[] = {
636 0x12, 0x34, 0x00, 0x01, NGX_AJP_CPING, 0x00
637 };
638
639 static char ngx_ajp_cpong_packet[] = {
640 0x41, 0x42, 0x00, 0x01, NGX_AJP_CPONG
641 };
642
643
644 static ngx_check_conf_t ngx_check_types[] = {
645
646 { NGX_HTTP_CHECK_TCP,
647 ngx_string("tcp"),
648 ngx_null_string,
649 0,
650 ngx_http_upstream_check_peek_handler,
651 ngx_http_upstream_check_peek_handler,
652 NULL,
653 NULL,
654 NULL,
655 0,
656 1 },
657
658 { NGX_HTTP_CHECK_HTTP,
659 ngx_string("http"),
660 ngx_string("GET / HTTP/1.0\r\n\r\n"),
661 NGX_CONF_BITMASK_SET | NGX_CHECK_HTTP_2XX | NGX_CHECK_HTTP_3XX,
662 ngx_http_upstream_check_send_handler,
663 ngx_http_upstream_check_recv_handler,
664 ngx_http_upstream_check_http_init,
665 ngx_http_upstream_check_http_parse,
666 ngx_http_upstream_check_http_reinit,
667 1,
668 1 },
669
670 { NGX_HTTP_CHECK_HTTP,
671 ngx_string("fastcgi"),
672 ngx_null_string,
673 0,
674 ngx_http_upstream_check_send_handler,
675 ngx_http_upstream_check_recv_handler,
676 ngx_http_upstream_check_http_init,
677 ngx_http_upstream_check_fastcgi_parse,
678 ngx_http_upstream_check_http_reinit,
679 1,
680 0 },
681
682 { NGX_HTTP_CHECK_SSL_HELLO,
683 ngx_string("ssl_hello"),
684 ngx_string(sslv3_client_hello_pkt),
685 0,
686 ngx_http_upstream_check_send_handler,
687 ngx_http_upstream_check_recv_handler,
688 ngx_http_upstream_check_ssl_hello_init,
689 ngx_http_upstream_check_ssl_hello_parse,
690 ngx_http_upstream_check_ssl_hello_reinit,
691 1,
692 0 },
693
694 { NGX_HTTP_CHECK_MYSQL,
695 ngx_string("mysql"),
696 ngx_null_string,
697 0,
698 ngx_http_upstream_check_send_handler,
699 ngx_http_upstream_check_recv_handler,
700 ngx_http_upstream_check_mysql_init,
701 ngx_http_upstream_check_mysql_parse,
702 ngx_http_upstream_check_mysql_reinit,
703 1,
704 0 },
705
706 { NGX_HTTP_CHECK_AJP,
707 ngx_string("ajp"),
708 ngx_string(ngx_ajp_cping_packet),
709 0,
710 ngx_http_upstream_check_send_handler,
711 ngx_http_upstream_check_recv_handler,
712 ngx_http_upstream_check_ajp_init,
713 ngx_http_upstream_check_ajp_parse,
714 ngx_http_upstream_check_ajp_reinit,
715 1,
716 0 },
717
718 { 0,
719 ngx_null_string,
720 ngx_null_string,
721 0,
722 NULL,
723 NULL,
724 NULL,
725 NULL,
726 NULL,
727 0,
728 0 }
729 };
730
731
732 static ngx_check_status_conf_t ngx_check_status_formats[] = {
733
734 { ngx_string("html"),
735 ngx_string("text/html"),
736 ngx_http_upstream_check_status_html_format },
737
738 { ngx_string("csv"),
739 ngx_string("text/plain"),
740 ngx_http_upstream_check_status_csv_format },
741
742 { ngx_string("json"),
743 ngx_string("application/json"), /* RFC 4627 */
744 ngx_http_upstream_check_status_json_format },
745
746 { ngx_null_string, ngx_null_string, NULL }
747 };
748
749
750 static ngx_check_status_command_t ngx_check_status_commands[] = {
751
752 { ngx_string("format"),
753 ngx_http_upstream_check_status_command_format },
754
755 { ngx_string("status"),
756 ngx_http_upstream_check_status_command_status },
757
758 { ngx_null_string, NULL }
759 };
760
761
762 static ngx_uint_t ngx_http_upstream_check_shm_generation = 0;
763 static ngx_http_upstream_check_peers_t *check_peers_ctx = NULL;
764
765
766 ngx_uint_t
ngx_http_upstream_check_add_peer(ngx_conf_t * cf,ngx_http_upstream_srv_conf_t * us,ngx_addr_t * peer_addr)767 ngx_http_upstream_check_add_peer(ngx_conf_t *cf,
768 ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer_addr)
769 {
770 ngx_http_upstream_check_peer_t *peer;
771 ngx_http_upstream_check_peers_t *peers;
772 ngx_http_upstream_check_srv_conf_t *ucscf;
773 ngx_http_upstream_check_main_conf_t *ucmcf;
774
775 if (us->srv_conf == NULL) {
776 return NGX_ERROR;
777 }
778
779 ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);
780
781 if(ucscf->check_interval == 0) {
782 return NGX_ERROR;
783 }
784
785 ucmcf = ngx_http_conf_get_module_main_conf(cf,
786 ngx_http_upstream_check_module);
787 peers = ucmcf->peers;
788
789 peer = ngx_array_push(&peers->peers);
790 if (peer == NULL) {
791 return NGX_ERROR;
792 }
793
794 ngx_memzero(peer, sizeof(ngx_http_upstream_check_peer_t));
795
796 peer->index = peers->peers.nelts - 1;
797 peer->conf = ucscf;
798 peer->upstream_name = &us->host;
799 peer->peer_addr = peer_addr;
800
801 if (ucscf->port) {
802 peer->check_peer_addr = ngx_pcalloc(cf->pool, sizeof(ngx_addr_t));
803 if (peer->check_peer_addr == NULL) {
804 return NGX_ERROR;
805 }
806
807 if (ngx_http_upstream_check_addr_change_port(cf->pool,
808 peer->check_peer_addr, peer_addr, ucscf->port)
809 != NGX_OK) {
810
811 return NGX_ERROR;
812 }
813
814 } else {
815 peer->check_peer_addr = peer->peer_addr;
816 }
817
818 peers->checksum +=
819 ngx_murmur_hash2(peer_addr->name.data, peer_addr->name.len);
820
821 return peer->index;
822 }
823
824
825 static ngx_int_t
ngx_http_upstream_check_addr_change_port(ngx_pool_t * pool,ngx_addr_t * dst,ngx_addr_t * src,ngx_uint_t port)826 ngx_http_upstream_check_addr_change_port(ngx_pool_t *pool, ngx_addr_t *dst,
827 ngx_addr_t *src, ngx_uint_t port)
828 {
829 size_t len;
830 u_char *p;
831 struct sockaddr_in *sin;
832 #if (NGX_HAVE_INET6)
833 struct sockaddr_in6 *sin6;
834 #endif
835
836 dst->socklen = src->socklen;
837 dst->sockaddr = ngx_palloc(pool, dst->socklen);
838 if (dst->sockaddr == NULL) {
839 return NGX_ERROR;
840 }
841
842 ngx_memcpy(dst->sockaddr, src->sockaddr, dst->socklen);
843
844 switch (dst->sockaddr->sa_family) {
845
846 case AF_INET:
847
848 len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
849 sin = (struct sockaddr_in *) dst->sockaddr;
850 sin->sin_port = htons(port);
851
852 break;
853
854 #if (NGX_HAVE_INET6)
855 case AF_INET6:
856
857 len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;
858 sin6 = (struct sockaddr_in6 *) dst->sockaddr;
859 sin6->sin6_port = htons(port);
860
861 break;
862 #endif
863
864 default:
865 return NGX_ERROR;
866 }
867
868 p = ngx_pnalloc(pool, len);
869 if (p == NULL) {
870 return NGX_ERROR;
871 }
872
873 #if (nginx_version >= 1005012)
874 len = ngx_sock_ntop(dst->sockaddr, dst->socklen, p, len, 1);
875 #else
876 len = ngx_sock_ntop(dst->sockaddr, p, len, 1);
877 #endif
878
879 dst->name.len = len;
880 dst->name.data = p;
881
882 return NGX_OK;
883 }
884
885
886 ngx_uint_t
ngx_http_upstream_check_peer_down(ngx_uint_t index)887 ngx_http_upstream_check_peer_down(ngx_uint_t index)
888 {
889 ngx_http_upstream_check_peer_t *peer;
890
891 if (check_peers_ctx == NULL || index >= check_peers_ctx->peers.nelts) {
892 return 0;
893 }
894
895 peer = check_peers_ctx->peers.elts;
896
897 return (peer[index].shm->down);
898 }
899
900
901 /* TODO: this interface can count each peer's busyness */
902 void
ngx_http_upstream_check_get_peer(ngx_uint_t index)903 ngx_http_upstream_check_get_peer(ngx_uint_t index)
904 {
905 ngx_http_upstream_check_peer_t *peer;
906
907 if (check_peers_ctx == NULL || index >= check_peers_ctx->peers.nelts) {
908 return;
909 }
910
911 peer = check_peers_ctx->peers.elts;
912
913 ngx_shmtx_lock(&peer[index].shm->mutex);
914
915 peer[index].shm->busyness++;
916 peer[index].shm->access_count++;
917
918 ngx_shmtx_unlock(&peer[index].shm->mutex);
919 }
920
921
922 void
ngx_http_upstream_check_free_peer(ngx_uint_t index)923 ngx_http_upstream_check_free_peer(ngx_uint_t index)
924 {
925 ngx_http_upstream_check_peer_t *peer;
926
927 if (check_peers_ctx == NULL || index >= check_peers_ctx->peers.nelts) {
928 return;
929 }
930
931 peer = check_peers_ctx->peers.elts;
932
933 ngx_shmtx_lock(&peer[index].shm->mutex);
934
935 if (peer[index].shm->busyness > 0) {
936 peer[index].shm->busyness--;
937 }
938
939 ngx_shmtx_unlock(&peer[index].shm->mutex);
940 }
941
942
943 static ngx_int_t
ngx_http_upstream_check_add_timers(ngx_cycle_t * cycle)944 ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle)
945 {
946 ngx_uint_t i;
947 ngx_msec_t t, delay;
948 ngx_check_conf_t *cf;
949 ngx_http_upstream_check_peer_t *peer;
950 ngx_http_upstream_check_peers_t *peers;
951 ngx_http_upstream_check_srv_conf_t *ucscf;
952 ngx_http_upstream_check_peer_shm_t *peer_shm;
953 ngx_http_upstream_check_peers_shm_t *peers_shm;
954
955 peers = check_peers_ctx;
956 if (peers == NULL) {
957 return NGX_OK;
958 }
959
960 peers_shm = peers->peers_shm;
961 if (peers_shm == NULL) {
962 return NGX_OK;
963 }
964
965 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cycle->log, 0,
966 "http check upstream init_process, shm_name: %V, "
967 "peer number: %ud",
968 &peers->check_shm_name,
969 peers->peers.nelts);
970
971 srandom(ngx_pid);
972
973 peer = peers->peers.elts;
974 peer_shm = peers_shm->peers;
975
976 for (i = 0; i < peers->peers.nelts; i++) {
977 peer[i].shm = &peer_shm[i];
978
979 peer[i].check_ev.handler = ngx_http_upstream_check_begin_handler;
980 peer[i].check_ev.log = cycle->log;
981 peer[i].check_ev.data = &peer[i];
982 peer[i].check_ev.timer_set = 0;
983
984 peer[i].check_timeout_ev.handler =
985 ngx_http_upstream_check_timeout_handler;
986 peer[i].check_timeout_ev.log = cycle->log;
987 peer[i].check_timeout_ev.data = &peer[i];
988 peer[i].check_timeout_ev.timer_set = 0;
989
990 ucscf = peer[i].conf;
991 cf = ucscf->check_type_conf;
992
993 if (cf->need_pool) {
994 peer[i].pool = ngx_create_pool(ngx_pagesize, cycle->log);
995 if (peer[i].pool == NULL) {
996 return NGX_ERROR;
997 }
998 }
999
1000 peer[i].send_handler = cf->send_handler;
1001 peer[i].recv_handler = cf->recv_handler;
1002
1003 peer[i].init = cf->init;
1004 peer[i].parse = cf->parse;
1005 peer[i].reinit = cf->reinit;
1006
1007 /*
1008 * We add a random start time here, since we don't want to trigger
1009 * the check events too close to each other at the beginning.
1010 */
1011 delay = ucscf->check_interval > 1000 ? ucscf->check_interval : 1000;
1012 t = ngx_random() % delay;
1013
1014 ngx_add_timer(&peer[i].check_ev, t);
1015 }
1016
1017 return NGX_OK;
1018 }
1019
1020
1021 static void
ngx_http_upstream_check_begin_handler(ngx_event_t * event)1022 ngx_http_upstream_check_begin_handler(ngx_event_t *event)
1023 {
1024 ngx_msec_t interval;
1025 ngx_http_upstream_check_peer_t *peer;
1026 ngx_http_upstream_check_peers_t *peers;
1027 ngx_http_upstream_check_srv_conf_t *ucscf;
1028 ngx_http_upstream_check_peers_shm_t *peers_shm;
1029
1030 if (ngx_http_upstream_check_need_exit()) {
1031 return;
1032 }
1033
1034 peers = check_peers_ctx;
1035 if (peers == NULL) {
1036 return;
1037 }
1038
1039 peers_shm = peers->peers_shm;
1040 if (peers_shm == NULL) {
1041 return;
1042 }
1043
1044 peer = event->data;
1045 ucscf = peer->conf;
1046
1047 ngx_add_timer(event, ucscf->check_interval / 2);
1048
1049 /* This process is processing this peer now. */
1050 if ((peer->shm->owner == ngx_pid ||
1051 (peer->pc.connection != NULL) ||
1052 peer->check_timeout_ev.timer_set)) {
1053 return;
1054 }
1055
1056 interval = ngx_current_msec - peer->shm->access_time;
1057 ngx_log_debug5(NGX_LOG_DEBUG_HTTP, event->log, 0,
1058 "http check begin handler index: %ui, owner: %P, "
1059 "ngx_pid: %P, interval: %M, check_interval: %M",
1060 peer->index, peer->shm->owner,
1061 ngx_pid, interval,
1062 ucscf->check_interval);
1063
1064 ngx_shmtx_lock(&peer->shm->mutex);
1065
1066 if (peers_shm->generation != ngx_http_upstream_check_shm_generation) {
1067 ngx_shmtx_unlock(&peer->shm->mutex);
1068 return;
1069 }
1070
1071 if ((interval >= ucscf->check_interval)
1072 && (peer->shm->owner == NGX_INVALID_PID))
1073 {
1074 peer->shm->owner = ngx_pid;
1075
1076 } else if (interval >= (ucscf->check_interval << 4)) {
1077
1078 /*
1079 * If the check peer has been untouched for 2^4 times of
1080 * the check interval, activate the current timer.
1081 * Sometimes, the checking process may disappear
1082 * in some circumstances, and the clean event will never
1083 * be triggered.
1084 */
1085 peer->shm->owner = ngx_pid;
1086 peer->shm->access_time = ngx_current_msec;
1087 }
1088
1089 ngx_shmtx_unlock(&peer->shm->mutex);
1090
1091 if (peer->shm->owner == ngx_pid) {
1092 ngx_http_upstream_check_connect_handler(event);
1093 }
1094 }
1095
1096
1097 static void
ngx_http_upstream_check_connect_handler(ngx_event_t * event)1098 ngx_http_upstream_check_connect_handler(ngx_event_t *event)
1099 {
1100 ngx_int_t rc;
1101 ngx_connection_t *c;
1102 ngx_http_upstream_check_peer_t *peer;
1103 ngx_http_upstream_check_srv_conf_t *ucscf;
1104
1105 if (ngx_http_upstream_check_need_exit()) {
1106 return;
1107 }
1108
1109 peer = event->data;
1110 ucscf = peer->conf;
1111
1112 if (peer->pc.connection != NULL) {
1113 c = peer->pc.connection;
1114 if ((rc = ngx_http_upstream_check_peek_one_byte(c)) == NGX_OK) {
1115 goto upstream_check_connect_done;
1116 } else {
1117 ngx_close_connection(c);
1118 peer->pc.connection = NULL;
1119 }
1120 }
1121 ngx_memzero(&peer->pc, sizeof(ngx_peer_connection_t));
1122
1123 peer->pc.sockaddr = peer->check_peer_addr->sockaddr;
1124 peer->pc.socklen = peer->check_peer_addr->socklen;
1125 peer->pc.name = &peer->check_peer_addr->name;
1126
1127 peer->pc.get = ngx_event_get_peer;
1128 peer->pc.log = event->log;
1129 peer->pc.log_error = NGX_ERROR_ERR;
1130
1131 peer->pc.cached = 0;
1132 peer->pc.connection = NULL;
1133
1134 rc = ngx_event_connect_peer(&peer->pc);
1135
1136 if (rc == NGX_ERROR || rc == NGX_DECLINED) {
1137 ngx_http_upstream_check_status_update(peer, 0);
1138 ngx_http_upstream_check_clean_event(peer);
1139 return;
1140 }
1141
1142 /* NGX_OK or NGX_AGAIN */
1143 c = peer->pc.connection;
1144 c->data = peer;
1145 c->log = peer->pc.log;
1146 c->sendfile = 0;
1147 c->read->log = c->log;
1148 c->write->log = c->log;
1149 c->pool = peer->pool;
1150
1151 upstream_check_connect_done:
1152 peer->state = NGX_HTTP_CHECK_CONNECT_DONE;
1153
1154 c->write->handler = peer->send_handler;
1155 c->read->handler = peer->recv_handler;
1156
1157 ngx_add_timer(&peer->check_timeout_ev, ucscf->check_timeout);
1158
1159 /* The kqueue's loop interface needs it. */
1160 if (rc == NGX_OK) {
1161 c->write->handler(c->write);
1162 }
1163 }
1164
1165 static ngx_int_t
ngx_http_upstream_check_peek_one_byte(ngx_connection_t * c)1166 ngx_http_upstream_check_peek_one_byte(ngx_connection_t *c)
1167 {
1168 char buf[1];
1169 ngx_int_t n;
1170 ngx_err_t err;
1171
1172 n = recv(c->fd, buf, 1, MSG_PEEK);
1173 err = ngx_socket_errno;
1174
1175 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, err,
1176 "http check upstream recv(): %i, fd: %d",
1177 n, c->fd);
1178
1179 if (n == 1 || (n == -1 && err == NGX_EAGAIN)) {
1180 return NGX_OK;
1181 } else {
1182 return NGX_ERROR;
1183 }
1184 }
1185
1186 static void
ngx_http_upstream_check_peek_handler(ngx_event_t * event)1187 ngx_http_upstream_check_peek_handler(ngx_event_t *event)
1188 {
1189 ngx_connection_t *c;
1190 ngx_http_upstream_check_peer_t *peer;
1191
1192 if (ngx_http_upstream_check_need_exit()) {
1193 return;
1194 }
1195
1196 c = event->data;
1197 peer = c->data;
1198
1199 if (ngx_http_upstream_check_peek_one_byte(c) == NGX_OK) {
1200 ngx_http_upstream_check_status_update(peer, 1);
1201
1202 } else {
1203 c->error = 1;
1204 ngx_http_upstream_check_status_update(peer, 0);
1205 }
1206
1207 ngx_http_upstream_check_clean_event(peer);
1208
1209 ngx_http_upstream_check_finish_handler(event);
1210 }
1211
1212
1213 static void
ngx_http_upstream_check_discard_handler(ngx_event_t * event)1214 ngx_http_upstream_check_discard_handler(ngx_event_t *event)
1215 {
1216 u_char buf[4096];
1217 ssize_t size;
1218 ngx_connection_t *c;
1219 ngx_http_upstream_check_peer_t *peer;
1220
1221 c = event->data;
1222
1223 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
1224 "upstream check discard handler");
1225
1226 if (ngx_http_upstream_check_need_exit()) {
1227 return;
1228 }
1229
1230 peer = c->data;
1231
1232 while (1) {
1233 size = c->recv(c, buf, 4096);
1234
1235 if (size > 0) {
1236 continue;
1237
1238 } else if (size == NGX_AGAIN) {
1239 break;
1240
1241 } else {
1242 if (size == 0) {
1243 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
1244 "peer closed its half side of the connection");
1245 }
1246
1247 goto check_discard_fail;
1248 }
1249 }
1250
1251 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
1252 goto check_discard_fail;
1253 }
1254
1255 return;
1256
1257 check_discard_fail:
1258 c->error = 1;
1259 ngx_http_upstream_check_clean_event(peer);
1260 }
1261
1262
1263 static void
ngx_http_upstream_check_dummy_handler(ngx_event_t * event)1264 ngx_http_upstream_check_dummy_handler(ngx_event_t *event)
1265 {
1266 return;
1267 }
1268
1269
1270 static void
ngx_http_upstream_check_send_handler(ngx_event_t * event)1271 ngx_http_upstream_check_send_handler(ngx_event_t *event)
1272 {
1273 ssize_t size;
1274 ngx_connection_t *c;
1275 ngx_http_upstream_check_ctx_t *ctx;
1276 ngx_http_upstream_check_peer_t *peer;
1277
1278 if (ngx_http_upstream_check_need_exit()) {
1279 return;
1280 }
1281
1282 c = event->data;
1283 peer = c->data;
1284
1285 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http check send.");
1286
1287 if (c->pool == NULL) {
1288 ngx_log_error(NGX_LOG_ERR, event->log, 0,
1289 "check pool NULL with peer: %V ",
1290 &peer->check_peer_addr->name);
1291
1292 goto check_send_fail;
1293 }
1294
1295 if (peer->state != NGX_HTTP_CHECK_CONNECT_DONE) {
1296 if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
1297
1298 ngx_log_error(NGX_LOG_ERR, event->log, 0,
1299 "check handle write event error with peer: %V ",
1300 &peer->check_peer_addr->name);
1301
1302 goto check_send_fail;
1303 }
1304
1305 return;
1306 }
1307
1308 if (peer->check_data == NULL) {
1309
1310 peer->check_data = ngx_pcalloc(peer->pool,
1311 sizeof(ngx_http_upstream_check_ctx_t));
1312 if (peer->check_data == NULL) {
1313 goto check_send_fail;
1314 }
1315
1316 if (peer->init == NULL || peer->init(peer) != NGX_OK) {
1317
1318 ngx_log_error(NGX_LOG_ERR, event->log, 0,
1319 "check init error with peer: %V ",
1320 &peer->check_peer_addr->name);
1321
1322 goto check_send_fail;
1323 }
1324 }
1325
1326 ctx = peer->check_data;
1327
1328 while (ctx->send.pos < ctx->send.last) {
1329
1330 size = c->send(c, ctx->send.pos, ctx->send.last - ctx->send.pos);
1331
1332 #if (NGX_DEBUG)
1333 {
1334 ngx_err_t err;
1335
1336 err = (size >=0) ? 0 : ngx_socket_errno;
1337 ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, err,
1338 "http check send size: %z, total: %z",
1339 size, ctx->send.last - ctx->send.pos);
1340 }
1341 #endif
1342
1343 if (size > 0) {
1344 ctx->send.pos += size;
1345 } else if (size == 0 || size == NGX_AGAIN) {
1346 return;
1347 } else {
1348 c->error = 1;
1349 goto check_send_fail;
1350 }
1351 }
1352
1353 if (ctx->send.pos == ctx->send.last) {
1354 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http check send done.");
1355 peer->state = NGX_HTTP_CHECK_SEND_DONE;
1356 c->requests++;
1357 }
1358
1359 return;
1360
1361 check_send_fail:
1362 ngx_http_upstream_check_status_update(peer, 0);
1363 ngx_http_upstream_check_clean_event(peer);
1364 }
1365
1366
1367 static void
ngx_http_upstream_check_recv_handler(ngx_event_t * event)1368 ngx_http_upstream_check_recv_handler(ngx_event_t *event)
1369 {
1370 u_char *new_buf;
1371 ssize_t size, n;
1372 ngx_int_t rc;
1373 ngx_connection_t *c;
1374 ngx_http_upstream_check_ctx_t *ctx;
1375 ngx_http_upstream_check_peer_t *peer;
1376
1377 if (ngx_http_upstream_check_need_exit()) {
1378 return;
1379 }
1380
1381 c = event->data;
1382 peer = c->data;
1383
1384 if (peer->state != NGX_HTTP_CHECK_SEND_DONE) {
1385
1386 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
1387 goto check_recv_fail;
1388 }
1389
1390 return;
1391 }
1392
1393 ctx = peer->check_data;
1394
1395 if (ctx->recv.start == NULL) {
1396 /* 1/2 of the page_size, is it enough? */
1397 ctx->recv.start = ngx_palloc(c->pool, ngx_pagesize / 2);
1398 if (ctx->recv.start == NULL) {
1399 goto check_recv_fail;
1400 }
1401
1402 ctx->recv.last = ctx->recv.pos = ctx->recv.start;
1403 ctx->recv.end = ctx->recv.start + ngx_pagesize / 2;
1404 }
1405
1406 while (1) {
1407 n = ctx->recv.end - ctx->recv.last;
1408
1409 /* buffer not big enough? enlarge it by twice */
1410 if (n == 0) {
1411 size = ctx->recv.end - ctx->recv.start;
1412 new_buf = ngx_palloc(c->pool, size * 2);
1413 if (new_buf == NULL) {
1414 goto check_recv_fail;
1415 }
1416
1417 ngx_memcpy(new_buf, ctx->recv.start, size);
1418
1419 ctx->recv.pos = ctx->recv.start = new_buf;
1420 ctx->recv.last = new_buf + size;
1421 ctx->recv.end = new_buf + size * 2;
1422
1423 n = ctx->recv.end - ctx->recv.last;
1424 }
1425
1426 size = c->recv(c, ctx->recv.last, n);
1427
1428 #if (NGX_DEBUG)
1429 {
1430 ngx_err_t err;
1431
1432 err = (size >= 0) ? 0 : ngx_socket_errno;
1433 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, err,
1434 "http check recv size: %z, peer: %V ",
1435 size, &peer->check_peer_addr->name);
1436 }
1437 #endif
1438
1439 if (size > 0) {
1440 ctx->recv.last += size;
1441 continue;
1442 } else if (size == 0 || size == NGX_AGAIN) {
1443 break;
1444 } else {
1445 c->error = 1;
1446 goto check_recv_fail;
1447 }
1448 }
1449
1450 rc = peer->parse(peer);
1451
1452 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
1453 "http check parse rc: %i, peer: %V ",
1454 rc, &peer->check_peer_addr->name);
1455
1456 switch (rc) {
1457
1458 case NGX_AGAIN:
1459 /* The peer has closed its half side of the connection. */
1460 if (size == 0) {
1461 ngx_http_upstream_check_status_update(peer, 0);
1462 c->error = 1;
1463 break;
1464 }
1465
1466 return;
1467
1468 case NGX_ERROR:
1469 ngx_log_error(NGX_LOG_ERR, event->log, 0,
1470 "check protocol %V error with peer: %V ",
1471 &peer->conf->check_type_conf->name,
1472 &peer->check_peer_addr->name);
1473
1474 ngx_http_upstream_check_status_update(peer, 0);
1475 break;
1476
1477 case NGX_OK:
1478 /* fall through */
1479
1480 default:
1481 ngx_http_upstream_check_status_update(peer, 1);
1482 break;
1483 }
1484
1485 peer->state = NGX_HTTP_CHECK_RECV_DONE;
1486 ngx_http_upstream_check_clean_event(peer);
1487 return;
1488
1489 check_recv_fail:
1490 ngx_http_upstream_check_status_update(peer, 0);
1491 ngx_http_upstream_check_clean_event(peer);
1492 }
1493
1494
1495 static ngx_int_t
ngx_http_upstream_check_http_init(ngx_http_upstream_check_peer_t * peer)1496 ngx_http_upstream_check_http_init(ngx_http_upstream_check_peer_t *peer)
1497 {
1498 ngx_http_upstream_check_ctx_t *ctx;
1499 ngx_http_upstream_check_srv_conf_t *ucscf;
1500
1501 ctx = peer->check_data;
1502 ucscf = peer->conf;
1503
1504 ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
1505 ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
1506
1507 ctx->recv.start = ctx->recv.pos = NULL;
1508 ctx->recv.end = ctx->recv.last = NULL;
1509
1510 ctx->state = 0;
1511
1512 ngx_memzero(&ctx->status, sizeof(ngx_http_status_t));
1513
1514 return NGX_OK;
1515 }
1516
1517
1518 static ngx_int_t
ngx_http_upstream_check_http_parse(ngx_http_upstream_check_peer_t * peer)1519 ngx_http_upstream_check_http_parse(ngx_http_upstream_check_peer_t *peer)
1520 {
1521 ngx_int_t rc;
1522 ngx_uint_t code, code_n;
1523 ngx_http_upstream_check_ctx_t *ctx;
1524 ngx_http_upstream_check_srv_conf_t *ucscf;
1525
1526 ucscf = peer->conf;
1527 ctx = peer->check_data;
1528
1529 if ((ctx->recv.last - ctx->recv.pos) > 0) {
1530
1531 rc = ngx_http_upstream_check_parse_status_line(ctx,
1532 &ctx->recv,
1533 &ctx->status);
1534 if (rc == NGX_AGAIN) {
1535 return rc;
1536 }
1537
1538 if (rc == NGX_ERROR) {
1539 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1540 "http parse status line error with peer: %V ",
1541 &peer->check_peer_addr->name);
1542 return rc;
1543 }
1544
1545 code = ctx->status.code;
1546
1547 if (code >= 200 && code < 300) {
1548 code_n = NGX_CHECK_HTTP_2XX;
1549 } else if (code >= 300 && code < 400) {
1550 code_n = NGX_CHECK_HTTP_3XX;
1551 } else if (code >= 400 && code < 500) {
1552 peer->pc.connection->error = 1;
1553 code_n = NGX_CHECK_HTTP_4XX;
1554 } else if (code >= 500 && code < 600) {
1555 peer->pc.connection->error = 1;
1556 code_n = NGX_CHECK_HTTP_5XX;
1557 } else {
1558 peer->pc.connection->error = 1;
1559 code_n = NGX_CHECK_HTTP_ERR;
1560 }
1561
1562 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1563 "http_parse: code_n: %ui, conf: %ui",
1564 code_n, ucscf->code.status_alive);
1565
1566 if (code_n & ucscf->code.status_alive) {
1567 return NGX_OK;
1568 } else {
1569 return NGX_ERROR;
1570 }
1571 } else {
1572 return NGX_AGAIN;
1573 }
1574
1575 return NGX_OK;
1576 }
1577
1578
1579 static ngx_int_t
ngx_http_upstream_check_fastcgi_process_record(ngx_http_upstream_check_ctx_t * ctx,ngx_buf_t * b,ngx_http_status_t * status)1580 ngx_http_upstream_check_fastcgi_process_record(
1581 ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b, ngx_http_status_t *status)
1582 {
1583 u_char ch, *p;
1584 ngx_http_fastcgi_state_e state;
1585
1586 state = ctx->state;
1587
1588 for (p = b->pos; p < b->last; p++) {
1589
1590 ch = *p;
1591
1592 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1593 "http fastcgi record byte: %02Xd", ch);
1594
1595 switch (state) {
1596
1597 case ngx_http_fastcgi_st_version:
1598 if (ch != 1) {
1599 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1600 "upstream sent unsupported FastCGI "
1601 "protocol version: %d", ch);
1602 return NGX_ERROR;
1603 }
1604 state = ngx_http_fastcgi_st_type;
1605 break;
1606
1607 case ngx_http_fastcgi_st_type:
1608 switch (ch) {
1609 case NGX_HTTP_FASTCGI_STDOUT:
1610 case NGX_HTTP_FASTCGI_STDERR:
1611 case NGX_HTTP_FASTCGI_END_REQUEST:
1612 status->code = (ngx_uint_t) ch;
1613 break;
1614 default:
1615 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1616 "upstream sent invalid FastCGI "
1617 "record type: %d", ch);
1618 return NGX_ERROR;
1619
1620 }
1621 state = ngx_http_fastcgi_st_request_id_hi;
1622 break;
1623
1624 /* we support the single request per connection */
1625
1626 case ngx_http_fastcgi_st_request_id_hi:
1627 if (ch != 0) {
1628 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1629 "upstream sent unexpected FastCGI "
1630 "request id high byte: %d", ch);
1631 return NGX_ERROR;
1632 }
1633 state = ngx_http_fastcgi_st_request_id_lo;
1634 break;
1635
1636 case ngx_http_fastcgi_st_request_id_lo:
1637 if (ch != 1) {
1638 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1639 "upstream sent unexpected FastCGI "
1640 "request id low byte: %d", ch);
1641 return NGX_ERROR;
1642 }
1643 state = ngx_http_fastcgi_st_content_length_hi;
1644 break;
1645
1646 case ngx_http_fastcgi_st_content_length_hi:
1647 ctx->length = ch << 8;
1648 state = ngx_http_fastcgi_st_content_length_lo;
1649 break;
1650
1651 case ngx_http_fastcgi_st_content_length_lo:
1652 ctx->length |= (size_t) ch;
1653 state = ngx_http_fastcgi_st_padding_length;
1654 break;
1655
1656 case ngx_http_fastcgi_st_padding_length:
1657 ctx->padding = (size_t) ch;
1658 state = ngx_http_fastcgi_st_reserved;
1659 break;
1660
1661 case ngx_http_fastcgi_st_reserved:
1662 state = ngx_http_fastcgi_st_data;
1663
1664 b->pos = p + 1;
1665 ctx->state = state;
1666
1667 return NGX_OK;
1668
1669 /* suppress warning */
1670 case ngx_http_fastcgi_st_data:
1671 case ngx_http_fastcgi_st_padding:
1672 break;
1673 }
1674 }
1675
1676 ctx->state = state;
1677
1678 return NGX_AGAIN;
1679 }
1680
1681
1682 static ngx_int_t
ngx_http_upstream_check_fastcgi_parse(ngx_http_upstream_check_peer_t * peer)1683 ngx_http_upstream_check_fastcgi_parse(ngx_http_upstream_check_peer_t *peer)
1684 {
1685 ngx_int_t rc;
1686 ngx_flag_t done;
1687 ngx_uint_t type, code, code_n;
1688 ngx_http_upstream_check_ctx_t *ctx;
1689 ngx_http_upstream_check_srv_conf_t *ucscf;
1690
1691 ucscf = peer->conf;
1692 ctx = peer->check_data;
1693
1694 if ((ctx->recv.last - ctx->recv.pos) <= 0) {
1695 return NGX_AGAIN;
1696 }
1697
1698 done = 0;
1699
1700 for ( ;; ) {
1701
1702 if (ctx->state < ngx_http_fastcgi_st_data) {
1703 rc = ngx_http_upstream_check_fastcgi_process_record(ctx,
1704 &ctx->recv, &ctx->status);
1705
1706 type = ctx->status.code;
1707
1708 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1709 "fastcgi_parse rc: [%i], type: [%ui]", rc, type);
1710
1711 if (rc == NGX_AGAIN) {
1712 return rc;
1713 }
1714
1715 if (rc == NGX_ERROR) {
1716 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1717 "check fastcgi parse status line error with peer: %V",
1718 &peer->check_peer_addr->name);
1719
1720 return rc;
1721 }
1722
1723 if (type != NGX_HTTP_FASTCGI_STDOUT
1724 && type != NGX_HTTP_FASTCGI_STDERR)
1725 {
1726 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1727 "check fastcgi sent unexpected FastCGI record: %d", type);
1728
1729 return NGX_ERROR;
1730 }
1731
1732 if (type == NGX_HTTP_FASTCGI_STDOUT && ctx->length == 0) {
1733 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1734 "check fastcgi prematurely closed FastCGI stdout");
1735
1736 return NGX_ERROR;
1737 }
1738 }
1739
1740 if (ctx->state == ngx_http_fastcgi_st_padding) {
1741
1742 if (ctx->recv.pos + ctx->padding < ctx->recv.last) {
1743 ctx->status.code = ngx_http_fastcgi_st_version;
1744 ctx->recv.pos += ctx->padding;
1745
1746 continue;
1747 }
1748
1749 if (ctx->recv.pos + ctx->padding == ctx->recv.last) {
1750 ctx->status.code = ngx_http_fastcgi_st_version;
1751 ctx->recv.pos = ctx->recv.last;
1752
1753 return NGX_AGAIN;
1754 }
1755
1756 ctx->padding -= ctx->recv.last - ctx->recv.pos;
1757 ctx->recv.pos = ctx->recv.last;
1758
1759 return NGX_AGAIN;
1760 }
1761
1762 if (ctx->status.code == NGX_HTTP_FASTCGI_STDERR) {
1763
1764 ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,
1765 "fastcgi check error");
1766
1767 return NGX_ERROR;
1768 }
1769
1770 /* ctx->status.code == NGX_HTTP_FASTCGI_STDOUT */
1771
1772 if (ctx->recv.pos + ctx->length < ctx->recv.last) {
1773 ctx->recv.last = ctx->recv.pos + ctx->length;
1774 } else {
1775 return NGX_ERROR;
1776 }
1777
1778 ctx->status.code = 0;
1779
1780 for ( ;; ) {
1781 rc = ngx_http_upstream_check_parse_fastcgi_status(ctx,
1782 &ctx->recv,
1783 &ctx->status);
1784 ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
1785 "fastcgi http parse status line rc: %i ", rc);
1786
1787 if (rc == NGX_ERROR) {
1788 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
1789 "fastcgi http parse status line error with peer: %V ",
1790 &peer->check_peer_addr->name);
1791 return NGX_ERROR;
1792 }
1793
1794 if (rc == NGX_AGAIN) {
1795 break;
1796 }
1797
1798 if (rc == NGX_DONE) {
1799 done = 1;
1800 ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,
1801 "fastcgi http parse status: %i",
1802 ctx->status.code);
1803 break;
1804 }
1805
1806 /* rc = NGX_OK */
1807 }
1808
1809 if (ucscf->code.status_alive == 0 || done == 0) {
1810 return NGX_OK;
1811 }
1812
1813 code = ctx->status.code;
1814
1815 if (code >= 200 && code < 300) {
1816 code_n = NGX_CHECK_HTTP_2XX;
1817 } else if (code >= 300 && code < 400) {
1818 code_n = NGX_CHECK_HTTP_3XX;
1819 } else if (code >= 400 && code < 500) {
1820 code_n = NGX_CHECK_HTTP_4XX;
1821 } else if (code >= 500 && code < 600) {
1822 code_n = NGX_CHECK_HTTP_5XX;
1823 } else {
1824 code_n = NGX_CHECK_HTTP_ERR;
1825 }
1826
1827 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1828 "fastcgi http_parse: code_n: %ui, conf: %ui",
1829 code_n, ucscf->code.status_alive);
1830
1831 if (code_n & ucscf->code.status_alive) {
1832 return NGX_OK;
1833 } else {
1834 return NGX_ERROR;
1835 }
1836
1837 }
1838
1839 return NGX_OK;
1840 }
1841
1842
1843 static ngx_int_t
ngx_http_upstream_check_parse_fastcgi_status(ngx_http_upstream_check_ctx_t * ctx,ngx_buf_t * b,ngx_http_status_t * status)1844 ngx_http_upstream_check_parse_fastcgi_status(ngx_http_upstream_check_ctx_t *ctx,
1845 ngx_buf_t *b, ngx_http_status_t *status)
1846 {
1847 u_char c, ch, *p, *name_s, *name_e;
1848 ngx_flag_t find;
1849
1850 enum {
1851 sw_start = 0,
1852 sw_name,
1853 sw_space_before_value,
1854 sw_value,
1855 sw_space_after_value,
1856 sw_ignore_line,
1857 sw_almost_done,
1858 sw_header_almost_done
1859 } state;
1860
1861 /* the last '\0' is not needed because string is zero terminated */
1862
1863 static u_char lowcase[] =
1864 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
1865 "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0"
1866 "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
1867 "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0"
1868 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
1869 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
1870 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
1871 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
1872
1873 status->count = 0;
1874 status->code = 0;
1875 find = 0;
1876 name_s = name_e = NULL;
1877 state = sw_start;
1878
1879 for (p = b->pos; p < b->last; p++) {
1880 ch = *p;
1881
1882 switch (state) {
1883
1884 /* first char */
1885 case sw_start:
1886
1887 switch (ch) {
1888 case CR:
1889 state = sw_header_almost_done;
1890 break;
1891 case LF:
1892 goto header_done;
1893 default:
1894 state = sw_name;
1895
1896 c = lowcase[ch];
1897
1898 if (c) {
1899 name_s = p;
1900 break;
1901 }
1902
1903 if (ch == '\0') {
1904 return NGX_ERROR;
1905 }
1906
1907
1908 break;
1909 }
1910
1911 break;
1912
1913 /* header name */
1914 case sw_name:
1915 c = lowcase[ch];
1916
1917 if (c) {
1918 break;
1919 }
1920
1921 if (ch == ':') {
1922 name_e = p;
1923 #if (NGX_DEBUG)
1924 ngx_str_t name;
1925 name.data = name_s;
1926 name.len = name_e - name_s;
1927 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1928 "fastcgi header: %V", &name);
1929 #endif
1930 state = sw_space_before_value;
1931
1932 if (ngx_strncasecmp(name_s, (u_char *) "status",
1933 name_e - name_s)
1934 == 0)
1935 {
1936
1937 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
1938 "find status header");
1939
1940 find = 1;
1941 }
1942
1943 break;
1944 }
1945
1946 if (ch == CR) {
1947 state = sw_almost_done;
1948 break;
1949 }
1950
1951 if (ch == LF) {
1952 goto done;
1953 }
1954
1955 /* IIS may send the duplicate "HTTP/1.1 ..." lines */
1956 if (ch == '\0') {
1957 return NGX_ERROR;
1958 }
1959
1960 break;
1961
1962 /* space* before header value */
1963 case sw_space_before_value:
1964 switch (ch) {
1965 case ' ':
1966 break;
1967 case CR:
1968 state = sw_almost_done;
1969 break;
1970 case LF:
1971 goto done;
1972 case '\0':
1973 return NGX_ERROR;
1974 default:
1975 state = sw_value;
1976 if (find) {
1977 if (ch < '1' || ch > '9') {
1978 return NGX_ERROR;
1979 }
1980
1981 status->code = status->code * 10 + ch - '0';
1982 if (status->count++ != 0) {
1983 return NGX_ERROR;
1984 }
1985 }
1986
1987 break;
1988 }
1989
1990 break;
1991
1992 /* header value */
1993 case sw_value:
1994
1995 if (find) {
1996 if (ch < '0' || ch > '9') {
1997 return NGX_ERROR;
1998 }
1999
2000 status->code = status->code * 10 + ch - '0';
2001
2002 if (++status->count == 3) {
2003 return NGX_DONE;
2004 }
2005 }
2006
2007 switch (ch) {
2008 case ' ':
2009 state = sw_space_after_value;
2010 break;
2011 case CR:
2012 state = sw_almost_done;
2013 break;
2014 case LF:
2015 goto done;
2016 case '\0':
2017 return NGX_ERROR;
2018 }
2019
2020 break;
2021
2022 /* space* before end of header line */
2023 case sw_space_after_value:
2024 switch (ch) {
2025 case ' ':
2026 break;
2027 case CR:
2028 state = sw_almost_done;
2029 break;
2030 case LF:
2031 state = sw_start;
2032 break;
2033 case '\0':
2034 return NGX_ERROR;
2035 default:
2036 state = sw_value;
2037 break;
2038 }
2039 break;
2040
2041 /* ignore header line */
2042 case sw_ignore_line:
2043 switch (ch) {
2044 case LF:
2045 state = sw_start;
2046 break;
2047 default:
2048 break;
2049 }
2050 break;
2051
2052 /* end of header line */
2053 case sw_almost_done:
2054 switch (ch) {
2055 case LF:
2056 goto done;
2057 case CR:
2058 break;
2059 default:
2060 return NGX_ERROR;
2061 }
2062 break;
2063
2064 /* end of header */
2065 case sw_header_almost_done:
2066 switch (ch) {
2067 case LF:
2068 goto header_done;
2069 default:
2070 return NGX_ERROR;
2071 }
2072 }
2073 }
2074
2075 b->pos = p;
2076 ctx->state = state;
2077
2078 return NGX_AGAIN;
2079
2080 done:
2081
2082 b->pos = p + 1;
2083 ctx->state = sw_start;
2084
2085 return NGX_OK;
2086
2087 header_done:
2088
2089 b->pos = p + 1;
2090 ctx->state = sw_start;
2091
2092 return NGX_OK;
2093 }
2094
2095
2096 static ngx_int_t
ngx_http_upstream_check_parse_status_line(ngx_http_upstream_check_ctx_t * ctx,ngx_buf_t * b,ngx_http_status_t * status)2097 ngx_http_upstream_check_parse_status_line(ngx_http_upstream_check_ctx_t *ctx,
2098 ngx_buf_t *b, ngx_http_status_t *status)
2099 {
2100 u_char ch, *p;
2101 enum {
2102 sw_start = 0,
2103 sw_H,
2104 sw_HT,
2105 sw_HTT,
2106 sw_HTTP,
2107 sw_first_major_digit,
2108 sw_major_digit,
2109 sw_first_minor_digit,
2110 sw_minor_digit,
2111 sw_status,
2112 sw_space_after_status,
2113 sw_status_text,
2114 sw_almost_done
2115 } state;
2116
2117 state = ctx->state;
2118
2119 for (p = b->pos; p < b->last; p++) {
2120 ch = *p;
2121
2122 switch (state) {
2123
2124 /* "HTTP/" */
2125 case sw_start:
2126 if (ch != 'H') {
2127 return NGX_ERROR;
2128 }
2129
2130 state = sw_H;
2131 break;
2132
2133 case sw_H:
2134 if (ch != 'T') {
2135 return NGX_ERROR;
2136 }
2137
2138 state = sw_HT;
2139 break;
2140
2141 case sw_HT:
2142 if (ch != 'T') {
2143 return NGX_ERROR;
2144 }
2145
2146 state = sw_HTT;
2147 break;
2148
2149 case sw_HTT:
2150 if (ch != 'P') {
2151 return NGX_ERROR;
2152 }
2153
2154 state = sw_HTTP;
2155 break;
2156
2157 case sw_HTTP:
2158 if (ch != '/') {
2159 return NGX_ERROR;
2160 }
2161
2162 state = sw_first_major_digit;
2163 break;
2164
2165 /* the first digit of major HTTP version */
2166 case sw_first_major_digit:
2167 if (ch < '1' || ch > '9') {
2168 return NGX_ERROR;
2169 }
2170
2171 state = sw_major_digit;
2172 break;
2173
2174 /* the major HTTP version or dot */
2175 case sw_major_digit:
2176 if (ch == '.') {
2177 state = sw_first_minor_digit;
2178 break;
2179 }
2180
2181 if (ch < '0' || ch > '9') {
2182 return NGX_ERROR;
2183 }
2184
2185 break;
2186
2187 /* the first digit of minor HTTP version */
2188 case sw_first_minor_digit:
2189 if (ch < '0' || ch > '9') {
2190 return NGX_ERROR;
2191 }
2192
2193 state = sw_minor_digit;
2194 break;
2195
2196 /* the minor HTTP version or the end of the request line */
2197 case sw_minor_digit:
2198 if (ch == ' ') {
2199 state = sw_status;
2200 break;
2201 }
2202
2203 if (ch < '0' || ch > '9') {
2204 return NGX_ERROR;
2205 }
2206
2207 break;
2208
2209 /* HTTP status code */
2210 case sw_status:
2211 if (ch == ' ') {
2212 break;
2213 }
2214
2215 if (ch < '0' || ch > '9') {
2216 return NGX_ERROR;
2217 }
2218
2219 status->code = status->code * 10 + ch - '0';
2220
2221 if (++status->count == 3) {
2222 state = sw_space_after_status;
2223 status->start = p - 2;
2224 }
2225
2226 break;
2227
2228 /* space or end of line */
2229 case sw_space_after_status:
2230 switch (ch) {
2231 case ' ':
2232 state = sw_status_text;
2233 break;
2234 case '.': /* IIS may send 403.1, 403.2, etc */
2235 state = sw_status_text;
2236 break;
2237 case CR:
2238 state = sw_almost_done;
2239 break;
2240 case LF:
2241 goto done;
2242 default:
2243 return NGX_ERROR;
2244 }
2245 break;
2246
2247 /* any text until end of line */
2248 case sw_status_text:
2249 switch (ch) {
2250 case CR:
2251 state = sw_almost_done;
2252
2253 break;
2254 case LF:
2255 goto done;
2256 }
2257 break;
2258
2259 /* end of status line */
2260 case sw_almost_done:
2261 status->end = p - 1;
2262 if (ch == LF) {
2263 goto done;
2264 } else {
2265 return NGX_ERROR;
2266 }
2267 }
2268 }
2269
2270 b->pos = p;
2271 ctx->state = state;
2272
2273 return NGX_AGAIN;
2274
2275 done:
2276
2277 b->pos = p + 1;
2278
2279 if (status->end == NULL) {
2280 status->end = p;
2281 }
2282
2283 ctx->state = sw_start;
2284
2285 return NGX_OK;
2286 }
2287
2288
2289 static void
ngx_http_upstream_check_http_reinit(ngx_http_upstream_check_peer_t * peer)2290 ngx_http_upstream_check_http_reinit(ngx_http_upstream_check_peer_t *peer)
2291 {
2292 ngx_http_upstream_check_ctx_t *ctx;
2293
2294 ctx = peer->check_data;
2295
2296 ctx->send.pos = ctx->send.start;
2297 ctx->send.last = ctx->send.end;
2298
2299 ctx->recv.pos = ctx->recv.last = ctx->recv.start;
2300
2301 ctx->state = 0;
2302
2303 ngx_memzero(&ctx->status, sizeof(ngx_http_status_t));
2304 }
2305
2306
2307 static ngx_int_t
ngx_http_upstream_check_ssl_hello_init(ngx_http_upstream_check_peer_t * peer)2308 ngx_http_upstream_check_ssl_hello_init(ngx_http_upstream_check_peer_t *peer)
2309 {
2310 ngx_http_upstream_check_ctx_t *ctx;
2311 ngx_http_upstream_check_srv_conf_t *ucscf;
2312
2313 ctx = peer->check_data;
2314 ucscf = peer->conf;
2315
2316 ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
2317 ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
2318
2319 ctx->recv.start = ctx->recv.pos = NULL;
2320 ctx->recv.end = ctx->recv.last = NULL;
2321
2322 return NGX_OK;
2323 }
2324
2325
2326 /* a rough check of server ssl_hello responses */
2327 static ngx_int_t
ngx_http_upstream_check_ssl_hello_parse(ngx_http_upstream_check_peer_t * peer)2328 ngx_http_upstream_check_ssl_hello_parse(ngx_http_upstream_check_peer_t *peer)
2329 {
2330 size_t size;
2331 ngx_ssl_server_hello_t *resp;
2332 ngx_http_upstream_check_ctx_t *ctx;
2333
2334 ctx = peer->check_data;
2335
2336 size = ctx->recv.last - ctx->recv.pos;
2337 if (size < sizeof(ngx_ssl_server_hello_t)) {
2338 return NGX_AGAIN;
2339 }
2340
2341 resp = (ngx_ssl_server_hello_t *) ctx->recv.pos;
2342
2343 ngx_log_debug7(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
2344 "http check ssl_parse, type: %ud, version: %ud.%ud, "
2345 "length: %ud, handshanke_type: %ud, hello_version: %ud.%ud",
2346 resp->msg_type, resp->version.major, resp->version.minor,
2347 ntohs(resp->length), resp->handshake_type,
2348 resp->hello_version.major, resp->hello_version.minor);
2349
2350 if (resp->msg_type != NGX_SSL_HANDSHAKE) {
2351 return NGX_ERROR;
2352 }
2353
2354 if (resp->handshake_type != NGX_SSL_SERVER_HELLO) {
2355 return NGX_ERROR;
2356 }
2357
2358 return NGX_OK;
2359 }
2360
2361
2362 static void
ngx_http_upstream_check_ssl_hello_reinit(ngx_http_upstream_check_peer_t * peer)2363 ngx_http_upstream_check_ssl_hello_reinit(ngx_http_upstream_check_peer_t *peer)
2364 {
2365 ngx_http_upstream_check_ctx_t *ctx;
2366
2367 ctx = peer->check_data;
2368
2369 ctx->send.pos = ctx->send.start;
2370 ctx->send.last = ctx->send.end;
2371
2372 ctx->recv.pos = ctx->recv.last = ctx->recv.start;
2373 }
2374
2375
2376 static ngx_int_t
ngx_http_upstream_check_mysql_init(ngx_http_upstream_check_peer_t * peer)2377 ngx_http_upstream_check_mysql_init(ngx_http_upstream_check_peer_t *peer)
2378 {
2379 ngx_http_upstream_check_ctx_t *ctx;
2380 ngx_http_upstream_check_srv_conf_t *ucscf;
2381
2382 ctx = peer->check_data;
2383 ucscf = peer->conf;
2384
2385 ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
2386 ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
2387
2388 ctx->recv.start = ctx->recv.pos = NULL;
2389 ctx->recv.end = ctx->recv.last = NULL;
2390
2391 return NGX_OK;
2392 }
2393
2394
2395 /* a rough check of mysql greeting responses */
2396 static ngx_int_t
ngx_http_upstream_check_mysql_parse(ngx_http_upstream_check_peer_t * peer)2397 ngx_http_upstream_check_mysql_parse(ngx_http_upstream_check_peer_t *peer)
2398 {
2399 size_t size;
2400 ngx_mysql_handshake_init_t *handshake;
2401 ngx_http_upstream_check_ctx_t *ctx;
2402
2403 ctx = peer->check_data;
2404
2405 size = ctx->recv.last - ctx->recv.pos;
2406 if (size < sizeof(ngx_mysql_handshake_init_t)) {
2407 return NGX_AGAIN;
2408 }
2409
2410 handshake = (ngx_mysql_handshake_init_t *) ctx->recv.pos;
2411
2412 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
2413 "mysql_parse: packet_number=%ud, protocol=%ud, server=%s",
2414 handshake->packet_number, handshake->protocol_version,
2415 handshake->others);
2416
2417 /* The mysql greeting packet's serial number always begins with 0. */
2418 if (handshake->packet_number != 0x00) {
2419 return NGX_ERROR;
2420 }
2421
2422 return NGX_OK;
2423 }
2424
2425
2426 static void
ngx_http_upstream_check_mysql_reinit(ngx_http_upstream_check_peer_t * peer)2427 ngx_http_upstream_check_mysql_reinit(ngx_http_upstream_check_peer_t *peer)
2428 {
2429 ngx_http_upstream_check_ctx_t *ctx;
2430
2431 ctx = peer->check_data;
2432
2433 ctx->send.pos = ctx->send.start;
2434 ctx->send.last = ctx->send.end;
2435
2436 ctx->recv.pos = ctx->recv.last = ctx->recv.start;
2437 }
2438
2439
2440 static ngx_int_t
ngx_http_upstream_check_ajp_init(ngx_http_upstream_check_peer_t * peer)2441 ngx_http_upstream_check_ajp_init(ngx_http_upstream_check_peer_t *peer)
2442 {
2443 ngx_http_upstream_check_ctx_t *ctx;
2444 ngx_http_upstream_check_srv_conf_t *ucscf;
2445
2446 ctx = peer->check_data;
2447 ucscf = peer->conf;
2448
2449 ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;
2450 ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;
2451
2452 ctx->recv.start = ctx->recv.pos = NULL;
2453 ctx->recv.end = ctx->recv.last = NULL;
2454
2455 return NGX_OK;
2456 }
2457
2458
2459 static ngx_int_t
ngx_http_upstream_check_ajp_parse(ngx_http_upstream_check_peer_t * peer)2460 ngx_http_upstream_check_ajp_parse(ngx_http_upstream_check_peer_t *peer)
2461 {
2462 size_t size;
2463 u_char *p;
2464 ngx_http_upstream_check_ctx_t *ctx;
2465
2466 ctx = peer->check_data;
2467
2468 size = ctx->recv.last - ctx->recv.pos;
2469 if (size < sizeof(ngx_ajp_cpong_packet)) {
2470 return NGX_AGAIN;
2471 }
2472
2473 p = ctx->recv.pos;
2474
2475 #if (NGX_DEBUG)
2476 {
2477 ngx_ajp_raw_packet_t *ajp;
2478
2479 ajp = (ngx_ajp_raw_packet_t *) p;
2480 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
2481 "ajp_parse: preamble=0x%uxd, length=0x%uxd, type=0x%uxd",
2482 ntohs(ajp->preamble), ntohs(ajp->length), ajp->type);
2483 }
2484 #endif
2485
2486 if (ngx_memcmp(ngx_ajp_cpong_packet, p, sizeof(ngx_ajp_cpong_packet)) == 0)
2487 {
2488 return NGX_OK;
2489 } else {
2490 return NGX_ERROR;
2491 }
2492 }
2493
2494
2495 static void
ngx_http_upstream_check_ajp_reinit(ngx_http_upstream_check_peer_t * peer)2496 ngx_http_upstream_check_ajp_reinit(ngx_http_upstream_check_peer_t *peer)
2497 {
2498 ngx_http_upstream_check_ctx_t *ctx;
2499
2500 ctx = peer->check_data;
2501
2502 ctx->send.pos = ctx->send.start;
2503 ctx->send.last = ctx->send.end;
2504
2505 ctx->recv.pos = ctx->recv.last = ctx->recv.start;
2506 }
2507
2508
2509 static void
ngx_http_upstream_check_status_update(ngx_http_upstream_check_peer_t * peer,ngx_int_t result)2510 ngx_http_upstream_check_status_update(ngx_http_upstream_check_peer_t *peer,
2511 ngx_int_t result)
2512 {
2513 ngx_http_upstream_check_srv_conf_t *ucscf;
2514
2515 ucscf = peer->conf;
2516
2517 if (result) {
2518 peer->shm->rise_count++;
2519 peer->shm->fall_count = 0;
2520 if (peer->shm->down && peer->shm->rise_count >= ucscf->rise_count) {
2521 peer->shm->down = 0;
2522 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
2523 "enable check peer: %V ",
2524 &peer->check_peer_addr->name);
2525 }
2526 } else {
2527 peer->shm->rise_count = 0;
2528 peer->shm->fall_count++;
2529 if (!peer->shm->down && peer->shm->fall_count >= ucscf->fall_count) {
2530 peer->shm->down = 1;
2531 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
2532 "disable check peer: %V ",
2533 &peer->check_peer_addr->name);
2534 }
2535 }
2536
2537 peer->shm->access_time = ngx_current_msec;
2538 }
2539
2540
2541 static void
ngx_http_upstream_check_clean_event(ngx_http_upstream_check_peer_t * peer)2542 ngx_http_upstream_check_clean_event(ngx_http_upstream_check_peer_t *peer)
2543 {
2544 ngx_connection_t *c;
2545 ngx_http_upstream_check_srv_conf_t *ucscf;
2546 ngx_check_conf_t *cf;
2547
2548 c = peer->pc.connection;
2549 ucscf = peer->conf;
2550 cf = ucscf->check_type_conf;
2551
2552 if (c) {
2553 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
2554 "http check clean event: index:%i, fd: %d",
2555 peer->index, c->fd);
2556 if (c->error == 0 &&
2557 cf->need_keepalive &&
2558 (c->requests < ucscf->check_keepalive_requests))
2559 {
2560 c->write->handler = ngx_http_upstream_check_dummy_handler;
2561 c->read->handler = ngx_http_upstream_check_discard_handler;
2562 } else {
2563 ngx_close_connection(c);
2564 peer->pc.connection = NULL;
2565 }
2566 }
2567
2568 if (peer->check_timeout_ev.timer_set) {
2569 ngx_del_timer(&peer->check_timeout_ev);
2570 }
2571
2572 peer->state = NGX_HTTP_CHECK_ALL_DONE;
2573
2574 if (peer->check_data != NULL && peer->reinit) {
2575 peer->reinit(peer);
2576 }
2577
2578 peer->shm->owner = NGX_INVALID_PID;
2579 }
2580
2581
2582 static void
ngx_http_upstream_check_timeout_handler(ngx_event_t * event)2583 ngx_http_upstream_check_timeout_handler(ngx_event_t *event)
2584 {
2585 ngx_http_upstream_check_peer_t *peer;
2586
2587 if (ngx_http_upstream_check_need_exit()) {
2588 return;
2589 }
2590
2591 peer = event->data;
2592 peer->pc.connection->error = 1;
2593
2594 ngx_log_error(NGX_LOG_ERR, event->log, 0,
2595 "check time out with peer: %V ",
2596 &peer->check_peer_addr->name);
2597
2598 ngx_http_upstream_check_status_update(peer, 0);
2599 ngx_http_upstream_check_clean_event(peer);
2600 }
2601
2602
2603 static void
ngx_http_upstream_check_finish_handler(ngx_event_t * event)2604 ngx_http_upstream_check_finish_handler(ngx_event_t *event)
2605 {
2606 if (ngx_http_upstream_check_need_exit()) {
2607 return;
2608 }
2609 }
2610
2611
2612 static ngx_int_t
ngx_http_upstream_check_need_exit()2613 ngx_http_upstream_check_need_exit()
2614 {
2615 if (ngx_terminate || ngx_exiting || ngx_quit) {
2616 ngx_http_upstream_check_clear_all_events();
2617 return 1;
2618 }
2619
2620 return 0;
2621 }
2622
2623
2624 static void
ngx_http_upstream_check_clear_all_events()2625 ngx_http_upstream_check_clear_all_events()
2626 {
2627 ngx_uint_t i;
2628 ngx_connection_t *c;
2629 ngx_http_upstream_check_peer_t *peer;
2630 ngx_http_upstream_check_peers_t *peers;
2631
2632 static ngx_flag_t has_cleared = 0;
2633
2634 if (has_cleared || check_peers_ctx == NULL) {
2635 return;
2636 }
2637
2638 ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
2639 "clear all the events on %P ", ngx_pid);
2640
2641 has_cleared = 1;
2642
2643 peers = check_peers_ctx;
2644
2645 peer = peers->peers.elts;
2646 for (i = 0; i < peers->peers.nelts; i++) {
2647
2648 if (peer[i].check_ev.timer_set) {
2649 ngx_del_timer(&peer[i].check_ev);
2650 }
2651
2652 if (peer[i].check_timeout_ev.timer_set) {
2653 ngx_del_timer(&peer[i].check_timeout_ev);
2654 }
2655
2656 c = peer[i].pc.connection;
2657 if (c) {
2658 ngx_close_connection(c);
2659 peer[i].pc.connection = NULL;
2660 }
2661
2662 if (peer[i].pool != NULL) {
2663 ngx_destroy_pool(peer[i].pool);
2664 peer[i].pool = NULL;
2665 }
2666 }
2667 }
2668
2669
2670 static ngx_int_t
ngx_http_upstream_check_status_handler(ngx_http_request_t * r)2671 ngx_http_upstream_check_status_handler(ngx_http_request_t *r)
2672 {
2673 size_t buffer_size;
2674 ngx_int_t rc;
2675 ngx_buf_t *b;
2676 ngx_chain_t out;
2677 ngx_http_upstream_check_peers_t *peers;
2678 ngx_http_upstream_check_loc_conf_t *uclcf;
2679 ngx_http_upstream_check_status_ctx_t *ctx;
2680
2681 if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
2682 return NGX_HTTP_NOT_ALLOWED;
2683 }
2684
2685 rc = ngx_http_discard_request_body(r);
2686
2687 if (rc != NGX_OK) {
2688 return rc;
2689 }
2690
2691 uclcf = ngx_http_get_module_loc_conf(r, ngx_http_upstream_check_module);
2692
2693 ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_check_status_ctx_t));
2694 if (ctx == NULL) {
2695 return NGX_HTTP_INTERNAL_SERVER_ERROR;
2696 }
2697
2698 ngx_http_upstream_check_status_parse_args(r, ctx);
2699
2700 if (ctx->format == NULL) {
2701 ctx->format = uclcf->format;
2702 }
2703
2704 r->headers_out.content_type = ctx->format->content_type;
2705
2706 if (r->method == NGX_HTTP_HEAD) {
2707 r->headers_out.status = NGX_HTTP_OK;
2708
2709 rc = ngx_http_send_header(r);
2710
2711 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
2712 return rc;
2713 }
2714 }
2715
2716 peers = check_peers_ctx;
2717 if (peers == NULL) {
2718 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2719 "http upstream check module can not find any check "
2720 "server, make sure you've added the check servers");
2721
2722 return NGX_HTTP_INTERNAL_SERVER_ERROR;
2723 }
2724
2725 /* 1/4 pagesize for each record */
2726 buffer_size = peers->peers.nelts * ngx_pagesize / 4;
2727 buffer_size = ngx_align(buffer_size, ngx_pagesize) + ngx_pagesize;
2728
2729 b = ngx_create_temp_buf(r->pool, buffer_size);
2730 if (b == NULL) {
2731 return NGX_HTTP_INTERNAL_SERVER_ERROR;
2732 }
2733
2734 out.buf = b;
2735 out.next = NULL;
2736
2737 ctx->format->output(b, peers, ctx->flag);
2738
2739 r->headers_out.status = NGX_HTTP_OK;
2740 r->headers_out.content_length_n = b->last - b->pos;
2741
2742 if (r->headers_out.content_length_n == 0) {
2743 r->header_only = 1;
2744 }
2745
2746 b->last_buf = 1;
2747
2748 rc = ngx_http_send_header(r);
2749
2750 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
2751 return rc;
2752 }
2753
2754 return ngx_http_output_filter(r, &out);
2755 }
2756
2757
2758 static void
ngx_http_upstream_check_status_parse_args(ngx_http_request_t * r,ngx_http_upstream_check_status_ctx_t * ctx)2759 ngx_http_upstream_check_status_parse_args(ngx_http_request_t *r,
2760 ngx_http_upstream_check_status_ctx_t *ctx)
2761 {
2762 ngx_str_t value;
2763 ngx_uint_t i;
2764 ngx_check_status_command_t *command;
2765
2766 if (r->args.len == 0) {
2767 return;
2768 }
2769
2770 for (i = 0; /* void */ ; i++) {
2771
2772 command = &ngx_check_status_commands[i];
2773
2774 if (command->name.len == 0) {
2775 break;
2776 }
2777
2778 if (ngx_http_arg(r, command->name.data, command->name.len, &value)
2779 == NGX_OK) {
2780
2781 if (command->handler(ctx, &value) != NGX_OK) {
2782 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2783 "http upstream check, bad argument: \"%V\"",
2784 &value);
2785 }
2786 }
2787 }
2788
2789 ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,
2790 "http upstream check, flag: \"%ui\"", ctx->flag);
2791 }
2792
2793
2794 static ngx_int_t
ngx_http_upstream_check_status_command_format(ngx_http_upstream_check_status_ctx_t * ctx,ngx_str_t * value)2795 ngx_http_upstream_check_status_command_format(
2796 ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value)
2797 {
2798 ctx->format = ngx_http_get_check_status_format_conf(value);
2799 if (ctx->format == NULL) {
2800 return NGX_ERROR;
2801 }
2802
2803 return NGX_OK;
2804 }
2805
2806
2807 static ngx_int_t
ngx_http_upstream_check_status_command_status(ngx_http_upstream_check_status_ctx_t * ctx,ngx_str_t * value)2808 ngx_http_upstream_check_status_command_status(
2809 ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value)
2810 {
2811 if (value->len == (sizeof("down") - 1)
2812 && ngx_strncasecmp(value->data, (u_char *) "down", value->len) == 0) {
2813
2814 ctx->flag |= NGX_CHECK_STATUS_DOWN;
2815
2816 } else if (value->len == (sizeof("up") - 1)
2817 && ngx_strncasecmp(value->data, (u_char *) "up", value->len)
2818 == 0) {
2819
2820 ctx->flag |= NGX_CHECK_STATUS_UP;
2821
2822 } else {
2823 return NGX_ERROR;
2824 }
2825
2826 return NGX_OK;
2827 }
2828
2829
2830 static void
ngx_http_upstream_check_status_html_format(ngx_buf_t * b,ngx_http_upstream_check_peers_t * peers,ngx_uint_t flag)2831 ngx_http_upstream_check_status_html_format(ngx_buf_t *b,
2832 ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag)
2833 {
2834 ngx_uint_t i, count;
2835 ngx_http_upstream_check_peer_t *peer;
2836
2837 peer = peers->peers.elts;
2838
2839 count = 0;
2840
2841 for (i = 0; i < peers->peers.nelts; i++) {
2842
2843 if (flag & NGX_CHECK_STATUS_DOWN) {
2844
2845 if (!peer[i].shm->down) {
2846 continue;
2847 }
2848
2849 } else if (flag & NGX_CHECK_STATUS_UP) {
2850
2851 if (peer[i].shm->down) {
2852 continue;
2853 }
2854 }
2855
2856 count++;
2857 }
2858
2859 b->last = ngx_snprintf(b->last, b->end - b->last,
2860 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\n"
2861 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
2862 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
2863 "<head>\n"
2864 " <title>Nginx http upstream check status</title>\n"
2865 "</head>\n"
2866 "<body>\n"
2867 "<h1>Nginx http upstream check status</h1>\n"
2868 "<h2>Check upstream server number: %ui, generation: %ui</h2>\n"
2869 "<table style=\"background-color:white\" cellspacing=\"0\" "
2870 " cellpadding=\"3\" border=\"1\">\n"
2871 " <tr bgcolor=\"#C0C0C0\">\n"
2872 " <th>Index</th>\n"
2873 " <th>Upstream</th>\n"
2874 " <th>Name</th>\n"
2875 " <th>Status</th>\n"
2876 " <th>Rise counts</th>\n"
2877 " <th>Fall counts</th>\n"
2878 " <th>Check type</th>\n"
2879 " <th>Check port</th>\n"
2880 " </tr>\n",
2881 count, ngx_http_upstream_check_shm_generation);
2882
2883 for (i = 0; i < peers->peers.nelts; i++) {
2884
2885 if (flag & NGX_CHECK_STATUS_DOWN) {
2886
2887 if (!peer[i].shm->down) {
2888 continue;
2889 }
2890
2891 } else if (flag & NGX_CHECK_STATUS_UP) {
2892
2893 if (peer[i].shm->down) {
2894 continue;
2895 }
2896 }
2897
2898 b->last = ngx_snprintf(b->last, b->end - b->last,
2899 " <tr%s>\n"
2900 " <td>%ui</td>\n"
2901 " <td>%V</td>\n"
2902 " <td>%V</td>\n"
2903 " <td>%s</td>\n"
2904 " <td>%ui</td>\n"
2905 " <td>%ui</td>\n"
2906 " <td>%V</td>\n"
2907 " <td>%ui</td>\n"
2908 " </tr>\n",
2909 peer[i].shm->down ? " bgcolor=\"#FF0000\"" : "",
2910 i,
2911 peer[i].upstream_name,
2912 &peer[i].peer_addr->name,
2913 peer[i].shm->down ? "down" : "up",
2914 peer[i].shm->rise_count,
2915 peer[i].shm->fall_count,
2916 &peer[i].conf->check_type_conf->name,
2917 peer[i].conf->port);
2918 }
2919
2920 b->last = ngx_snprintf(b->last, b->end - b->last,
2921 "</table>\n"
2922 "</body>\n"
2923 "</html>\n");
2924 }
2925
2926
2927 static void
ngx_http_upstream_check_status_csv_format(ngx_buf_t * b,ngx_http_upstream_check_peers_t * peers,ngx_uint_t flag)2928 ngx_http_upstream_check_status_csv_format(ngx_buf_t *b,
2929 ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag)
2930 {
2931 ngx_uint_t i;
2932 ngx_http_upstream_check_peer_t *peer;
2933
2934 peer = peers->peers.elts;
2935 for (i = 0; i < peers->peers.nelts; i++) {
2936
2937 if (flag & NGX_CHECK_STATUS_DOWN) {
2938
2939 if (!peer[i].shm->down) {
2940 continue;
2941 }
2942
2943 } else if (flag & NGX_CHECK_STATUS_UP) {
2944
2945 if (peer[i].shm->down) {
2946 continue;
2947 }
2948 }
2949
2950 b->last = ngx_snprintf(b->last, b->end - b->last,
2951 "%ui,%V,%V,%s,%ui,%ui,%V,%ui\n",
2952 i,
2953 peer[i].upstream_name,
2954 &peer[i].peer_addr->name,
2955 peer[i].shm->down ? "down" : "up",
2956 peer[i].shm->rise_count,
2957 peer[i].shm->fall_count,
2958 &peer[i].conf->check_type_conf->name,
2959 peer[i].conf->port);
2960 }
2961 }
2962
2963
2964 static void
ngx_http_upstream_check_status_json_format(ngx_buf_t * b,ngx_http_upstream_check_peers_t * peers,ngx_uint_t flag)2965 ngx_http_upstream_check_status_json_format(ngx_buf_t *b,
2966 ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag)
2967 {
2968 ngx_uint_t count, i, last;
2969 ngx_http_upstream_check_peer_t *peer;
2970
2971 peer = peers->peers.elts;
2972
2973 count = 0;
2974
2975 for (i = 0; i < peers->peers.nelts; i++) {
2976
2977 if (flag & NGX_CHECK_STATUS_DOWN) {
2978
2979 if (!peer[i].shm->down) {
2980 continue;
2981 }
2982
2983 } else if (flag & NGX_CHECK_STATUS_UP) {
2984
2985 if (peer[i].shm->down) {
2986 continue;
2987 }
2988 }
2989
2990 count++;
2991 }
2992
2993 b->last = ngx_snprintf(b->last, b->end - b->last,
2994 "{\"servers\": {\n"
2995 " \"total\": %ui,\n"
2996 " \"generation\": %ui,\n"
2997 " \"server\": [\n",
2998 count,
2999 ngx_http_upstream_check_shm_generation);
3000
3001 last = peers->peers.nelts - 1;
3002 for (i = 0; i < peers->peers.nelts; i++) {
3003
3004 if (flag & NGX_CHECK_STATUS_DOWN) {
3005
3006 if (!peer[i].shm->down) {
3007 continue;
3008 }
3009
3010 } else if (flag & NGX_CHECK_STATUS_UP) {
3011
3012 if (peer[i].shm->down) {
3013 continue;
3014 }
3015 }
3016
3017 b->last = ngx_snprintf(b->last, b->end - b->last,
3018 " {\"index\": %ui, "
3019 "\"upstream\": \"%V\", "
3020 "\"name\": \"%V\", "
3021 "\"status\": \"%s\", "
3022 "\"rise\": %ui, "
3023 "\"fall\": %ui, "
3024 "\"type\": \"%V\", "
3025 "\"port\": %ui}"
3026 "%s\n",
3027 i,
3028 peer[i].upstream_name,
3029 &peer[i].peer_addr->name,
3030 peer[i].shm->down ? "down" : "up",
3031 peer[i].shm->rise_count,
3032 peer[i].shm->fall_count,
3033 &peer[i].conf->check_type_conf->name,
3034 peer[i].conf->port,
3035 (i == last) ? "" : ",");
3036 }
3037
3038 b->last = ngx_snprintf(b->last, b->end - b->last,
3039 " ]\n");
3040
3041 b->last = ngx_snprintf(b->last, b->end - b->last,
3042 "}}\n");
3043 }
3044
3045
3046 static ngx_check_conf_t *
ngx_http_get_check_type_conf(ngx_str_t * str)3047 ngx_http_get_check_type_conf(ngx_str_t *str)
3048 {
3049 ngx_uint_t i;
3050
3051 for (i = 0; /* void */ ; i++) {
3052
3053 if (ngx_check_types[i].type == 0) {
3054 break;
3055 }
3056
3057 if (str->len != ngx_check_types[i].name.len) {
3058 continue;
3059 }
3060
3061 if (ngx_strncmp(str->data, ngx_check_types[i].name.data,
3062 str->len) == 0)
3063 {
3064 return &ngx_check_types[i];
3065 }
3066 }
3067
3068 return NULL;
3069 }
3070
3071
3072 static char *
ngx_http_upstream_check(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)3073 ngx_http_upstream_check(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3074 {
3075 ngx_str_t *value, s;
3076 ngx_uint_t i, port, rise, fall, default_down;
3077 ngx_msec_t interval, timeout;
3078 ngx_http_upstream_check_srv_conf_t *ucscf;
3079
3080 /* default values */
3081 port = 0;
3082 rise = 2;
3083 fall = 5;
3084 interval = 30000;
3085 timeout = 1000;
3086 default_down = 1;
3087
3088 value = cf->args->elts;
3089
3090 ucscf = ngx_http_conf_get_module_srv_conf(cf,
3091 ngx_http_upstream_check_module);
3092 if (ucscf == NULL) {
3093 return NGX_CONF_ERROR;
3094 }
3095
3096 for (i = 1; i < cf->args->nelts; i++) {
3097
3098 if (ngx_strncmp(value[i].data, "type=", 5) == 0) {
3099 s.len = value[i].len - 5;
3100 s.data = value[i].data + 5;
3101
3102 ucscf->check_type_conf = ngx_http_get_check_type_conf(&s);
3103
3104 if (ucscf->check_type_conf == NULL) {
3105 goto invalid_check_parameter;
3106 }
3107
3108 continue;
3109 }
3110
3111 if (ngx_strncmp(value[i].data, "port=", 5) == 0) {
3112 s.len = value[i].len - 5;
3113 s.data = value[i].data + 5;
3114
3115 port = ngx_atoi(s.data, s.len);
3116 if (port == (ngx_uint_t) NGX_ERROR || port == 0) {
3117 goto invalid_check_parameter;
3118 }
3119
3120 continue;
3121 }
3122
3123 if (ngx_strncmp(value[i].data, "interval=", 9) == 0) {
3124 s.len = value[i].len - 9;
3125 s.data = value[i].data + 9;
3126
3127 interval = ngx_atoi(s.data, s.len);
3128 if (interval == (ngx_msec_t) NGX_ERROR || interval == 0) {
3129 goto invalid_check_parameter;
3130 }
3131
3132 continue;
3133 }
3134
3135 if (ngx_strncmp(value[i].data, "timeout=", 8) == 0) {
3136 s.len = value[i].len - 8;
3137 s.data = value[i].data + 8;
3138
3139 timeout = ngx_atoi(s.data, s.len);
3140 if (timeout == (ngx_msec_t) NGX_ERROR || timeout == 0) {
3141 goto invalid_check_parameter;
3142 }
3143
3144 continue;
3145 }
3146
3147 if (ngx_strncmp(value[i].data, "rise=", 5) == 0) {
3148 s.len = value[i].len - 5;
3149 s.data = value[i].data + 5;
3150
3151 rise = ngx_atoi(s.data, s.len);
3152 if (rise == (ngx_uint_t) NGX_ERROR || rise == 0) {
3153 goto invalid_check_parameter;
3154 }
3155
3156 continue;
3157 }
3158
3159 if (ngx_strncmp(value[i].data, "fall=", 5) == 0) {
3160 s.len = value[i].len - 5;
3161 s.data = value[i].data + 5;
3162
3163 fall = ngx_atoi(s.data, s.len);
3164 if (fall == (ngx_uint_t) NGX_ERROR || fall == 0) {
3165 goto invalid_check_parameter;
3166 }
3167
3168 continue;
3169 }
3170
3171 if (ngx_strncmp(value[i].data, "default_down=", 13) == 0) {
3172 s.len = value[i].len - 13;
3173 s.data = value[i].data + 13;
3174
3175 if (ngx_strcasecmp(s.data, (u_char *) "true") == 0) {
3176 default_down = 1;
3177 } else if (ngx_strcasecmp(s.data, (u_char *) "false") == 0) {
3178 default_down = 0;
3179 } else {
3180 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
3181 "invalid value \"%s\", "
3182 "it must be \"true\" or \"false\"",
3183 value[i].data);
3184 return NGX_CONF_ERROR;
3185 }
3186
3187 continue;
3188 }
3189
3190 goto invalid_check_parameter;
3191 }
3192
3193 ucscf->port = port;
3194 ucscf->check_interval = interval;
3195 ucscf->check_timeout = timeout;
3196 ucscf->fall_count = fall;
3197 ucscf->rise_count = rise;
3198 ucscf->default_down = default_down;
3199
3200 if (ucscf->check_type_conf == NGX_CONF_UNSET_PTR) {
3201 ngx_str_set(&s, "tcp");
3202 ucscf->check_type_conf = ngx_http_get_check_type_conf(&s);
3203 }
3204
3205 return NGX_CONF_OK;
3206
3207 invalid_check_parameter:
3208
3209 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
3210 "invalid parameter \"%V\"", &value[i]);
3211
3212 return NGX_CONF_ERROR;
3213 }
3214
3215
3216 static char *
ngx_http_upstream_check_keepalive_requests(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)3217 ngx_http_upstream_check_keepalive_requests(ngx_conf_t *cf, ngx_command_t *cmd,
3218 void *conf)
3219 {
3220 ngx_str_t *value;
3221 ngx_http_upstream_check_srv_conf_t *ucscf;
3222 ngx_uint_t requests;
3223
3224 value = cf->args->elts;
3225
3226 ucscf = ngx_http_conf_get_module_srv_conf(cf,
3227 ngx_http_upstream_check_module);
3228
3229 requests = ngx_atoi(value[1].data, value[1].len);
3230 if (requests == (ngx_uint_t) NGX_ERROR || requests == 0) {
3231 return "invalid value";
3232 }
3233
3234 ucscf->check_keepalive_requests = requests;
3235
3236 return NGX_CONF_OK;
3237 }
3238
3239
3240 static char *
ngx_http_upstream_check_http_send(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)3241 ngx_http_upstream_check_http_send(ngx_conf_t *cf, ngx_command_t *cmd,
3242 void *conf)
3243 {
3244 ngx_str_t *value;
3245 ngx_http_upstream_check_srv_conf_t *ucscf;
3246
3247 value = cf->args->elts;
3248
3249 ucscf = ngx_http_conf_get_module_srv_conf(cf,
3250 ngx_http_upstream_check_module);
3251
3252 ucscf->send = value[1];
3253
3254 return NGX_CONF_OK;
3255 }
3256
3257
3258 static char *
ngx_http_upstream_check_fastcgi_params(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)3259 ngx_http_upstream_check_fastcgi_params(ngx_conf_t *cf, ngx_command_t *cmd,
3260 void *conf)
3261 {
3262 ngx_str_t *value, *k, *v;
3263 ngx_http_upstream_check_srv_conf_t *ucscf;
3264
3265 value = cf->args->elts;
3266
3267 ucscf = ngx_http_conf_get_module_srv_conf(cf,
3268 ngx_http_upstream_check_module);
3269
3270 k = ngx_array_push(ucscf->fastcgi_params);
3271 if (k == NULL) {
3272 return NGX_CONF_ERROR;
3273 }
3274
3275 v = ngx_array_push(ucscf->fastcgi_params);
3276 if (v == NULL) {
3277 return NGX_CONF_ERROR;
3278 }
3279
3280 *k = value[1];
3281 *v = value[2];
3282
3283 return NGX_CONF_OK;
3284 }
3285
3286
3287 static char *
ngx_http_upstream_check_http_expect_alive(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)3288 ngx_http_upstream_check_http_expect_alive(ngx_conf_t *cf, ngx_command_t *cmd,
3289 void *conf)
3290 {
3291 ngx_str_t *value;
3292 ngx_uint_t bit, i, m;
3293 ngx_conf_bitmask_t *mask;
3294 ngx_http_upstream_check_srv_conf_t *ucscf;
3295
3296 value = cf->args->elts;
3297 mask = ngx_check_http_expect_alive_masks;
3298
3299 ucscf = ngx_http_conf_get_module_srv_conf(cf,
3300 ngx_http_upstream_check_module);
3301 bit = ucscf->code.status_alive;
3302
3303 for (i = 1; i < cf->args->nelts; i++) {
3304 for (m = 0; mask[m].name.len != 0; m++) {
3305
3306 if (mask[m].name.len != value[i].len
3307 || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)
3308 {
3309 continue;
3310 }
3311
3312 if (bit & mask[m].mask) {
3313 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
3314 "duplicate value \"%s\"", value[i].data);
3315
3316 } else {
3317 bit |= mask[m].mask;
3318 }
3319
3320 break;
3321 }
3322
3323 if (mask[m].name.len == 0) {
3324 ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
3325 "invalid value \"%s\"", value[i].data);
3326
3327 return NGX_CONF_ERROR;
3328 }
3329 }
3330
3331 ucscf->code.status_alive = bit;
3332
3333 return NGX_CONF_OK;
3334 }
3335
3336
3337 static char *
ngx_http_upstream_check_shm_size(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)3338 ngx_http_upstream_check_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3339 {
3340 ngx_str_t *value;
3341 ngx_http_upstream_check_main_conf_t *ucmcf;
3342
3343 ucmcf = ngx_http_conf_get_module_main_conf(cf,
3344 ngx_http_upstream_check_module);
3345 if (ucmcf->check_shm_size) {
3346 return "is duplicate";
3347 }
3348
3349 value = cf->args->elts;
3350
3351 ucmcf->check_shm_size = ngx_parse_size(&value[1]);
3352 if (ucmcf->check_shm_size == (size_t) NGX_ERROR) {
3353 return "invalid value";
3354 }
3355
3356 return NGX_CONF_OK;
3357 }
3358
3359
3360 static ngx_check_status_conf_t *
ngx_http_get_check_status_format_conf(ngx_str_t * str)3361 ngx_http_get_check_status_format_conf(ngx_str_t *str)
3362 {
3363 ngx_uint_t i;
3364
3365 for (i = 0; /* void */ ; i++) {
3366
3367 if (ngx_check_status_formats[i].format.len == 0) {
3368 break;
3369 }
3370
3371 if (str->len != ngx_check_status_formats[i].format.len) {
3372 continue;
3373 }
3374
3375 if (ngx_strncmp(str->data, ngx_check_status_formats[i].format.data,
3376 str->len) == 0)
3377 {
3378 return &ngx_check_status_formats[i];
3379 }
3380 }
3381
3382 return NULL;
3383 }
3384
3385
3386 static char *
ngx_http_upstream_check_status(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)3387 ngx_http_upstream_check_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
3388 {
3389 ngx_str_t *value;
3390 ngx_http_core_loc_conf_t *clcf;
3391 ngx_http_upstream_check_loc_conf_t *uclcf;
3392
3393 value = cf->args->elts;
3394
3395 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
3396
3397 clcf->handler = ngx_http_upstream_check_status_handler;
3398
3399 if (cf->args->nelts == 2) {
3400 uclcf = ngx_http_conf_get_module_loc_conf(cf,
3401 ngx_http_upstream_check_module);
3402
3403 uclcf->format = ngx_http_get_check_status_format_conf(&value[1]);
3404 if (uclcf->format == NULL) {
3405 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
3406 "invalid check format \"%V\"", &value[1]);
3407
3408 return NGX_CONF_ERROR;
3409 }
3410 }
3411
3412 return NGX_CONF_OK;
3413 }
3414
3415
3416 static void *
ngx_http_upstream_check_create_main_conf(ngx_conf_t * cf)3417 ngx_http_upstream_check_create_main_conf(ngx_conf_t *cf)
3418 {
3419 ngx_http_upstream_check_main_conf_t *ucmcf;
3420
3421 ucmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_main_conf_t));
3422 if (ucmcf == NULL) {
3423 return NULL;
3424 }
3425
3426 ucmcf->peers = ngx_pcalloc(cf->pool,
3427 sizeof(ngx_http_upstream_check_peers_t));
3428 if (ucmcf->peers == NULL) {
3429 return NULL;
3430 }
3431
3432 ucmcf->peers->checksum = 0;
3433
3434 if (ngx_array_init(&ucmcf->peers->peers, cf->pool, 16,
3435 sizeof(ngx_http_upstream_check_peer_t)) != NGX_OK)
3436 {
3437 return NULL;
3438 }
3439
3440 return ucmcf;
3441 }
3442
3443
3444 static ngx_buf_t *
ngx_http_upstream_check_create_fastcgi_request(ngx_pool_t * pool,ngx_str_t * params,ngx_uint_t num)3445 ngx_http_upstream_check_create_fastcgi_request(ngx_pool_t *pool,
3446 ngx_str_t *params, ngx_uint_t num)
3447 {
3448 size_t size, len, padding;
3449 ngx_buf_t *b;
3450 ngx_str_t *k, *v;
3451 ngx_uint_t i, j;
3452 ngx_http_fastcgi_header_t *h;
3453
3454 len = 0;
3455 for (i = 0, j = 0; i < num; i++, j = i * 2) {
3456 k = ¶ms[j];
3457 v = ¶ms[j + 1];
3458
3459 len += 1 + k->len + ((v->len > 127) ? 4 : 1) + v->len;
3460 }
3461
3462 padding = 8 - len % 8;
3463 padding = (padding == 8) ? 0 : padding;
3464
3465 size = sizeof(ngx_http_fastcgi_header_t)
3466 + sizeof(ngx_http_fastcgi_begin_request_t)
3467
3468 + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
3469 + len + padding
3470 + sizeof(ngx_http_fastcgi_header_t) /* NGX_HTTP_FASTCGI_PARAMS */
3471
3472 + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */
3473
3474
3475 b = ngx_create_temp_buf(pool, size);
3476 if (b == NULL) {
3477 return NULL;
3478 }
3479
3480 ngx_http_fastcgi_request_start.br.flags = 0;
3481
3482 ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,
3483 sizeof(ngx_http_fastcgi_request_start_t));
3484
3485 h = (ngx_http_fastcgi_header_t *)
3486 (b->pos + sizeof(ngx_http_fastcgi_header_t)
3487 + sizeof(ngx_http_fastcgi_begin_request_t));
3488
3489 h->content_length_hi = (u_char) ((len >> 8) & 0xff);
3490 h->content_length_lo = (u_char) (len & 0xff);
3491 h->padding_length = (u_char) padding;
3492 h->reserved = 0;
3493
3494 b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)
3495 + sizeof(ngx_http_fastcgi_begin_request_t)
3496 + sizeof(ngx_http_fastcgi_header_t);
3497
3498 for (i = 0, j = 0; i < num; i++, j = i * 2) {
3499 k = ¶ms[j];
3500 v = ¶ms[j + 1];
3501
3502 if (k->len > 127) {
3503 *b->last++ = (u_char) (((k->len >> 24) & 0x7f) | 0x80);
3504 *b->last++ = (u_char) ((k->len >> 16) & 0xff);
3505 *b->last++ = (u_char) ((k->len >> 8) & 0xff);
3506 *b->last++ = (u_char) (k->len & 0xff);
3507
3508 } else {
3509 *b->last++ = (u_char) k->len;
3510 }
3511
3512 if (v->len > 127) {
3513 *b->last++ = (u_char) (((v->len >> 24) & 0x7f) | 0x80);
3514 *b->last++ = (u_char) ((v->len >> 16) & 0xff);
3515 *b->last++ = (u_char) ((v->len >> 8) & 0xff);
3516 *b->last++ = (u_char) (v->len & 0xff);
3517
3518 } else {
3519 *b->last++ = (u_char) v->len;
3520 }
3521
3522 b->last = ngx_copy(b->last, k->data, k->len);
3523 b->last = ngx_copy(b->last, v->data, v->len);
3524 }
3525
3526 if (padding) {
3527 ngx_memzero(b->last, padding);
3528 b->last += padding;
3529 }
3530
3531 h = (ngx_http_fastcgi_header_t *) b->last;
3532 b->last += sizeof(ngx_http_fastcgi_header_t);
3533
3534 h->version = 1;
3535 h->type = NGX_HTTP_FASTCGI_PARAMS;
3536 h->request_id_hi = 0;
3537 h->request_id_lo = 1;
3538 h->content_length_hi = 0;
3539 h->content_length_lo = 0;
3540 h->padding_length = 0;
3541 h->reserved = 0;
3542
3543 h = (ngx_http_fastcgi_header_t *) b->last;
3544 b->last += sizeof(ngx_http_fastcgi_header_t);
3545
3546 return b;
3547 }
3548
3549
3550 static char *
ngx_http_upstream_check_init_main_conf(ngx_conf_t * cf,void * conf)3551 ngx_http_upstream_check_init_main_conf(ngx_conf_t *cf, void *conf)
3552 {
3553 ngx_buf_t *b;
3554 ngx_uint_t i;
3555 ngx_http_upstream_srv_conf_t **uscfp;
3556 ngx_http_upstream_main_conf_t *umcf;
3557
3558 umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
3559
3560 b = ngx_http_upstream_check_create_fastcgi_request(cf->pool,
3561 fastcgi_default_params,
3562 sizeof(fastcgi_default_params) / sizeof(ngx_str_t) / 2);
3563
3564 if (b == NULL) {
3565 return NGX_CONF_ERROR;
3566 }
3567
3568 fastcgi_default_request.data = b->pos;
3569 fastcgi_default_request.len = b->last - b->pos;
3570
3571 uscfp = umcf->upstreams.elts;
3572
3573 for (i = 0; i < umcf->upstreams.nelts; i++) {
3574
3575 if (ngx_http_upstream_check_init_srv_conf(cf, uscfp[i]) != NGX_OK) {
3576 return NGX_CONF_ERROR;
3577 }
3578 }
3579
3580 return ngx_http_upstream_check_init_shm(cf, conf);
3581 }
3582
3583
3584 static void *
ngx_http_upstream_check_create_srv_conf(ngx_conf_t * cf)3585 ngx_http_upstream_check_create_srv_conf(ngx_conf_t *cf)
3586 {
3587 ngx_http_upstream_check_srv_conf_t *ucscf;
3588
3589 ucscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_srv_conf_t));
3590 if (ucscf == NULL) {
3591 return NULL;
3592 }
3593
3594 ucscf->fastcgi_params = ngx_array_create(cf->pool, 2 * 4, sizeof(ngx_str_t));
3595 if (ucscf->fastcgi_params == NULL) {
3596 return NULL;
3597 }
3598
3599 ucscf->port = NGX_CONF_UNSET_UINT;
3600 ucscf->fall_count = NGX_CONF_UNSET_UINT;
3601 ucscf->rise_count = NGX_CONF_UNSET_UINT;
3602 ucscf->check_timeout = NGX_CONF_UNSET_MSEC;
3603 ucscf->check_keepalive_requests = NGX_CONF_UNSET_UINT;
3604 ucscf->check_type_conf = NGX_CONF_UNSET_PTR;
3605
3606 return ucscf;
3607 }
3608
3609
3610 static void *
ngx_http_upstream_check_create_loc_conf(ngx_conf_t * cf)3611 ngx_http_upstream_check_create_loc_conf(ngx_conf_t *cf)
3612 {
3613 ngx_http_upstream_check_loc_conf_t *uclcf;
3614
3615 uclcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_loc_conf_t));
3616 if (uclcf == NULL) {
3617 return NULL;
3618 }
3619
3620 uclcf->format = NGX_CONF_UNSET_PTR;
3621
3622 return uclcf;
3623 }
3624
3625
3626 static char *
ngx_http_upstream_check_init_srv_conf(ngx_conf_t * cf,void * conf)3627 ngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf)
3628 {
3629 ngx_str_t s;
3630 ngx_buf_t *b;
3631 ngx_check_conf_t *check;
3632 ngx_http_upstream_srv_conf_t *us = conf;
3633 ngx_http_upstream_check_srv_conf_t *ucscf;
3634
3635 if (us->srv_conf == NULL) {
3636 return NGX_CONF_OK;
3637 }
3638
3639 ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);
3640
3641 if (ucscf->port == NGX_CONF_UNSET_UINT) {
3642 ucscf->port = 0;
3643 }
3644
3645 if (ucscf->fall_count == NGX_CONF_UNSET_UINT) {
3646 ucscf->fall_count = 2;
3647 }
3648
3649 if (ucscf->rise_count == NGX_CONF_UNSET_UINT) {
3650 ucscf->rise_count = 5;
3651 }
3652
3653 if (ucscf->check_interval == NGX_CONF_UNSET_MSEC) {
3654 ucscf->check_interval = 0;
3655 }
3656
3657 if (ucscf->check_timeout == NGX_CONF_UNSET_MSEC) {
3658 ucscf->check_timeout = 1000;
3659 }
3660
3661 if (ucscf->check_keepalive_requests == NGX_CONF_UNSET_UINT) {
3662 ucscf->check_keepalive_requests = 1;
3663 }
3664
3665 if (ucscf->check_type_conf == NGX_CONF_UNSET_PTR) {
3666 ucscf->check_type_conf = NULL;
3667 }
3668
3669 check = ucscf->check_type_conf;
3670
3671 if (check) {
3672 if (ucscf->send.len == 0) {
3673 ngx_str_set(&s, "fastcgi");
3674
3675 if (check == ngx_http_get_check_type_conf(&s)) {
3676
3677 if (ucscf->fastcgi_params->nelts == 0) {
3678 ucscf->send.data = fastcgi_default_request.data;
3679 ucscf->send.len = fastcgi_default_request.len;
3680
3681 } else {
3682 b = ngx_http_upstream_check_create_fastcgi_request(
3683 cf->pool, ucscf->fastcgi_params->elts,
3684 ucscf->fastcgi_params->nelts / 2);
3685 if (b == NULL) {
3686 return NGX_CONF_ERROR;
3687 }
3688
3689 ucscf->send.data = b->pos;
3690 ucscf->send.len = b->last - b->pos;
3691 }
3692 } else {
3693 ucscf->send.data = check->default_send.data;
3694 ucscf->send.len = check->default_send.len;
3695 }
3696 }
3697
3698
3699 if (ucscf->code.status_alive == 0) {
3700 ucscf->code.status_alive = check->default_status_alive;
3701 }
3702 }
3703
3704 return NGX_CONF_OK;
3705 }
3706
3707
3708 static char *
ngx_http_upstream_check_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)3709 ngx_http_upstream_check_merge_loc_conf(ngx_conf_t *cf, void *parent,
3710 void *child)
3711 {
3712 ngx_str_t format = ngx_string("html");
3713 ngx_http_upstream_check_loc_conf_t *prev = parent;
3714 ngx_http_upstream_check_loc_conf_t *conf = child;
3715
3716 ngx_conf_merge_ptr_value(conf->format, prev->format,
3717 ngx_http_get_check_status_format_conf(&format));
3718
3719 return NGX_CONF_OK;
3720 }
3721
3722
3723 static char *
ngx_http_upstream_check_init_shm(ngx_conf_t * cf,void * conf)3724 ngx_http_upstream_check_init_shm(ngx_conf_t *cf, void *conf)
3725 {
3726 ngx_str_t *shm_name;
3727 ngx_uint_t shm_size;
3728 ngx_shm_zone_t *shm_zone;
3729 ngx_http_upstream_check_main_conf_t *ucmcf = conf;
3730
3731 if (ucmcf->peers->peers.nelts > 0) {
3732
3733 ngx_http_upstream_check_shm_generation++;
3734
3735 shm_name = &ucmcf->peers->check_shm_name;
3736
3737 ngx_http_upstream_check_get_shm_name(shm_name, cf->pool,
3738 ngx_http_upstream_check_shm_generation);
3739
3740 /* The default check shared memory size is 1M */
3741 shm_size = 1 * 1024 * 1024;
3742
3743 shm_size = shm_size < ucmcf->check_shm_size ?
3744 ucmcf->check_shm_size : shm_size;
3745
3746 shm_zone = ngx_shared_memory_add(cf, shm_name, shm_size,
3747 &ngx_http_upstream_check_module);
3748
3749 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0,
3750 "http upstream check, upsteam:%V, shm_zone size:%ui",
3751 shm_name, shm_size);
3752
3753 shm_zone->data = cf->pool;
3754 check_peers_ctx = ucmcf->peers;
3755
3756 shm_zone->init = ngx_http_upstream_check_init_shm_zone;
3757 }
3758 else {
3759 check_peers_ctx = NULL;
3760 }
3761
3762 return NGX_CONF_OK;
3763 }
3764
3765
3766 static ngx_int_t
ngx_http_upstream_check_get_shm_name(ngx_str_t * shm_name,ngx_pool_t * pool,ngx_uint_t generation)3767 ngx_http_upstream_check_get_shm_name(ngx_str_t *shm_name, ngx_pool_t *pool,
3768 ngx_uint_t generation)
3769 {
3770 u_char *last;
3771
3772 shm_name->data = ngx_palloc(pool, SHM_NAME_LEN);
3773 if (shm_name->data == NULL) {
3774 return NGX_ERROR;
3775 }
3776
3777 last = ngx_snprintf(shm_name->data, SHM_NAME_LEN, "%s#%ui",
3778 "ngx_http_upstream_check", generation);
3779
3780 shm_name->len = last - shm_name->data;
3781
3782 return NGX_OK;
3783 }
3784
3785
3786 static ngx_int_t
ngx_http_upstream_check_init_shm_zone(ngx_shm_zone_t * shm_zone,void * data)3787 ngx_http_upstream_check_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)
3788 {
3789 size_t size;
3790 ngx_str_t oshm_name;
3791 ngx_int_t rc;
3792 ngx_uint_t i, same, number;
3793 ngx_pool_t *pool;
3794 ngx_shm_zone_t *oshm_zone;
3795 ngx_slab_pool_t *shpool;
3796 ngx_http_upstream_check_peer_t *peer;
3797 ngx_http_upstream_check_peers_t *peers;
3798 ngx_http_upstream_check_srv_conf_t *ucscf;
3799 ngx_http_upstream_check_peer_shm_t *peer_shm, *opeer_shm;
3800 ngx_http_upstream_check_peers_shm_t *peers_shm, *opeers_shm;
3801
3802 opeers_shm = NULL;
3803 peers_shm = NULL;
3804 ngx_str_null(&oshm_name);
3805
3806 same = 0;
3807 peers = check_peers_ctx;
3808 if (peers == NULL) {
3809 return NGX_OK;
3810 }
3811
3812 number = peers->peers.nelts;
3813 if (number == 0) {
3814 return NGX_OK;
3815 }
3816
3817 pool = shm_zone->data;
3818 if (pool == NULL) {
3819 pool = ngx_cycle->pool;
3820 }
3821
3822 shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
3823
3824 if (data) {
3825 opeers_shm = data;
3826
3827 if ((opeers_shm->number == number)
3828 && (opeers_shm->checksum == peers->checksum)) {
3829
3830 peers_shm = data;
3831 same = 1;
3832 }
3833 }
3834
3835 if (!same) {
3836
3837 if (ngx_http_upstream_check_shm_generation > 1) {
3838
3839 ngx_http_upstream_check_get_shm_name(&oshm_name,
3840 pool, ngx_http_upstream_check_shm_generation - 1);
3841
3842 /* The global variable ngx_cycle still points to the old one */
3843 oshm_zone = ngx_shared_memory_find((ngx_cycle_t *) ngx_cycle,
3844 &oshm_name,
3845 &ngx_http_upstream_check_module);
3846
3847 if (oshm_zone) {
3848 opeers_shm = oshm_zone->data;
3849
3850 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, shm_zone->shm.log, 0,
3851 "http upstream check, find oshm_zone:%p, "
3852 "opeers_shm: %p",
3853 oshm_zone, opeers_shm);
3854 }
3855 }
3856
3857 size = sizeof(*peers_shm) +
3858 (number - 1) * sizeof(ngx_http_upstream_check_peer_shm_t);
3859
3860 peers_shm = ngx_slab_alloc(shpool, size);
3861
3862 if (peers_shm == NULL) {
3863 goto failure;
3864 }
3865
3866 ngx_memzero(peers_shm, size);
3867 }
3868
3869 peers_shm->generation = ngx_http_upstream_check_shm_generation;
3870 peers_shm->checksum = peers->checksum;
3871 peers_shm->number = number;
3872
3873 peer = peers->peers.elts;
3874
3875 for (i = 0; i < number; i++) {
3876
3877 peer_shm = &peers_shm->peers[i];
3878
3879 /*
3880 * This function may be triggered before the old stale
3881 * work process exits. The owner may stick to the old
3882 * pid.
3883 */
3884 peer_shm->owner = NGX_INVALID_PID;
3885
3886 if (same) {
3887 continue;
3888 }
3889
3890 peer_shm->socklen = peer[i].peer_addr->socklen;
3891 peer_shm->sockaddr = ngx_slab_alloc(shpool, peer_shm->socklen);
3892 if (peer_shm->sockaddr == NULL) {
3893 goto failure;
3894 }
3895
3896 ngx_memcpy(peer_shm->sockaddr, peer[i].peer_addr->sockaddr,
3897 peer_shm->socklen);
3898
3899 if (opeers_shm) {
3900
3901 opeer_shm = ngx_http_upstream_check_find_shm_peer(opeers_shm,
3902 peer[i].peer_addr);
3903 if (opeer_shm) {
3904 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, shm_zone->shm.log, 0,
3905 "http upstream check, inherit opeer: %V ",
3906 &peer[i].peer_addr->name);
3907
3908 rc = ngx_http_upstream_check_init_shm_peer(peer_shm, opeer_shm,
3909 0, pool, &peer[i].peer_addr->name);
3910 if (rc != NGX_OK) {
3911 return NGX_ERROR;
3912 }
3913
3914 continue;
3915 }
3916 }
3917
3918 ucscf = peer[i].conf;
3919 rc = ngx_http_upstream_check_init_shm_peer(peer_shm, NULL,
3920 ucscf->default_down, pool,
3921 &peer[i].peer_addr->name);
3922 if (rc != NGX_OK) {
3923 return NGX_ERROR;
3924 }
3925 }
3926
3927 peers->peers_shm = peers_shm;
3928 shm_zone->data = peers_shm;
3929
3930 return NGX_OK;
3931
3932 failure:
3933 ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,
3934 "http upstream check_shm_size is too small, "
3935 "you should specify a larger size.");
3936 return NGX_ERROR;
3937 }
3938
3939
3940 static ngx_shm_zone_t *
ngx_shared_memory_find(ngx_cycle_t * cycle,ngx_str_t * name,void * tag)3941 ngx_shared_memory_find(ngx_cycle_t *cycle, ngx_str_t *name, void *tag)
3942 {
3943 ngx_uint_t i;
3944 ngx_shm_zone_t *shm_zone;
3945 ngx_list_part_t *part;
3946
3947 part = (ngx_list_part_t *) &(cycle->shared_memory.part);
3948 shm_zone = part->elts;
3949
3950 for (i = 0; /* void */ ; i++) {
3951
3952 if (i >= part->nelts) {
3953 if (part->next == NULL) {
3954 break;
3955 }
3956 part = part->next;
3957 shm_zone = part->elts;
3958 i = 0;
3959 }
3960
3961 if (name->len != shm_zone[i].shm.name.len) {
3962 continue;
3963 }
3964
3965 if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len) != 0)
3966 {
3967 continue;
3968 }
3969
3970 if (tag != shm_zone[i].tag) {
3971 continue;
3972 }
3973
3974 return &shm_zone[i];
3975 }
3976
3977 return NULL;
3978 }
3979
3980
3981 static ngx_http_upstream_check_peer_shm_t *
ngx_http_upstream_check_find_shm_peer(ngx_http_upstream_check_peers_shm_t * p,ngx_addr_t * addr)3982 ngx_http_upstream_check_find_shm_peer(ngx_http_upstream_check_peers_shm_t *p,
3983 ngx_addr_t *addr)
3984 {
3985 ngx_uint_t i;
3986 ngx_http_upstream_check_peer_shm_t *peer_shm;
3987
3988 for (i = 0; i < p->number; i++) {
3989
3990 peer_shm = &p->peers[i];
3991
3992 if (addr->socklen != peer_shm->socklen) {
3993 continue;
3994 }
3995
3996 if (ngx_memcmp(addr->sockaddr, peer_shm->sockaddr, addr->socklen) == 0) {
3997 return peer_shm;
3998 }
3999 }
4000
4001 return NULL;
4002 }
4003
4004
4005 static ngx_int_t
ngx_http_upstream_check_init_shm_peer(ngx_http_upstream_check_peer_shm_t * psh,ngx_http_upstream_check_peer_shm_t * opsh,ngx_uint_t init_down,ngx_pool_t * pool,ngx_str_t * name)4006 ngx_http_upstream_check_init_shm_peer(ngx_http_upstream_check_peer_shm_t *psh,
4007 ngx_http_upstream_check_peer_shm_t *opsh, ngx_uint_t init_down,
4008 ngx_pool_t *pool, ngx_str_t *name)
4009 {
4010 u_char *file;
4011
4012 if (opsh) {
4013 psh->access_time = opsh->access_time;
4014 psh->access_count = opsh->access_count;
4015
4016 psh->fall_count = opsh->fall_count;
4017 psh->rise_count = opsh->rise_count;
4018 psh->busyness = opsh->busyness;
4019
4020 psh->down = opsh->down;
4021
4022 } else {
4023 psh->access_time = 0;
4024 psh->access_count = 0;
4025
4026 psh->fall_count = 0;
4027 psh->rise_count = 0;
4028 psh->busyness = 0;
4029
4030 psh->down = init_down;
4031 }
4032
4033 #if (NGX_HAVE_ATOMIC_OPS)
4034
4035 file = NULL;
4036
4037 #else
4038
4039 file = ngx_pnalloc(pool, ngx_cycle->lock_file.len + name->len);
4040 if (file == NULL) {
4041 return NGX_ERROR;
4042 }
4043
4044 (void) ngx_sprintf(file, "%V%V%Z", &ngx_cycle->lock_file, name);
4045
4046 #endif
4047
4048 #if (nginx_version >= 1002000)
4049 if (ngx_shmtx_create(&psh->mutex, &psh->lock, file) != NGX_OK) {
4050 #else
4051 if (ngx_shmtx_create(&psh->mutex, (void *) &psh->lock, file) != NGX_OK) {
4052 #endif
4053 return NGX_ERROR;
4054 }
4055
4056 return NGX_OK;
4057 }
4058
4059
4060 static ngx_int_t
4061 ngx_http_upstream_check_init_process(ngx_cycle_t *cycle)
4062 {
4063 ngx_http_upstream_check_main_conf_t *ucmcf;
4064
4065 ucmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_check_module);
4066 if (ucmcf == NULL) {
4067 return NGX_OK;
4068 }
4069
4070 return ngx_http_upstream_check_add_timers(cycle);
4071 }
4072