diff options
author | Andrey Uzunov <andrey.uzunov@gmail.com> | 2013-08-11 20:40:36 +0000 |
---|---|---|
committer | Andrey Uzunov <andrey.uzunov@gmail.com> | 2013-08-11 20:40:36 +0000 |
commit | 4b0a8adbcc7d22cc86bb07dc56f7367dc96ad1ed (patch) | |
tree | dd2e10eaa6c27b2930060111da677b775b228840 | |
parent | e421dc4c64105540b24ee732e8887ee26c40276a (diff) | |
download | libmicrohttpd-4b0a8adbcc7d22cc86bb07dc56f7367dc96ad1ed.tar.gz libmicrohttpd-4b0a8adbcc7d22cc86bb07dc56f7367dc96ad1ed.zip |
spdy: WINDOW_UPDATE sent on data receiving to allow big HTTP bodies
-rw-r--r-- | src/microspdy/internal.h | 18 | ||||
-rw-r--r-- | src/microspdy/session.c | 111 | ||||
-rw-r--r-- | src/microspdy/session.h | 31 | ||||
-rw-r--r-- | src/microspdy/stream.c | 1 | ||||
-rw-r--r-- | src/microspdy/structures.h | 5 |
5 files changed, 161 insertions, 5 deletions
diff --git a/src/microspdy/internal.h b/src/microspdy/internal.h index 74281577..a7aec14f 100644 --- a/src/microspdy/internal.h +++ b/src/microspdy/internal.h | |||
@@ -28,14 +28,24 @@ | |||
28 | #include "platform.h" | 28 | #include "platform.h" |
29 | #include "microspdy.h" | 29 | #include "microspdy.h" |
30 | 30 | ||
31 | /* size of read buffers for each connection | 31 | /** |
32 | * must be at least the size of SPDY_MAX_SUPPORTED_FRAME_SIZE */ | 32 | * size of read buffers for each connection |
33 | * must be at least the size of SPDY_MAX_SUPPORTED_FRAME_SIZE | ||
34 | */ | ||
33 | #define SPDYF_BUFFER_SIZE 8192 | 35 | #define SPDYF_BUFFER_SIZE 8192 |
34 | 36 | ||
35 | /* number of frames written to the socket at once. After X frames | 37 | /** |
38 | * initial size of window for each stream (this is for the data | ||
39 | * within data frames that can be handled) | ||
40 | */ | ||
41 | #define SPDYF_INITIAL_WINDOW_SIZE 65536 | ||
42 | |||
43 | /** | ||
44 | * number of frames written to the socket at once. After X frames | ||
36 | * everything should be run again. In this way the application can | 45 | * everything should be run again. In this way the application can |
37 | * response to more important requests while a big file is still | 46 | * response to more important requests while a big file is still |
38 | * being transmitted to the client */ | 47 | * being transmitted to the client |
48 | */ | ||
39 | #define SPDYF_NUM_SENT_FRAMES_AT_ONCE 10 | 49 | #define SPDYF_NUM_SENT_FRAMES_AT_ONCE 10 |
40 | 50 | ||
41 | 51 | ||
diff --git a/src/microspdy/session.c b/src/microspdy/session.c index 7edebaba..918b9fdf 100644 --- a/src/microspdy/session.c +++ b/src/microspdy/session.c | |||
@@ -380,7 +380,9 @@ spdyf_handler_read_data (struct SPDY_Session *session) | |||
380 | frame->length, | 380 | frame->length, |
381 | 0 == (SPDY_DATA_FLAG_FIN & frame->flags)); | 381 | 0 == (SPDY_DATA_FLAG_FIN & frame->flags)); |
382 | 382 | ||
383 | session->read_buffer_beginning += frame->length; | 383 | session->read_buffer_beginning += frame->length; |
384 | |||
385 | stream->window_size -= frame->length; | ||
384 | 386 | ||
385 | //TODO close in and send rst maybe | 387 | //TODO close in and send rst maybe |
386 | SPDYF_ASSERT(SPDY_YES == ret, "Cancel POST data is not yet implemented"); | 388 | SPDYF_ASSERT(SPDY_YES == ret, "Cancel POST data is not yet implemented"); |
@@ -389,6 +391,20 @@ spdyf_handler_read_data (struct SPDY_Session *session) | |||
389 | { | 391 | { |
390 | stream->is_in_closed = true; | 392 | stream->is_in_closed = true; |
391 | } | 393 | } |
394 | else if(stream->window_size < SPDYF_INITIAL_WINDOW_SIZE / 2) | ||
395 | { | ||
396 | //very simple implementation of flow control | ||
397 | //when the window's size is under the half of the initial value, | ||
398 | //increase it again up to the initial value | ||
399 | |||
400 | //prepare WINDOW_UPDATE | ||
401 | if(SPDY_YES == SPDYF_prepare_window_update(session, stream, | ||
402 | SPDYF_INITIAL_WINDOW_SIZE - stream->window_size)) | ||
403 | { | ||
404 | stream->window_size = SPDYF_INITIAL_WINDOW_SIZE; | ||
405 | } | ||
406 | //else: do it later | ||
407 | } | ||
392 | session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; | 408 | session->status = SPDY_SESSION_STATUS_WAIT_FOR_HEADER; |
393 | free(frame); | 409 | free(frame); |
394 | } | 410 | } |
@@ -751,6 +767,47 @@ SPDYF_handler_write_rst_stream (struct SPDY_Session *session) | |||
751 | return SPDY_YES; | 767 | return SPDY_YES; |
752 | } | 768 | } |
753 | 769 | ||
770 | |||
771 | int | ||
772 | SPDYF_handler_write_window_update (struct SPDY_Session *session) | ||
773 | { | ||
774 | struct SPDYF_Response_Queue *response_queue = session->response_queue_head; | ||
775 | struct SPDYF_Control_Frame control_frame; | ||
776 | size_t total_size; | ||
777 | |||
778 | SPDYF_ASSERT(NULL == session->write_buffer, "the function is called not in the correct moment"); | ||
779 | |||
780 | memcpy(&control_frame, response_queue->control_frame, sizeof(control_frame)); | ||
781 | |||
782 | total_size = sizeof(struct SPDYF_Control_Frame) //SPDY header | ||
783 | + 4 // stream id as "subheader" | ||
784 | + 4; // delta-window-size as "subheader" | ||
785 | |||
786 | if(NULL == (session->write_buffer = malloc(total_size))) | ||
787 | { | ||
788 | return SPDY_NO; | ||
789 | } | ||
790 | session->write_buffer_beginning = 0; | ||
791 | session->write_buffer_offset = 0; | ||
792 | session->write_buffer_size = total_size; | ||
793 | |||
794 | control_frame.length = 8; // always for WINDOW_UPDATE | ||
795 | SPDYF_CONTROL_FRAME_HTON(&control_frame); | ||
796 | |||
797 | //put frame headers to write buffer | ||
798 | memcpy(session->write_buffer + session->write_buffer_offset,&control_frame,sizeof(struct SPDYF_Control_Frame)); | ||
799 | session->write_buffer_offset += sizeof(struct SPDYF_Control_Frame); | ||
800 | |||
801 | //put stream id and delta-window-size to write buffer | ||
802 | memcpy(session->write_buffer + session->write_buffer_offset, response_queue->data, 8); | ||
803 | session->write_buffer_offset += 8; | ||
804 | |||
805 | SPDYF_ASSERT(0 == session->write_buffer_beginning, "bug1"); | ||
806 | SPDYF_ASSERT(session->write_buffer_offset == session->write_buffer_size, "bug2"); | ||
807 | |||
808 | return SPDY_YES; | ||
809 | } | ||
810 | |||
754 | 811 | ||
755 | void | 812 | void |
756 | SPDYF_handler_ignore_frame (struct SPDY_Session *session) | 813 | SPDYF_handler_ignore_frame (struct SPDY_Session *session) |
@@ -1638,3 +1695,55 @@ SPDYF_prepare_rst_stream (struct SPDY_Session *session, | |||
1638 | 1695 | ||
1639 | return SPDY_YES; | 1696 | return SPDY_YES; |
1640 | } | 1697 | } |
1698 | |||
1699 | |||
1700 | int | ||
1701 | SPDYF_prepare_window_update (struct SPDY_Session *session, | ||
1702 | struct SPDYF_Stream * stream, | ||
1703 | int32_t delta_window_size) | ||
1704 | { | ||
1705 | struct SPDYF_Response_Queue *response_to_queue; | ||
1706 | struct SPDYF_Control_Frame *control_frame; | ||
1707 | uint32_t *data; | ||
1708 | |||
1709 | SPDYF_ASSERT(NULL != stream, "stream cannot be NULL"); | ||
1710 | |||
1711 | if(NULL == (response_to_queue = malloc(sizeof(struct SPDYF_Response_Queue)))) | ||
1712 | { | ||
1713 | return SPDY_NO; | ||
1714 | } | ||
1715 | memset(response_to_queue, 0, sizeof(struct SPDYF_Response_Queue)); | ||
1716 | |||
1717 | if(NULL == (control_frame = malloc(sizeof(struct SPDYF_Control_Frame)))) | ||
1718 | { | ||
1719 | free(response_to_queue); | ||
1720 | return SPDY_NO; | ||
1721 | } | ||
1722 | memset(control_frame, 0, sizeof(struct SPDYF_Control_Frame)); | ||
1723 | |||
1724 | if(NULL == (data = malloc(8))) | ||
1725 | { | ||
1726 | free(control_frame); | ||
1727 | free(response_to_queue); | ||
1728 | return SPDY_NO; | ||
1729 | } | ||
1730 | *(data) = HTON31(stream->stream_id); | ||
1731 | *(data + 1) = HTON31(delta_window_size); | ||
1732 | |||
1733 | control_frame->control_bit = 1; | ||
1734 | control_frame->version = SPDY_VERSION; | ||
1735 | control_frame->type = SPDY_CONTROL_FRAME_TYPES_WINDOW_UPDATE; | ||
1736 | control_frame->flags = 0; | ||
1737 | |||
1738 | response_to_queue->control_frame = control_frame; | ||
1739 | response_to_queue->process_response_handler = &SPDYF_handler_write_window_update; | ||
1740 | response_to_queue->data = data; | ||
1741 | response_to_queue->data_size = 8; | ||
1742 | response_to_queue->stream = stream; | ||
1743 | |||
1744 | SPDYF_queue_response (response_to_queue, | ||
1745 | session, | ||
1746 | -1); | ||
1747 | |||
1748 | return SPDY_YES; | ||
1749 | } | ||
diff --git a/src/microspdy/session.h b/src/microspdy/session.h index 6af1ad95..e5c22c9b 100644 --- a/src/microspdy/session.h +++ b/src/microspdy/session.h | |||
@@ -172,6 +172,23 @@ SPDYF_prepare_rst_stream (struct SPDY_Session *session, | |||
172 | 172 | ||
173 | 173 | ||
174 | /** | 174 | /** |
175 | * Prepares WINDOW_UPDATE frame to tell the other party that more | ||
176 | * data can be sent on the stream. The frame will be put at the head of | ||
177 | * the queue. | ||
178 | * | ||
179 | * @param session SPDY session | ||
180 | * @param stream stream to which the changed window will apply | ||
181 | * @param delta_window_size how much the window grows | ||
182 | * @return SPDY_NO on memory error or | ||
183 | * SPDY_YES on success | ||
184 | */ | ||
185 | int | ||
186 | SPDYF_prepare_window_update (struct SPDY_Session *session, | ||
187 | struct SPDYF_Stream * stream, | ||
188 | int32_t delta_window_size); | ||
189 | |||
190 | |||
191 | /** | ||
175 | * Handler called by session_write to fill the write buffer according to | 192 | * Handler called by session_write to fill the write buffer according to |
176 | * the data frame waiting in the response queue. | 193 | * the data frame waiting in the response queue. |
177 | * When response data is given by user callback, the lib does not know | 194 | * When response data is given by user callback, the lib does not know |
@@ -233,6 +250,20 @@ SPDYF_handler_write_rst_stream (struct SPDY_Session *session); | |||
233 | 250 | ||
234 | 251 | ||
235 | /** | 252 | /** |
253 | * Handler called by session_write to fill the write buffer based on the | ||
254 | * control frame (WINDOW_UPDATE) waiting in the response queue. | ||
255 | * | ||
256 | * @param session SPDY session | ||
257 | * @return SPDY_NO on error (not enough memory). If | ||
258 | * the error is unrecoverable the handler changes session's | ||
259 | * status. | ||
260 | * SPDY_YES on success | ||
261 | */ | ||
262 | int | ||
263 | SPDYF_handler_write_window_update (struct SPDY_Session *session); | ||
264 | |||
265 | |||
266 | /** | ||
236 | * Carefully ignore the full size of frames which are not yet supported | 267 | * Carefully ignore the full size of frames which are not yet supported |
237 | * by the lib. | 268 | * by the lib. |
238 | * TODO Ignoring frames containing compressed bodies means that the | 269 | * TODO Ignoring frames containing compressed bodies means that the |
diff --git a/src/microspdy/stream.c b/src/microspdy/stream.c index 336672ac..42666960 100644 --- a/src/microspdy/stream.c +++ b/src/microspdy/stream.c | |||
@@ -104,6 +104,7 @@ SPDYF_stream_new (struct SPDY_Session *session) | |||
104 | stream->flag_unidirectional = (frame->flags & SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL) != 0; | 104 | stream->flag_unidirectional = (frame->flags & SPDY_SYN_STREAM_FLAG_UNIDIRECTIONAL) != 0; |
105 | stream->is_out_closed = stream->flag_unidirectional; | 105 | stream->is_out_closed = stream->flag_unidirectional; |
106 | stream->is_server_initiator = false; | 106 | stream->is_server_initiator = false; |
107 | stream->window_size = SPDYF_INITIAL_WINDOW_SIZE; | ||
107 | 108 | ||
108 | //put the stream to the list of streams for the session | 109 | //put the stream to the list of streams for the session |
109 | DLL_insert(session->streams_head, session->streams_tail, stream); | 110 | DLL_insert(session->streams_head, session->streams_tail, stream); |
diff --git a/src/microspdy/structures.h b/src/microspdy/structures.h index 1c2a0aaf..8a94dcd3 100644 --- a/src/microspdy/structures.h +++ b/src/microspdy/structures.h | |||
@@ -554,6 +554,11 @@ struct SPDYF_Stream | |||
554 | uint32_t assoc_stream_id; | 554 | uint32_t assoc_stream_id; |
555 | 555 | ||
556 | /** | 556 | /** |
557 | * The window of the data within data frames. | ||
558 | */ | ||
559 | uint32_t window_size; | ||
560 | |||
561 | /** | ||
557 | * Stream priority. 0 is the highest, 7 is the lowest. | 562 | * Stream priority. 0 is the highest, 7 is the lowest. |
558 | */ | 563 | */ |
559 | uint8_t priority; | 564 | uint8_t priority; |