Mercurial > hg > nginx
comparison src/http/ngx_http_spdy_filter_module.c @ 5514:b7ee1bae0ffa
SPDY: implemented buffers chain splitting.
It fixes "chain too big in spdy filter" alerts, and adds full support for rate
limiting of SPDY streams.
author | Valentin Bartenev <vbart@nginx.com> |
---|---|
date | Tue, 14 Jan 2014 16:24:45 +0400 |
parents | 311803b21504 |
children | e5fb14e85040 |
comparison
equal
deleted
inserted
replaced
5513:311803b21504 | 5514:b7ee1bae0ffa |
---|---|
32 ngx_chain_t *in, off_t limit); | 32 ngx_chain_t *in, off_t limit); |
33 | 33 |
34 static ngx_inline ngx_int_t ngx_http_spdy_filter_send( | 34 static ngx_inline ngx_int_t ngx_http_spdy_filter_send( |
35 ngx_connection_t *fc, ngx_http_spdy_stream_t *stream); | 35 ngx_connection_t *fc, ngx_http_spdy_stream_t *stream); |
36 | 36 |
37 static ngx_chain_t *ngx_http_spdy_filter_get_shadow( | |
38 ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, size_t offset, | |
39 size_t size); | |
37 static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame( | 40 static ngx_http_spdy_out_frame_t *ngx_http_spdy_filter_get_data_frame( |
38 ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first, | 41 ngx_http_spdy_stream_t *stream, size_t len, ngx_chain_t *first, |
39 ngx_chain_t *last); | 42 ngx_chain_t *last); |
40 | 43 |
41 static ngx_int_t ngx_http_spdy_syn_frame_handler( | 44 static ngx_int_t ngx_http_spdy_syn_frame_handler( |
616 | 619 |
617 | 620 |
618 static ngx_chain_t * | 621 static ngx_chain_t * |
619 ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) | 622 ngx_http_spdy_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) |
620 { | 623 { |
621 off_t size; | 624 off_t size, offset; |
622 ngx_buf_t *b; | 625 size_t rest, frame_size; |
623 ngx_chain_t *cl, *out, **ln; | 626 ngx_chain_t *cl, *out, **ln; |
624 ngx_http_request_t *r; | 627 ngx_http_request_t *r; |
625 ngx_http_spdy_stream_t *stream; | 628 ngx_http_spdy_stream_t *stream; |
626 ngx_http_spdy_out_frame_t *frame; | 629 ngx_http_spdy_out_frame_t *frame; |
627 | 630 |
637 } | 640 } |
638 | 641 |
639 return NULL; | 642 return NULL; |
640 } | 643 } |
641 | 644 |
642 size = 0; | 645 size = ngx_buf_size(in->buf); |
643 ln = &out; | 646 |
644 | 647 if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { |
645 do { | |
646 b = in->buf; | |
647 | |
648 cl = ngx_alloc_chain_link(r->pool); | 648 cl = ngx_alloc_chain_link(r->pool); |
649 if (cl == NULL) { | 649 if (cl == NULL) { |
650 return NGX_CHAIN_ERROR; | 650 return NGX_CHAIN_ERROR; |
651 } | 651 } |
652 | 652 |
653 size += ngx_buf_size(b); | 653 cl->buf = in->buf; |
654 cl->buf = b; | 654 in->buf = cl->buf->shadow; |
655 | 655 |
656 *ln = cl; | 656 offset = ngx_buf_in_memory(in->buf) |
657 ln = &cl->next; | 657 ? (cl->buf->pos - in->buf->pos) |
658 | 658 : (cl->buf->file_pos - in->buf->file_pos); |
659 in = in->next; | 659 |
660 | 660 cl->next = stream->free_bufs; |
661 } while (in); | 661 stream->free_bufs = cl; |
662 | 662 |
663 if (size > NGX_SPDY_MAX_FRAME_SIZE) { | 663 } else { |
664 ngx_log_error(NGX_LOG_ALERT, fc->log, 0, | 664 offset = 0; |
665 "FIXME: chain too big in spdy filter: %O", size); | 665 } |
666 return NGX_CHAIN_ERROR; | 666 |
667 } | 667 frame_size = (limit && limit < NGX_SPDY_MAX_FRAME_SIZE) |
668 | 668 ? limit : NGX_SPDY_MAX_FRAME_SIZE; |
669 frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size, | 669 |
670 out, cl); | 670 for ( ;; ) { |
671 if (frame == NULL) { | 671 ln = &out; |
672 return NGX_CHAIN_ERROR; | 672 rest = frame_size; |
673 } | 673 |
674 | 674 while ((off_t) rest >= size) { |
675 ngx_http_spdy_queue_frame(stream->connection, frame); | 675 |
676 | 676 if (offset) { |
677 stream->queued++; | 677 cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, |
678 offset, size); | |
679 if (cl == NULL) { | |
680 return NGX_CHAIN_ERROR; | |
681 } | |
682 | |
683 offset = 0; | |
684 | |
685 } else { | |
686 cl = ngx_alloc_chain_link(r->pool); | |
687 if (cl == NULL) { | |
688 return NGX_CHAIN_ERROR; | |
689 } | |
690 | |
691 cl->buf = in->buf; | |
692 } | |
693 | |
694 *ln = cl; | |
695 ln = &cl->next; | |
696 | |
697 rest -= size; | |
698 in = in->next; | |
699 | |
700 if (in == NULL) { | |
701 frame_size -= rest; | |
702 rest = 0; | |
703 break; | |
704 } | |
705 | |
706 size = ngx_buf_size(in->buf); | |
707 } | |
708 | |
709 if (rest) { | |
710 cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, | |
711 offset, rest); | |
712 if (cl == NULL) { | |
713 return NGX_CHAIN_ERROR; | |
714 } | |
715 | |
716 cl->buf->flush = 0; | |
717 cl->buf->last_buf = 0; | |
718 | |
719 *ln = cl; | |
720 | |
721 offset += rest; | |
722 size -= rest; | |
723 } | |
724 | |
725 frame = ngx_http_spdy_filter_get_data_frame(stream, frame_size, | |
726 out, cl); | |
727 if (frame == NULL) { | |
728 return NGX_CHAIN_ERROR; | |
729 } | |
730 | |
731 ngx_http_spdy_queue_frame(stream->connection, frame); | |
732 | |
733 stream->queued++; | |
734 | |
735 if (in == NULL) { | |
736 break; | |
737 } | |
738 | |
739 if (limit) { | |
740 limit -= frame_size; | |
741 | |
742 if (limit == 0) { | |
743 break; | |
744 } | |
745 | |
746 if (limit < NGX_SPDY_MAX_FRAME_SIZE) { | |
747 frame_size = limit; | |
748 } | |
749 } | |
750 } | |
751 | |
752 if (offset) { | |
753 cl = ngx_http_spdy_filter_get_shadow(stream, in->buf, offset, size); | |
754 if (cl == NULL) { | |
755 return NGX_CHAIN_ERROR; | |
756 } | |
757 | |
758 in->buf = cl->buf; | |
759 ngx_free_chain(r->pool, cl); | |
760 } | |
678 | 761 |
679 if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) { | 762 if (ngx_http_spdy_filter_send(fc, stream) == NGX_ERROR) { |
680 return NGX_CHAIN_ERROR; | 763 return NGX_CHAIN_ERROR; |
681 } | 764 } |
682 | 765 |
683 return NULL; | 766 return in; |
767 } | |
768 | |
769 | |
770 static ngx_chain_t * | |
771 ngx_http_spdy_filter_get_shadow(ngx_http_spdy_stream_t *stream, ngx_buf_t *buf, | |
772 size_t offset, size_t size) | |
773 { | |
774 ngx_buf_t *chunk; | |
775 ngx_chain_t *cl; | |
776 | |
777 cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs); | |
778 if (cl == NULL) { | |
779 return NULL; | |
780 } | |
781 | |
782 chunk = cl->buf; | |
783 | |
784 ngx_memcpy(chunk, buf, sizeof(ngx_buf_t)); | |
785 | |
786 chunk->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow; | |
787 chunk->shadow = buf; | |
788 | |
789 if (ngx_buf_in_memory(chunk)) { | |
790 chunk->pos += offset; | |
791 chunk->last = chunk->pos + size; | |
792 } | |
793 | |
794 if (chunk->in_file) { | |
795 chunk->file_pos += offset; | |
796 chunk->file_last = chunk->file_pos + size; | |
797 } | |
798 | |
799 return cl; | |
684 } | 800 } |
685 | 801 |
686 | 802 |
687 static ngx_http_spdy_out_frame_t * | 803 static ngx_http_spdy_out_frame_t * |
688 ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, | 804 ngx_http_spdy_filter_get_data_frame(ngx_http_spdy_stream_t *stream, |
745 p = ngx_spdy_frame_write_flags_and_len(p, flags, len); | 861 p = ngx_spdy_frame_write_flags_and_len(p, flags, len); |
746 | 862 |
747 buf->last = p; | 863 buf->last = p; |
748 buf->end = p; | 864 buf->end = p; |
749 | 865 |
750 buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_module; | 866 buf->tag = (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame; |
751 buf->memory = 1; | 867 buf->memory = 1; |
752 } | 868 } |
753 | 869 |
754 cl->next = first; | 870 cl->next = first; |
755 first = cl; | 871 first = cl; |
823 | 939 |
824 static ngx_int_t | 940 static ngx_int_t |
825 ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, | 941 ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, |
826 ngx_http_spdy_out_frame_t *frame) | 942 ngx_http_spdy_out_frame_t *frame) |
827 { | 943 { |
944 ngx_buf_t *buf; | |
828 ngx_chain_t *cl, *ln; | 945 ngx_chain_t *cl, *ln; |
829 ngx_http_spdy_stream_t *stream; | 946 ngx_http_spdy_stream_t *stream; |
830 | 947 |
831 stream = frame->stream; | 948 stream = frame->stream; |
832 | 949 |
833 cl = frame->first; | 950 cl = frame->first; |
834 | 951 |
835 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_module) { | 952 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_data_frame) { |
836 | 953 |
837 if (cl->buf->pos != cl->buf->last) { | 954 if (cl->buf->pos != cl->buf->last) { |
838 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, | 955 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, |
839 "spdy:%ui DATA frame %p was sent partially", | 956 "spdy:%ui DATA frame %p was sent partially", |
840 stream->id, frame); | 957 stream->id, frame); |
853 | 970 |
854 cl = ln; | 971 cl = ln; |
855 } | 972 } |
856 | 973 |
857 for ( ;; ) { | 974 for ( ;; ) { |
975 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { | |
976 buf = cl->buf->shadow; | |
977 | |
978 if (ngx_buf_in_memory(buf)) { | |
979 buf->pos = cl->buf->pos; | |
980 } | |
981 | |
982 if (buf->in_file) { | |
983 buf->file_pos = cl->buf->file_pos; | |
984 } | |
985 } | |
986 | |
858 if (ngx_buf_size(cl->buf) != 0) { | 987 if (ngx_buf_size(cl->buf) != 0) { |
859 | 988 |
860 if (cl != frame->first) { | 989 if (cl != frame->first) { |
861 frame->first = cl; | 990 frame->first = cl; |
862 ngx_http_spdy_handle_stream(sc, stream); | 991 ngx_http_spdy_handle_stream(sc, stream); |
869 return NGX_AGAIN; | 998 return NGX_AGAIN; |
870 } | 999 } |
871 | 1000 |
872 ln = cl->next; | 1001 ln = cl->next; |
873 | 1002 |
874 ngx_free_chain(stream->request->pool, cl); | 1003 if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_filter_get_shadow) { |
1004 cl->next = stream->free_bufs; | |
1005 stream->free_bufs = cl; | |
1006 | |
1007 } else { | |
1008 ngx_free_chain(stream->request->pool, cl); | |
1009 } | |
875 | 1010 |
876 if (cl == frame->last) { | 1011 if (cl == frame->last) { |
877 goto done; | 1012 goto done; |
878 } | 1013 } |
879 | 1014 |