Mercurial > hg > nginx
comparison src/http/v2/ngx_http_v2.c @ 7673:c5840ca2063d
HTTP/2: lingering close after GOAWAY.
After sending the GOAWAY frame, a connection is now closed using
the lingering close mechanism.
This allows for the reliable delivery of the GOAWAY frames, while
also fixing connection resets observed when http2_max_requests is
reached (ticket #1250), or with graceful shutdown (ticket #1544),
when some additional data from the client is received on a fully
closed connection.
For HTTP/2, the settings lingering_close, lingering_timeout, and
lingering_time are taken from the "server" level.
author | Ruslan Ermilov <ru@nginx.com> |
---|---|
date | Fri, 03 Jul 2020 16:16:47 +0300 |
parents | 7114d21bc2b1 |
children | d57f15922ca3 |
comparison
equal
deleted
inserted
replaced
7672:3dcb1aba894a | 7673:c5840ca2063d |
---|---|
58 | 58 |
59 | 59 |
60 static void ngx_http_v2_read_handler(ngx_event_t *rev); | 60 static void ngx_http_v2_read_handler(ngx_event_t *rev); |
61 static void ngx_http_v2_write_handler(ngx_event_t *wev); | 61 static void ngx_http_v2_write_handler(ngx_event_t *wev); |
62 static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c); | 62 static void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c); |
63 static void ngx_http_v2_lingering_close(ngx_http_v2_connection_t *h2c); | |
64 static void ngx_http_v2_lingering_close_handler(ngx_event_t *rev); | |
63 | 65 |
64 static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, | 66 static u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, |
65 u_char *pos, u_char *end); | 67 u_char *pos, u_char *end); |
66 static u_char *ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, | 68 static u_char *ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, |
67 u_char *pos, u_char *end); | 69 u_char *pos, u_char *end); |
659 | 661 |
660 /* rc == NGX_OK */ | 662 /* rc == NGX_OK */ |
661 } | 663 } |
662 | 664 |
663 if (h2c->goaway) { | 665 if (h2c->goaway) { |
664 ngx_http_close_connection(c); | 666 ngx_http_v2_lingering_close(h2c); |
665 return; | 667 return; |
666 } | 668 } |
667 | 669 |
668 h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, | 670 h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx, |
669 ngx_http_v2_module); | 671 ngx_http_v2_module); |
694 if (c->write->timer_set) { | 696 if (c->write->timer_set) { |
695 ngx_del_timer(c->write); | 697 ngx_del_timer(c->write); |
696 } | 698 } |
697 | 699 |
698 ngx_add_timer(c->read, h2scf->idle_timeout); | 700 ngx_add_timer(c->read, h2scf->idle_timeout); |
701 } | |
702 | |
703 | |
704 static void | |
705 ngx_http_v2_lingering_close(ngx_http_v2_connection_t *h2c) | |
706 { | |
707 ngx_event_t *rev, *wev; | |
708 ngx_connection_t *c; | |
709 ngx_http_core_loc_conf_t *clcf; | |
710 | |
711 c = h2c->connection; | |
712 | |
713 clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, | |
714 ngx_http_core_module); | |
715 | |
716 if (clcf->lingering_close == NGX_HTTP_LINGERING_OFF) { | |
717 ngx_http_close_connection(c); | |
718 return; | |
719 } | |
720 | |
721 rev = c->read; | |
722 rev->handler = ngx_http_v2_lingering_close_handler; | |
723 | |
724 h2c->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); | |
725 ngx_add_timer(rev, clcf->lingering_timeout); | |
726 | |
727 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | |
728 ngx_http_close_connection(c); | |
729 return; | |
730 } | |
731 | |
732 wev = c->write; | |
733 wev->handler = ngx_http_empty_handler; | |
734 | |
735 if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) { | |
736 if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) { | |
737 ngx_http_close_connection(c); | |
738 return; | |
739 } | |
740 } | |
741 | |
742 if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) { | |
743 ngx_connection_error(c, ngx_socket_errno, | |
744 ngx_shutdown_socket_n " failed"); | |
745 ngx_http_close_connection(c); | |
746 return; | |
747 } | |
748 | |
749 if (rev->ready) { | |
750 ngx_http_v2_lingering_close_handler(rev); | |
751 } | |
752 } | |
753 | |
754 | |
755 static void | |
756 ngx_http_v2_lingering_close_handler(ngx_event_t *rev) | |
757 { | |
758 ssize_t n; | |
759 ngx_msec_t timer; | |
760 ngx_connection_t *c; | |
761 ngx_http_core_loc_conf_t *clcf; | |
762 ngx_http_v2_connection_t *h2c; | |
763 u_char buffer[NGX_HTTP_LINGERING_BUFFER_SIZE]; | |
764 | |
765 c = rev->data; | |
766 h2c = c->data; | |
767 | |
768 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, | |
769 "http2 lingering close handler"); | |
770 | |
771 if (rev->timedout) { | |
772 ngx_http_close_connection(c); | |
773 return; | |
774 } | |
775 | |
776 timer = (ngx_msec_t) h2c->lingering_time - (ngx_msec_t) ngx_time(); | |
777 if ((ngx_msec_int_t) timer <= 0) { | |
778 ngx_http_close_connection(c); | |
779 return; | |
780 } | |
781 | |
782 do { | |
783 n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE); | |
784 | |
785 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "lingering read: %z", n); | |
786 | |
787 if (n == NGX_ERROR || n == 0) { | |
788 ngx_http_close_connection(c); | |
789 return; | |
790 } | |
791 | |
792 } while (rev->ready); | |
793 | |
794 if (ngx_handle_read_event(rev, 0) != NGX_OK) { | |
795 ngx_http_close_connection(c); | |
796 return; | |
797 } | |
798 | |
799 clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx, | |
800 ngx_http_core_module); | |
801 timer *= 1000; | |
802 | |
803 if (timer > clcf->lingering_timeout) { | |
804 timer = clcf->lingering_timeout; | |
805 } | |
806 | |
807 ngx_add_timer(rev, timer); | |
699 } | 808 } |
700 | 809 |
701 | 810 |
702 static u_char * | 811 static u_char * |
703 ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos, | 812 ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos, |
4539 c = h2c->connection; | 4648 c = h2c->connection; |
4540 | 4649 |
4541 h2c->blocked = 1; | 4650 h2c->blocked = 1; |
4542 | 4651 |
4543 if (!c->error && !h2c->goaway) { | 4652 if (!c->error && !h2c->goaway) { |
4653 h2c->goaway = 1; | |
4654 | |
4544 if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { | 4655 if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) { |
4545 (void) ngx_http_v2_send_output_queue(h2c); | 4656 (void) ngx_http_v2_send_output_queue(h2c); |
4546 } | 4657 } |
4547 } | 4658 } |
4548 | 4659 |
4549 c->error = 1; | |
4550 | |
4551 if (!h2c->processing && !h2c->pushing) { | 4660 if (!h2c->processing && !h2c->pushing) { |
4552 ngx_http_close_connection(c); | 4661 goto done; |
4553 return; | |
4554 } | 4662 } |
4555 | 4663 |
4556 c->read->handler = ngx_http_empty_handler; | 4664 c->read->handler = ngx_http_empty_handler; |
4557 c->write->handler = ngx_http_empty_handler; | 4665 c->write->handler = ngx_http_empty_handler; |
4558 | 4666 |
4596 } | 4704 } |
4597 | 4705 |
4598 h2c->blocked = 0; | 4706 h2c->blocked = 0; |
4599 | 4707 |
4600 if (h2c->processing || h2c->pushing) { | 4708 if (h2c->processing || h2c->pushing) { |
4709 c->error = 1; | |
4601 return; | 4710 return; |
4602 } | 4711 } |
4603 | 4712 |
4604 ngx_http_close_connection(c); | 4713 done: |
4714 | |
4715 if (c->error) { | |
4716 ngx_http_close_connection(c); | |
4717 return; | |
4718 } | |
4719 | |
4720 ngx_http_v2_lingering_close(h2c); | |
4605 } | 4721 } |
4606 | 4722 |
4607 | 4723 |
4608 static ngx_int_t | 4724 static ngx_int_t |
4609 ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta) | 4725 ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta) |