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)