| /** @file |
| |
| Http2Stream.h |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #ifndef __HTTP2_STREAM_H__ |
| #define __HTTP2_STREAM_H__ |
| |
| #include "HTTP2.h" |
| #include "../ProxyClientTransaction.h" |
| #include "Http2DebugNames.h" |
| #include "../http/HttpTunnel.h" // To get ChunkedHandler |
| #include "Http2DependencyTree.h" |
| |
| class Http2Stream; |
| class Http2ConnectionState; |
| |
| typedef Http2DependencyTree<Http2Stream *> DependencyTree; |
| |
| class Http2Stream : public ProxyClientTransaction |
| { |
| public: |
| typedef ProxyClientTransaction super; ///< Parent type. |
| Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) |
| : client_rwnd(initial_rwnd), |
| server_rwnd(Http2::initial_window_size), |
| header_blocks(NULL), |
| header_blocks_length(0), |
| request_header_length(0), |
| recv_end_stream(false), |
| send_end_stream(false), |
| sent_request_header(false), |
| response_header_done(false), |
| request_sent(false), |
| is_first_transaction_flag(false), |
| response_reader(NULL), |
| request_reader(NULL), |
| request_buffer(CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX), |
| priority_node(NULL), |
| _start_time(0), |
| _thread(NULL), |
| _id(sid), |
| _state(HTTP2_STREAM_STATE_IDLE), |
| trailing_header(false), |
| body_done(false), |
| closed(false), |
| sent_delete(false), |
| chunked(false), |
| data_length(0), |
| bytes_sent(0), |
| cross_thread_event(NULL), |
| active_timeout(0), |
| active_event(NULL), |
| inactive_timeout(0), |
| inactive_timeout_at(0), |
| inactive_event(NULL), |
| read_event(NULL), |
| write_event(NULL) |
| { |
| SET_HANDLER(&Http2Stream::main_event_handler); |
| } |
| |
| void |
| init(Http2StreamId sid, ssize_t initial_rwnd) |
| { |
| _id = sid; |
| _start_time = Thread::get_hrtime(); |
| _thread = this_ethread(); |
| this->client_rwnd = initial_rwnd; |
| HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread); |
| HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_STREAM_COUNT, _thread); |
| sm_reader = request_reader = request_buffer.alloc_reader(); |
| http_parser_init(&http_parser); |
| // FIXME: Are you sure? every "stream" needs request_header? |
| _req_header.create(HTTP_TYPE_REQUEST); |
| response_header.create(HTTP_TYPE_RESPONSE); |
| } |
| |
| ~Http2Stream() { this->destroy(); } |
| int main_event_handler(int event, void *edata); |
| |
| void destroy(); |
| |
| bool |
| is_body_done() const |
| { |
| return body_done; |
| } |
| |
| void |
| mark_body_done() |
| { |
| body_done = true; |
| } |
| |
| void |
| update_sent_count(unsigned num_bytes) |
| { |
| bytes_sent += num_bytes; |
| } |
| |
| const Http2StreamId |
| get_id() const |
| { |
| return _id; |
| } |
| |
| const Http2StreamState |
| get_state() const |
| { |
| return _state; |
| } |
| |
| bool change_state(uint8_t type, uint8_t flags); |
| |
| void |
| set_id(Http2StreamId sid) |
| { |
| _id = sid; |
| } |
| |
| void |
| update_initial_rwnd(Http2WindowSize new_size) |
| { |
| client_rwnd = new_size; |
| } |
| |
| bool |
| has_trailing_header() const |
| { |
| return trailing_header; |
| } |
| |
| void |
| set_request_headers(HTTPHdr &h2_headers) |
| { |
| _req_header.copy(&h2_headers); |
| } |
| |
| // Check entire DATA payload length if content-length: header is exist |
| void |
| increment_data_length(uint64_t length) |
| { |
| data_length += length; |
| } |
| |
| bool |
| payload_length_is_valid() const |
| { |
| uint32_t content_length = _req_header.get_content_length(); |
| return content_length == 0 || content_length == data_length; |
| } |
| |
| Http2ErrorCode decode_header_blocks(HpackHandle &hpack_handle); |
| void send_request(Http2ConnectionState &cstate); |
| VIO *do_io_read(Continuation *c, int64_t nbytes, MIOBuffer *buf); |
| VIO *do_io_write(Continuation *c, int64_t nbytes, IOBufferReader *abuffer, bool owner = false); |
| void do_io_close(int lerrno = -1); |
| void initiating_close(); |
| void do_io_shutdown(ShutdownHowTo_t) {} |
| void update_read_request(int64_t read_len, bool send_update); |
| bool update_write_request(IOBufferReader *buf_reader, int64_t write_len, bool send_update); |
| void reenable(VIO *vio); |
| virtual void transaction_done(); |
| void send_response_body(); |
| void push_promise(URL &url); |
| |
| // Stream level window size |
| ssize_t client_rwnd, server_rwnd; |
| |
| LINK(Http2Stream, link); |
| |
| uint8_t *header_blocks; |
| uint32_t header_blocks_length; // total length of header blocks (not include |
| // Padding or other fields) |
| uint32_t request_header_length; // total length of payload (include Padding |
| // and other fields) |
| bool recv_end_stream; |
| bool send_end_stream; |
| |
| bool sent_request_header; |
| bool response_header_done; |
| bool request_sent; |
| bool is_first_transaction_flag; |
| |
| HTTPHdr response_header; |
| IOBufferReader *response_reader; |
| IOBufferReader *request_reader; |
| MIOBuffer request_buffer; |
| DependencyTree::Node *priority_node; |
| |
| EThread * |
| get_thread() |
| { |
| return _thread; |
| } |
| |
| IOBufferReader *response_get_data_reader() const; |
| bool |
| response_is_chunked() const |
| { |
| return chunked; |
| } |
| |
| void release(IOBufferReader *r); |
| |
| virtual bool |
| allow_half_open() const |
| { |
| return false; |
| } |
| |
| virtual void set_active_timeout(ink_hrtime timeout_in); |
| virtual void set_inactivity_timeout(ink_hrtime timeout_in); |
| virtual void cancel_inactivity_timeout(); |
| void clear_inactive_timer(); |
| void clear_active_timer(); |
| void clear_timers(); |
| void clear_io_events(); |
| bool |
| is_client_state_writeable() const |
| { |
| return _state == HTTP2_STREAM_STATE_OPEN || _state == HTTP2_STREAM_STATE_HALF_CLOSED_REMOTE || |
| HTTP2_STREAM_STATE_RESERVED_LOCAL; |
| } |
| |
| bool |
| is_closed() const |
| { |
| return closed; |
| } |
| |
| bool |
| is_first_transaction() const |
| { |
| return is_first_transaction_flag; |
| } |
| |
| private: |
| void response_initialize_data_handling(bool &is_done); |
| void response_process_data(bool &is_done); |
| bool response_is_data_available() const; |
| Event *send_tracked_event(Event *event, int send_event, VIO *vio); |
| HTTPParser http_parser; |
| ink_hrtime _start_time; |
| EThread *_thread; |
| Http2StreamId _id; |
| Http2StreamState _state; |
| |
| MIOBuffer response_buffer; |
| HTTPHdr _req_header; |
| VIO read_vio; |
| VIO write_vio; |
| |
| bool trailing_header; |
| bool body_done; |
| bool closed; |
| bool sent_delete; |
| bool chunked; |
| |
| uint64_t data_length; |
| uint64_t bytes_sent; |
| |
| ChunkedHandler chunked_handler; |
| Event *cross_thread_event; |
| |
| // Support stream-specific timeouts |
| ink_hrtime active_timeout; |
| Event *active_event; |
| |
| ink_hrtime inactive_timeout; |
| ink_hrtime inactive_timeout_at; |
| Event *inactive_event; |
| |
| Event *read_event; |
| Event *write_event; |
| }; |
| |
| extern ClassAllocator<Http2Stream> http2StreamAllocator; |
| |
| extern bool check_continuation(Continuation *cont); |
| extern bool check_stream_thread(Continuation *cont); |
| |
| #endif |