Module: Syntropy::HTTP::ProtocolMethods

Defined in:
lib/syntropy/http/io_extensions.rb

Constant Summary collapse

RE_REQUEST_LINE =
/^([a-z]+)\s+([^\s]+)\s+HTTP\/([019\.]{1,3})/i
RE_RESPONSE_LINE =
/^HTTP\/1\.1\s+(\d{3})(\s+.+)?$/i
RE_HEADER_LINE =
/^([a-z0-9-]+):\s+(.+)/i
MAX_REQUEST_LINE_LEN =

16KB

1 << 14
MAX_RESPONSE_LINE_LEN =

256

1 << 8
MAX_HEADER_LINE_LEN =

1KB

1 << 10
MAX_CHUNK_SIZE_LEN =
16

Instance Method Summary collapse

Instance Method Details

#http_read_body(headers) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/syntropy/http/io_extensions.rb', line 77

def http_read_body(headers)
  content_length = headers['content-length']
  if content_length
    chunk = read(content_length.to_i)
    return chunk
  end

  chunked_encoding = headers['transfer-encoding']&.downcase == 'chunked'
  if chunked_encoding
    buf = +''
    while (chunk = http_read_cte_chunk(nil))
      buf << chunk
    end
    return buf
  end

  nil
end

#http_read_body_chunk(headers) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/syntropy/http/io_extensions.rb', line 96

def http_read_body_chunk(headers)
  content_length = headers['content-length']
  if content_length
    chunk = read(content_length.to_i)
    return chunk
  end

  chunked_encoding = headers['transfer-encoding']&.downcase == 'chunked'
  return http_read_cte_chunk(nil) if chunked_encoding

  nil
end

#http_read_request_headersHash

Returns headers.

Returns:

  • (Hash)

    headers

Raises:



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/syntropy/http/io_extensions.rb', line 18

def http_read_request_headers
  line = read_line(MAX_REQUEST_LINE_LEN)
  return nil if !line

  m = line.match(RE_REQUEST_LINE)
  raise ProtocolError, 'Invalid request line' if !m

  http_version = m[3]
  raise UnsupportedHTTPVersionError, 'HTTP version not supported' if http_version != '1.1'

  headers = {
    ':method'   => m[1].downcase,
    ':path'     => m[2],
    ':protocol' => 'http/1.1'
  }

  loop do
    line = read_line(MAX_HEADER_LINE_LEN)
    break if line.nil? || line.empty?

    m = line.match(RE_HEADER_LINE)
    raise ProtocolError, "Invalid header: #{line[0..2047].inspect}" if !m

    headers[m[1].downcase] = m[2]
  end

  headers
end

#http_read_response_headersObject

Raises:



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/syntropy/http/io_extensions.rb', line 47

def http_read_response_headers
  line = read_line(MAX_RESPONSE_LINE_LEN)
  return nil if !line

  m = line.match(RE_RESPONSE_LINE)
  raise ProtocolError, 'Invalid response line' if !m

  headers = {
    ':status'   => m[1].to_i
  }

  loop do
    line = read_line(MAX_HEADER_LINE_LEN)
    break if line.nil? || line.empty?

    m = line.match(RE_HEADER_LINE)
    raise ProtocolError, "Invalid header: #{line[0..2047].inspect}" if !m

    k = m[1].downcase
    if (h = headers[k])
      (h = headers[k] = [h]) if !h.is_a?(Array)
      h << m[2]
    else
      headers[k] = m[2]
    end
  end

  headers
end

#http_write_request_headers(headers) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/syntropy/http/io_extensions.rb', line 109

def http_write_request_headers(headers)
  method = headers[':method'] || (raise BadRequestError)
  path = headers[':path'] || (raise BadRequestError)

  lines = ["#{method} #{path} HTTP/1.1\r\n"]
  headers.each do |k, v|
    next if k =~ /^\:/

    if v.is_a?(Array)
      v.each { lines << "#{k}: #{it}\r\n" }
    else
      lines << "#{k}: #{v}\r\n"
    end
  end
  lines << "\r\n"
  write(*lines)
end