Class: HTTPX::Parser::HTTP1
- Inherits:
-
Object
- Object
- HTTPX::Parser::HTTP1
- Defined in:
- lib/httpx/parser/http1.rb,
sig/parser/http1.rbs
Constant Summary collapse
- VERSIONS =
%w[1.0 1.1].freeze
Instance Attribute Summary collapse
-
#headers ⇒ parsed_headers
readonly
Returns the value of attribute headers.
-
#http_version ⇒ [Integer, Integer]
readonly
Returns the value of attribute http_version.
-
#status_code ⇒ Integer
readonly
Returns the value of attribute status_code.
Instance Method Summary collapse
- #<<(chunk) ⇒ void
-
#initialize(observer, max_headers, max_header_value_size) ⇒ HTTP1
constructor
A new instance of HTTP1.
- #nextstate(state) ⇒ void
- #no_more_data? ⇒ Boolean
- #parse ⇒ void
- #parse_data ⇒ void
- #parse_headers ⇒ void
- #parse_headline ⇒ void
- #prepare_data(headers) ⇒ void
- #reset! ⇒ void
- #upgrade? ⇒ Boolean
- #upgrade_data ⇒ String
Constructor Details
#initialize(observer, max_headers, max_header_value_size) ⇒ HTTP1
Returns a new instance of HTTP1.
12 13 14 15 16 17 18 19 20 21 |
# File 'lib/httpx/parser/http1.rb', line 12 def initialize(observer, max_headers, max_header_value_size) @observer = observer @state = :idle @buffer = "".b @headers = {} @max_headers = max_headers @max_header_value_size = max_header_value_size @content_length = nil @_has_trailers = @upgrade = false end |
Instance Attribute Details
#headers ⇒ parsed_headers (readonly)
Returns the value of attribute headers.
10 11 12 |
# File 'lib/httpx/parser/http1.rb', line 10 def headers @headers end |
#http_version ⇒ [Integer, Integer] (readonly)
Returns the value of attribute http_version.
10 11 12 |
# File 'lib/httpx/parser/http1.rb', line 10 def http_version @http_version end |
#status_code ⇒ Integer (readonly)
Returns the value of attribute status_code.
10 11 12 |
# File 'lib/httpx/parser/http1.rb', line 10 def status_code @status_code end |
Instance Method Details
#<<(chunk) ⇒ void
This method returns an undefined value.
23 24 25 26 |
# File 'lib/httpx/parser/http1.rb', line 23 def <<(chunk) @buffer << chunk parse end |
#nextstate(state) ⇒ void
This method returns an undefined value.
189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/httpx/parser/http1.rb', line 189 def nextstate(state) @state = state case state when :headers @observer.on_start when :complete @observer.on_complete reset! nextstate(:idle) unless @buffer.empty? end end |
#no_more_data? ⇒ Boolean
178 179 180 181 182 183 184 185 186 187 |
# File 'lib/httpx/parser/http1.rb', line 178 def no_more_data? if @content_length @content_length <= 0 elsif @buffer.respond_to?(:finished?) # @type ivar @buffer: Transcoder::Chunker::Decoder @buffer.finished? else false end end |
#parse ⇒ void
This method returns an undefined value.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/httpx/parser/http1.rb', line 46 def parse loop do state = @state case @state when :idle parse_headline when :headers, :trailers parse_headers when :data parse_data end return if @buffer.empty? || state == @state end end |
#parse_data ⇒ void
This method returns an undefined value.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/httpx/parser/http1.rb', line 130 def parse_data if @buffer.respond_to?(:each) # @type ivar @buffer: Transcoder::Chunker::Decoder @buffer.each do |chunk| @observer.on_data(chunk) end elsif @content_length # @type ivar @buffer: String data = @buffer.byteslice(0, @content_length) # @type var data: String @buffer = @buffer.byteslice(@content_length..-1) || "".b @content_length -= data.bytesize @observer.on_data(data) data.clear else # @type ivar @buffer: String @observer.on_data(@buffer) @buffer.clear end return unless no_more_data? @buffer = @buffer.to_s if @_has_trailers nextstate(:trailers) else nextstate(:complete) end end |
#parse_headers ⇒ void
This method returns an undefined value.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/httpx/parser/http1.rb', line 79 def parse_headers headers = @headers buffer = @buffer #: @type var buffer: String while (idx = buffer.index("\n")) # @type var line: String line = buffer.byteslice(0..idx) raise Error, "wrong header format" if line.start_with?("\s", "\t") line.lstrip! buffer = @buffer = buffer.byteslice((idx + 1)..-1) if line.empty? case @state when :headers prepare_data(headers) @observer.on_headers(headers) return unless @state == :headers # state might have been reset # in the :headers callback nextstate(:data) headers.clear when :trailers @observer.on_trailers(headers) headers.clear nextstate(:complete) end return end separator_index = line.index(":") raise Error, "wrong header format" unless separator_index # @type var key: String key = line.byteslice(0..(separator_index - 1)) key.rstrip! # was lstripped previously! # @type var value: String value = line.byteslice((separator_index + 1)..-1) value.strip! raise Error, "wrong header format" if value.nil? values = (headers[key.downcase] ||= []) << value raise Error, "maximum header value size exceeded" if @max_header_value_size && (values.sum(&:size) > @max_header_value_size) raise Error, "maximum number of response headers exceeded" if headers.size > @max_headers end end |
#parse_headline ⇒ void
This method returns an undefined value.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/httpx/parser/http1.rb', line 61 def parse_headline #: @type ivar @buffer: String idx = @buffer.index("\n") return unless idx (m = %r{\AHTTP(?:/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?}in.match(@buffer)) || raise(Error, "wrong head line format") version, code, _ = m.captures raise(Error, "unsupported HTTP version (HTTP/#{version})") unless version && VERSIONS.include?(version) @http_version = version.split(".").map(&:to_i) @status_code = code.to_i raise(Error, "wrong status code (#{@status_code})") unless (100..599).cover?(@status_code) @buffer = @buffer.byteslice((idx + 1)..-1) nextstate(:headers) end |
#prepare_data(headers) ⇒ void
This method returns an undefined value.
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
# File 'lib/httpx/parser/http1.rb', line 159 def prepare_data(headers) @upgrade = headers.key?("upgrade") @_has_trailers = headers.key?("trailer") if (tr_encodings = headers["transfer-encoding"]) tr_encodings.reverse_each do |tr_encoding| tr_encoding.split(/ *, */).each do |encoding| case encoding when "chunked" @buffer = Transcoder::Chunker::Decoder.new(@buffer.to_s, @_has_trailers) end end end else @content_length = headers["content-length"][0].to_i if headers.key?("content-length") end end |
#reset! ⇒ void
This method returns an undefined value.
28 29 30 31 32 33 34 |
# File 'lib/httpx/parser/http1.rb', line 28 def reset! @state = :idle @headers = {} @content_length = nil @_has_trailers = @upgrade = false @buffer = @buffer.to_s end |
#upgrade? ⇒ Boolean
36 37 38 |
# File 'lib/httpx/parser/http1.rb', line 36 def upgrade? @upgrade end |
#upgrade_data ⇒ String
40 41 42 |
# File 'lib/httpx/parser/http1.rb', line 40 def upgrade_data @buffer.to_s end |