forked from chronolaw/annotated_nginx
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ngx_http_request.c
4837 lines (3718 loc) · 142 KB
/
ngx_http_request.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// annotated by chrono since 2016
//
// * ngx_http_init_connection
// * ngx_http_create_request
// * ngx_http_wait_request_handler
// * ngx_http_process_request_line
// * ngx_http_find_virtual_server
// * ngx_http_process_request_headers
// * ngx_http_request_handler
// * ngx_http_run_posted_requests
//
// * ngx_http_set_write_handler
// * ngx_http_writer
//
// * ngx_http_ssl_handshake
//
// * ngx_http_log_request
// * ngx_http_free_request
// * ngx_http_close_connection
// * ngx_http_close_request
// * ngx_http_finalize_connection
// * ngx_http_finalize_request
/*
* Copyright (C) Igor Sysoev
* Copyright (C) Nginx, Inc.
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
// 接受连接后,读事件加入epoll,当socket有数据可读时就调用
// 因为是事件触发,可能会被多次调用,即重入
// 处理读事件,读取请求头
static void ngx_http_wait_request_handler(ngx_event_t *ev);
// 1.15.9新函数,专门创建请求结构体
static ngx_http_request_t *ngx_http_alloc_request(ngx_connection_t *c);
// 调用recv读取数据,解析出请求行信息,存在r->header_in里
// 如果头太大,或者配置的太小,nginx会再多分配内存
// 这里用无限循环,保证读取完数据
// again说明客户端发送的数据不足,会继续读取,error则结束请求
// 请求行处理完毕设置读事件处理函数为ngx_http_process_request_headers
static void ngx_http_process_request_line(ngx_event_t *rev);
// 解析请求行之后的请求头数据
// 处理逻辑与ngx_http_process_request_line类似,也是无限循环,保证读取完数据
// 如果头太大,或者配置的太小,nginx会再多分配内存
// 检查收到的http请求头:content_length不能是非数字,不支持trace方法,设置keep_alive头信息
// 最后调用ngx_http_process_request
// again说明客户端发送的数据不足,会继续读取,error则结束请求
static void ngx_http_process_request_headers(ngx_event_t *rev);
// 调用recv读数据,存在r->header_in里
// 如果暂时无数据就加入定时器等待,加入读事件
// 下次读事件发生还会进入这里继续读取
// 返回读取的字节数量
static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
// 为接收http头数据分配一个大的缓冲区,拷贝已经接收的数据
// 使用了hc->busy/free等成员
static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,
ngx_uint_t request_line);
// 使用offset设置headers_in里的请求头
static ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
// 使用offset设置headers_in里的请求头,但不允许重复
// 如Content-Length/If-Modified-Since
static ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
// 处理请求头里的host
static ngx_int_t ngx_http_process_host(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
// 检查头里的user_agent,设置ie/chrome/safari标志位
static ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
// 简单验证host字符串的合法性
static ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,
ngx_uint_t alloc);
// 由请求行或请求头里的host定位server{}块位置,决定进入哪个server
// 核心是ngx_http_find_virtual_server
static ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,
ngx_str_t *host);
// 查找匹配的server{}块
// 先在hash表里找完全匹配
// hash找不到用正则匹配
static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,
ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,
ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);
// http请求处理时的事件handler
// 当读取完请求头后读写事件的handler都是它
// 通常写事件就是ngx_http_core_run_phases引擎数组处理请求
static void ngx_http_request_handler(ngx_event_t *ev);
// 释放主请求相关的资源,调用cleanup链表,相当于析构
// 如果主请求有多线程任务阻塞,那么不能结束请求
// 否则调用ngx_http_close_request尝试关闭请求,引用计数减1
static void ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc);
// 设置为主请求的write_event_handler
// 强制令引用计数为1,必须关闭
// 调用ngx_http_close_request
static void ngx_http_terminate_handler(ngx_http_request_t *r);
// 检查请求相关的异步事件,尝试关闭请求
//
// 有多个引用计数,表示有其他异步事件在处理
// 那么就不能真正结束请求
// 调用ngx_http_close_request尝试关闭请求,引用计数减1
// r->main->count == 1,可以结束请求
// 如果正在读取请求体,那么设置标志位,要求延后读取数据关闭
// 如果进程正在运行,没有退出,且请求要求keepalive
// 那么调用ngx_http_set_keepalive而不是关闭请求
// 不keepalive,也不延后关闭,那么就真正关闭
// 尝试关闭请求,引用计数减1,表示本操作完成
static void ngx_http_finalize_connection(ngx_http_request_t *r);
// 设置发送数据的handler,即写事件的回调handler为write_event_handler
// 不限速,需要加入发送超时,即send_timeout时间内socket不可写则报错
// 使用send_lowat设置epoll写事件
// 只有内核socket缓冲区有send_lowat的空间才会触发写事件
// 当可写时真正的向客户端发送数据,调用send_chain
// 如果数据发送不完,就保存在r->out里,返回again,需要再次发生可写事件才能发送
// 不是last、flush,且数据量较小(默认1460)
// 那么就不真正调用write发送,减少系统调用的次数,提高性能
static ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);
// 写事件handler是ngx_http_writer
// 检查写事件是否已经超时
// delayed表示限速,如果不限速那么就结束请求
// 调用过滤链表发送数据
// 有数据被缓存,没有完全发送
// 加上超时等待,注册写事件,等socket可写再发送
static void ngx_http_writer(ngx_http_request_t *r);
static void ngx_http_request_finalizer(ngx_http_request_t *r);
// 代替关闭连接的动作,保持连接
// 释放请求相关的资源,调用cleanup链表,相当于析构
// 但连接的内存池还在,可以用于长连接继续使用
// 关注读事件,等待客户端发送数据
// rev->handler = ngx_http_keepalive_handler;
static void ngx_http_set_keepalive(ngx_http_request_t *r);
static void ngx_http_keepalive_handler(ngx_event_t *ev);
// 计算延后关闭的时间,添加超时
// 设置读事件处理函数为ngx_http_lingering_close_handler
// 如果此时有数据可读那么直接调用ngx_http_lingering_close_handler
static void ngx_http_set_lingering_close(ngx_connection_t *c);
// 超时直接关闭连接
// 否则读取数据,但并不处理,使用固定的buffer
// 返回again,无数据可读,需要继续等待
static void ngx_http_lingering_close_handler(ngx_event_t *ev);
static ngx_int_t ngx_http_post_action(ngx_http_request_t *r);
// 尝试关闭请求,引用计数减1,表示本操作完成
// 如果还有引用计数,意味着此请求还有关联的epoll事件未完成
// 不能关闭,直接返回
// 引用计数为0,没有任何操作了,可以安全关闭
// 释放请求相关的资源,调用cleanup链表,相当于析构
// 此时请求已经结束,调用log模块记录日志
// 销毁请求的内存池
// 调用ngx_close_connection,释放连接,加入空闲链表,可以再次使用
// 最后销毁连接的内存池
static void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);
// 请求已经结束,调用log模块记录日志
// 在ngx_http_free_request里调用
// log handler不在引擎数组里
// 不检查handler的返回值,直接调用,不使用checker
static void ngx_http_log_request(ngx_http_request_t *r);
// 记录错误日志时由log对象调用的函数,增加http请求的专有信息
static u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len);
static u_char *ngx_http_log_error_handler(ngx_http_request_t *r,
ngx_http_request_t *sr, u_char *buf, size_t len);
#if (NGX_HTTP_SSL)
static void ngx_http_ssl_handshake(ngx_event_t *rev);
static void ngx_http_ssl_handshake_handler(ngx_connection_t *c);
#endif
static char *ngx_http_client_errors[] = {
/* NGX_HTTP_PARSE_INVALID_METHOD */
"client sent invalid method",
/* NGX_HTTP_PARSE_INVALID_REQUEST */
"client sent invalid request",
/* NGX_HTTP_PARSE_INVALID_VERSION */
"client sent invalid version",
/* NGX_HTTP_PARSE_INVALID_09_METHOD */
"client sent invalid method in HTTP/0.9 request"
};
// 使用字符串映射操作函数,填充headers_in
// 在ngx_http_init_headers_in_hash构造为散列表,提高查找效率
ngx_http_header_t ngx_http_headers_in[] = {
{ ngx_string("Host"), offsetof(ngx_http_headers_in_t, host),
ngx_http_process_host },
{ ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection),
ngx_http_process_connection },
{ ngx_string("If-Modified-Since"),
offsetof(ngx_http_headers_in_t, if_modified_since),
ngx_http_process_unique_header_line },
{ ngx_string("If-Unmodified-Since"),
offsetof(ngx_http_headers_in_t, if_unmodified_since),
ngx_http_process_unique_header_line },
{ ngx_string("If-Match"),
offsetof(ngx_http_headers_in_t, if_match),
ngx_http_process_unique_header_line },
{ ngx_string("If-None-Match"),
offsetof(ngx_http_headers_in_t, if_none_match),
ngx_http_process_unique_header_line },
{ ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent),
ngx_http_process_user_agent },
{ ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer),
ngx_http_process_header_line },
{ ngx_string("Content-Length"),
offsetof(ngx_http_headers_in_t, content_length),
ngx_http_process_unique_header_line },
{ ngx_string("Content-Range"),
offsetof(ngx_http_headers_in_t, content_range),
ngx_http_process_unique_header_line },
{ ngx_string("Content-Type"),
offsetof(ngx_http_headers_in_t, content_type),
ngx_http_process_header_line },
{ ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),
ngx_http_process_header_line },
{ ngx_string("If-Range"),
offsetof(ngx_http_headers_in_t, if_range),
ngx_http_process_unique_header_line },
{ ngx_string("Transfer-Encoding"),
offsetof(ngx_http_headers_in_t, transfer_encoding),
ngx_http_process_unique_header_line },
{ ngx_string("TE"),
offsetof(ngx_http_headers_in_t, te),
ngx_http_process_header_line },
{ ngx_string("Expect"),
offsetof(ngx_http_headers_in_t, expect),
ngx_http_process_unique_header_line },
{ ngx_string("Upgrade"),
offsetof(ngx_http_headers_in_t, upgrade),
ngx_http_process_header_line },
#if (NGX_HTTP_GZIP || NGX_HTTP_HEADERS)
{ ngx_string("Accept-Encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding),
ngx_http_process_header_line },
{ ngx_string("Via"), offsetof(ngx_http_headers_in_t, via),
ngx_http_process_header_line },
#endif
{ ngx_string("Authorization"),
offsetof(ngx_http_headers_in_t, authorization),
ngx_http_process_unique_header_line },
{ ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive),
ngx_http_process_header_line },
#if (NGX_HTTP_X_FORWARDED_FOR)
{ ngx_string("X-Forwarded-For"),
offsetof(ngx_http_headers_in_t, x_forwarded_for),
ngx_http_process_header_line },
#endif
#if (NGX_HTTP_REALIP)
{ ngx_string("X-Real-IP"),
offsetof(ngx_http_headers_in_t, x_real_ip),
ngx_http_process_header_line },
#endif
#if (NGX_HTTP_HEADERS)
{ ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept),
ngx_http_process_header_line },
{ ngx_string("Accept-Language"),
offsetof(ngx_http_headers_in_t, accept_language),
ngx_http_process_header_line },
#endif
#if (NGX_HTTP_DAV)
{ ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth),
ngx_http_process_header_line },
{ ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination),
ngx_http_process_header_line },
{ ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite),
ngx_http_process_header_line },
{ ngx_string("Date"), offsetof(ngx_http_headers_in_t, date),
ngx_http_process_header_line },
#endif
{ ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookie),
ngx_http_process_header_line },
{ ngx_null_string, 0, NULL }
};
// 当epoll检测到连接事件,会调用event_accept,最后会调用此函数,开始处理http请求
// 在ngx_http_optimize_servers->ngx_http_add_listening里设置有连接发生时的回调函数
// 调用发生在ngx_event_accept.c:ngx_event_accept()
// 把读事件加入epoll,当socket有数据可读时就调用ngx_http_wait_request_handler
void
ngx_http_init_connection(ngx_connection_t *c)
{
ngx_uint_t i;
ngx_event_t *rev;
struct sockaddr_in *sin;
ngx_http_port_t *port;
ngx_http_in_addr_t *addr;
ngx_http_log_ctx_t *ctx;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
#if (NGX_HAVE_INET6)
struct sockaddr_in6 *sin6;
ngx_http_in6_addr_t *addr6;
#endif
// 建立连接时server{}里相关的信息
// 重要的是conf_ctx,server的配置数组
// 准备初始化hc
hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));
if (hc == NULL) {
ngx_http_close_connection(c);
return;
}
// 之后在create_request里使用
c->data = hc;
/* find the server configuration for the address:port */
// 取监听同一端口的server信息
port = c->listening->servers;
// 一个端口对应多个地址的情况
if (port->naddrs > 1) {
/*
* there are several addresses on this port and one of them
* is an "*:port" wildcard so getsockname() in ngx_http_server_addr()
* is required to determine a server address
*/
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
switch (c->local_sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
addr6 = port->addrs;
/* the last address is "*" */
for (i = 0; i < port->naddrs - 1; i++) {
if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {
break;
}
}
hc->addr_conf = &addr6[i].conf;
break;
#endif
default: /* AF_INET */
sin = (struct sockaddr_in *) c->local_sockaddr;
addr = port->addrs;
/* the last address is "*" */
for (i = 0; i < port->naddrs - 1; i++) {
if (addr[i].addr == sin->sin_addr.s_addr) {
break;
}
}
hc->addr_conf = &addr[i].conf;
break;
}
} else {
// 唯一监听端口的server
switch (c->local_sockaddr->sa_family) {
#if (NGX_HAVE_INET6)
case AF_INET6:
addr6 = port->addrs;
hc->addr_conf = &addr6[0].conf;
break;
#endif
default: /* AF_INET */
addr = port->addrs;
hc->addr_conf = &addr[0].conf;
break;
}
}
/* the default server configuration for the address:port */
// addr_conf->default_server->ctx就是端口所在的server的配置数组
hc->conf_ctx = hc->addr_conf->default_server->ctx;
// http log相关的信息
ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
if (ctx == NULL) {
ngx_http_close_connection(c);
return;
}
ctx->connection = c;
ctx->request = NULL;
ctx->current_request = NULL;
c->log->connection = c->number;
c->log->handler = ngx_http_log_error;
c->log->data = ctx;
c->log->action = "waiting for request";
c->log_error = NGX_ERROR_INFO;
// 连接的读事件,此时是已经发生连接,即将读数据
rev = c->read;
// 处理读事件,读取请求头
// 设置了读事件的handler,可读时就会调用ngx_http_wait_request_handler
rev->handler = ngx_http_wait_request_handler;
// 暂时不处理写事件
c->write->handler = ngx_http_empty_handler;
// http2使用特殊的读事件处理函数ngx_http_v2_init
#if (NGX_HTTP_V2)
if (hc->addr_conf->http2) {
rev->handler = ngx_http_v2_init;
}
#endif
// ssl连接使用特殊的读事件处理函数ngx_http_ssl_handshake
#if (NGX_HTTP_SSL)
{
ngx_http_ssl_srv_conf_t *sscf;
sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);
// sscf->enable对应指令ssl on,通常不使用
// hc->addr_conf->ssl对应listen xxx ssl
if (sscf->enable || hc->addr_conf->ssl) {
// 1.15.0不要求必须指定证书
hc->ssl = 1;
c->log->action = "SSL handshaking";
// ssl连接使用特殊的读事件处理函数ngx_http_ssl_handshake
// 进入ssl握手处理,而不是直接读取http头
rev->handler = ngx_http_ssl_handshake;
}
}
#endif
// listen指令配置了代理协议,需要额外处理
if (hc->addr_conf->proxy_protocol) {
hc->proxy_protocol = 1;
c->log->action = "reading PROXY protocol";
}
// 通常此时读事件都是ready=0,只有iocp或者使用了deferred才是ready
// ngx_event_accept里设置
// 为了提高nginx的性能,减少epoll调用,应该设置deferred
if (rev->ready) {
/* the deferred accept(), iocp */
if (ngx_use_accept_mutex) {
// 如果是负载均衡,那么加入延后处理队列
// 尽快释放锁,方便其他进程再接受请求
// 会在ngx_event_process_posted里处理
ngx_post_event(rev, &ngx_posted_events);
return;
}
// 否则直接处理请求,即调用ngx_http_wait_request_handler
rev->handler(rev);
return;
}
cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
// 虽然建立了连接,但暂时没有数据可读,ready=0
// 加一个超时事件,等待读事件发生
ngx_add_timer(rev, cscf->client_header_timeout);
// 连接加入cycle的复用队列ngx_cycle->reusable_connections_queue
ngx_reusable_connection(c, 1);
// 把读事件加入epoll,当socket有数据可读时就调用ngx_http_wait_request_handler
// 因为事件加入了定时器,超时时也会调用ngx_http_wait_request_handler
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
// 调用ngx_close_connection
// 释放连接,加入空闲链表,可以再次使用
// 销毁连接的内存池
ngx_http_close_connection(c);
return;
}
}
// 接受连接后,读事件加入epoll,当socket有数据可读时就调用
// 因为是事件触发,可能会被多次调用,即重入
// 处理读事件,读取请求头
static void
ngx_http_wait_request_handler(ngx_event_t *rev)
{
u_char *p;
size_t size;
ssize_t n;
ngx_buf_t *b;
ngx_connection_t *c;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
// 从事件的data获得连接对象
c = rev->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http wait request handler");
// 首先检查超时
// 由定时器超时引发的,由ngx_event_expire_timers调用
// 超时客户端没有发送数据,关闭连接
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
// 调用ngx_close_connection
// 释放连接,加入空闲链表,可以再次使用
// 销毁连接的内存池
ngx_http_close_connection(c);
return;
}
// 没有超时,检查连接是否被关闭了
if (c->close) {
// 调用ngx_close_connection
// 释放连接,加入空闲链表,可以再次使用
// 销毁连接的内存池
ngx_http_close_connection(c);
return;
}
// 连接对象里获取配置数组, 在ngx_http_init_connection里设置的
// 重要的是conf_ctx,server的配置数组
hc = c->data;
cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
// 配置的头缓冲区大小,默认1k
// 如果头太大,或者配置的太小
// nginx会再多分配内存,保证读取完数据
size = cscf->client_header_buffer_size;
// 这个缓冲区是给连接对象用的
b = c->buffer;
// 如果还没有创建缓冲区则创建
// 第一次调用是没有,之后再调用就有了
if (b == NULL) {
b = ngx_create_temp_buf(c->pool, size);
if (b == NULL) {
ngx_http_close_connection(c);
return;
}
c->buffer = b;
// 虽然已经有了buf结构,但没有关联的内存空间
// 之前因为again被释放了,所以要重新分配内存
// 见后面的recv判断
} else if (b->start == NULL) {
b->start = ngx_palloc(c->pool, size);
if (b->start == NULL) {
ngx_http_close_connection(c);
return;
}
// 这里pos==last==start,表示一个空的缓冲区
b->pos = b->start;
b->last = b->start;
b->end = b->last + size;
}
// 调用接收函数,b->last是缓冲区的末位置,前面可能有数据
// ngx_event_accept.c:ngx_event_accept()里设置为ngx_recv
// ngx_posix_init.c里初始化为linux的底层接口
// <0 出错, =0 连接关闭, >0 接收到数据大小
n = c->recv(c, b->last, size);
// 如果返回NGX_AGAIN表示还没有数据
// 就要再次加定时器防止超时,然后epoll等待下一次的读事件发生
if (n == NGX_AGAIN) {
// 没设置超时就再来一次
if (!rev->timer_set) {
ngx_add_timer(rev, cscf->client_header_timeout);
ngx_reusable_connection(c, 1);
}
// 把读事件加入epoll,当socket有数据可读时就调用ngx_http_wait_request_handler
// 因为事件加入了定时器,超时时也会调用ngx_http_wait_request_handler
if (ngx_handle_read_event(rev, 0) != NGX_OK) {
ngx_http_close_connection(c);
return;
}
/*
* We are trying to not hold c->buffer's memory for an idle connection.
*/
// 释放缓冲区,避免空闲连接占用内存
// 这样,即使有大量的无数据连接,也不会占用很多的内存
// 只有连接对象的内存消耗
if (ngx_pfree(c->pool, b->start) == NGX_OK) {
b->start = NULL;
}
// 读事件处理完成,因为没读到数据,等待下一次事件发生
return;
}
// 读数据出错了,直接关闭连接
if (n == NGX_ERROR) {
ngx_http_close_connection(c);
return;
}
// 读到了0字节,即连接被客户端关闭,client abort
if (n == 0) {
ngx_log_error(NGX_LOG_INFO, c->log, 0,
"client closed connection");
ngx_http_close_connection(c);
return;
}
// 真正读取了n个字节
b->last += n;
// listen指令是否使用了proxy_protocol参数
if (hc->proxy_protocol) {
// 清除标志位
hc->proxy_protocol = 0;
// 读取proxy_protocol定义的信息
// 早期只支持版本1的文本形式
// 见http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
p = ngx_proxy_protocol_read(c, b->pos, b->last);
if (p == NULL) {
ngx_http_close_connection(c);
return;
}
// 消费已经读取的proxy protocol数据
b->pos = p;
// 如果缓冲区空,那么等待下次读事件
// 否则后续的是正常的http头
if (b->pos == b->last) {
c->log->action = "waiting for request";
b->pos = b->start;
b->last = b->start;
ngx_post_event(rev, &ngx_posted_events);
return;
}
}
// http日志的额外信息
c->log->action = "reading client request line";
// 参数reusable表示是否可以复用,即加入队列
// 因为此时连接已经在使用,故不能复用
ngx_reusable_connection(c, 0);
// 创建ngx_http_request_t对象,准备开始真正的处理请求
c->data = ngx_http_create_request(c);
if (c->data == NULL) {
ngx_http_close_connection(c);
return;
}
// 读事件的handler改变,变成ngx_http_process_request_line
// 之后再有数据来就换成ngx_http_process_request_line
rev->handler = ngx_http_process_request_line;
// 必须马上执行ngx_http_process_request_line
// 否则因为et模式的特性,将无法再获得此事件
ngx_http_process_request_line(rev);
}
// 创建ngx_http_request_t对象,准备开始真正的处理请求
// 连接对象里获取配置数组, 在ngx_http_init_connection里设置的
// 创建请求内存池,创建请求对象
// 为所有http模块分配存储ctx数据的空间,即一个大数组
// 为所有变量创建数组
ngx_http_request_t *
ngx_http_create_request(ngx_connection_t *c)
{
ngx_http_request_t *r;
ngx_http_log_ctx_t *ctx;
ngx_http_core_loc_conf_t *clcf;
r = ngx_http_alloc_request(c);
if (r == NULL) {
return NULL;
}
// 处理的请求次数,在ngx_http_create_request里增加
// 用来控制长连接里可处理的请求次数,指令keepalive_requests
// requests字段仅在http处理时有用
c->requests++;
// 取当前location的配置
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
ngx_set_connection_log(c, clcf->error_log);
// 日志的ctx
// 在记录错误日志时使用
ctx = c->log->data;
ctx->request = r;
ctx->current_request = r;
// 在共享内存里增加计数器
#if (NGX_STAT_STUB)
(void) ngx_atomic_fetch_add(ngx_stat_reading, 1);
r->stat_reading = 1;
(void) ngx_atomic_fetch_add(ngx_stat_requests, 1);
#endif
return r;
}
// 1.15.9新函数,专门创建请求结构体
static ngx_http_request_t *
ngx_http_alloc_request(ngx_connection_t *c)
{
ngx_pool_t *pool;
ngx_time_t *tp;
ngx_http_request_t *r;
ngx_http_connection_t *hc;
ngx_http_core_srv_conf_t *cscf;
ngx_http_core_main_conf_t *cmcf;
// 连接对象里获取配置数组, 在ngx_http_init_connection里设置的
// 重要的是conf_ctx,server的配置数组
hc = c->data;
// 获取server{}的配置
cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);
// 请求用的内存池
pool = ngx_create_pool(cscf->request_pool_size, c->log);
if (pool == NULL) {
return NULL;
}
// 在请求内存池里创建请求对象
// 不使用连接的内存池,当请求结束时自动回收
r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));
if (r == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
// 保存请求的内存池
r->pool = pool;
// 请求的连接参数,重要的是conf_ctx,server的配置数组
r->http_connection = hc;
r->signature = NGX_HTTP_MODULE;
// 请求关联的连接对象,里面有log
r->connection = c;
// 请求对应的配置数组
// 此时只是default server的配置信息
// main_conf所有server都只有一个,所以这个不需要再设置
r->main_conf = hc->conf_ctx->main_conf;
// 在读取完请求头后,会在ngx_http_find_virtual_server()里再设置正确的
r->srv_conf = hc->conf_ctx->srv_conf;
r->loc_conf = hc->conf_ctx->loc_conf;
// 设置请求的读处理函数
// 注意这个不是读事件的处理函数!!
r->read_event_handler = ngx_http_block_reading;
// 设置读取缓冲区,暂不深究
r->header_in = hc->busy ? hc->busy->buf : c->buffer;
// 初始化响应头链表
if (ngx_list_init(&r->headers_out.headers, r->pool, 20,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_destroy_pool(r->pool);
return NULL;
}
if (ngx_list_init(&r->headers_out.trailers, r->pool, 4,
sizeof(ngx_table_elt_t))
!= NGX_OK)
{
ngx_destroy_pool(r->pool);
return NULL;
}
// 为所有http模块分配存储ctx数据的空间,即一个大数组
r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);
if (r->ctx == NULL) {
ngx_destroy_pool(r->pool);
return NULL;
}
// 取http core的main配置
cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
// 为所有变量创建数组
// 里面存放的是变量值
r->variables = ngx_pcalloc(r->pool, cmcf->variables.nelts
* sizeof(ngx_http_variable_value_t));
if (r->variables == NULL) {
ngx_destroy_pool(r->pool);
return NULL;
}
#if (NGX_HTTP_SSL)
if (c->ssl && !c->ssl->sendfile) {
r->main_filter_need_in_memory = 1;
}
#endif
// accept的连接是主请求
r->main = r;
// 当前只有一个请求,所有子请求最大调用深度不能超过50
// 在1.8版之前是个数200,1.10之后改变为深度50
r->count = 1;
// 得到当前时间
tp = ngx_timeofday();
// 设置请求开始的时间,限速用
r->start_sec = tp->sec;
r->start_msec = tp->msec;
// 还没有开始解析请求头,方法未知
r->method = NGX_HTTP_UNKNOWN;
// http协议版本号默认是1.0
r->http_version = NGX_HTTP_VERSION_10;
// 初始化请求头、响应头的长度都是未知
r->headers_in.content_length_n = -1;
r->headers_in.keep_alive_n = -1;
r->headers_out.content_length_n = -1;
r->headers_out.last_modified_time = -1;
// uri改写次数限制,最多10次
// 每次rewrite就会减少,到0就不能rewrite,返回错误
r->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1;
// 每个请求最多只能产生50层次调用的子请求
// 在1.8版之前是主请求最多200个
// 1.10之后改变了实现方式,50是子请求的“深度”限制
// 所以产生子请求基本已经没有限制
// 每产生一个子请求,sr->subrequests递减
r->subrequests = NGX_HTTP_MAX_SUBREQUESTS + 1;
// 当前请求的状态,正在读取请求
r->http_state = NGX_HTTP_READING_REQUEST_STATE;
// 在记录错误日志时回调
r->log_handler = ngx_http_log_error_handler;
return r;
}
#if (NGX_HTTP_SSL)
// ssl连接使用特殊的读事件处理函数ngx_http_ssl_handshake
// 进入ssl握手处理,而不是直接读取http头
static void
ngx_http_ssl_handshake(ngx_event_t *rev)
{
u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1];
size_t size;
ssize_t n;
ngx_err_t err;
ngx_int_t rc;
ngx_connection_t *c;
ngx_http_connection_t *hc;
ngx_http_ssl_srv_conf_t *sscf;
ngx_http_core_loc_conf_t *clcf;
ngx_http_core_srv_conf_t *cscf;
// 连接可读,即客户端发来了数据
// 取连接对象
c = rev->data;
// 取配置信息
hc = c->data;
ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
"http check ssl handshake");
// 检查超时
if (rev->timedout) {
ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
ngx_http_close_connection(c);
return;
}
// 连接已经关闭
if (c->close) {
ngx_http_close_connection(c);
return;
}
// 是否代理协议,不是则size=1
size = hc->proxy_protocol ? sizeof(buf) : 1;
// 读取数据
// 通常只读取1个字节,是ssl的版本号
n = recv(c->fd, (char *) buf, size, MSG_PEEK);
err = ngx_socket_errno;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http recv(): %z", n);
// 检查错误码
if (n == -1) {
// again表示数据未准备好