|
ferencd@0
|
1 require 'net/protocol'
|
|
ferencd@0
|
2
|
|
ferencd@0
|
3 module Net
|
|
ferencd@0
|
4 class HTTP < Protocol
|
|
ferencd@0
|
5 module Server
|
|
ferencd@0
|
6 module Requests
|
|
ferencd@0
|
7 # Default ports for common URI schemes
|
|
ferencd@0
|
8 DEFAULT_PORTS = {
|
|
ferencd@0
|
9 'https' => 443,
|
|
ferencd@0
|
10 'http' => 80
|
|
ferencd@0
|
11 }
|
|
ferencd@0
|
12
|
|
ferencd@0
|
13 protected
|
|
ferencd@0
|
14
|
|
ferencd@0
|
15 #
|
|
ferencd@0
|
16 # Reads a HTTP Request from the stream.
|
|
ferencd@0
|
17 #
|
|
ferencd@0
|
18 # @param [IO] stream
|
|
ferencd@0
|
19 # The stream to read from.
|
|
ferencd@0
|
20 #
|
|
ferencd@0
|
21 # @return [String, nil]
|
|
ferencd@0
|
22 # The raw HTTP Request or `nil` if the Request was malformed.
|
|
ferencd@0
|
23 #
|
|
ferencd@0
|
24 def read_request(stream)
|
|
ferencd@0
|
25 buffer = ''
|
|
ferencd@0
|
26
|
|
ferencd@0
|
27 begin
|
|
ferencd@0
|
28 request_line = stream.readline("\r\n")
|
|
ferencd@0
|
29 post_req = false
|
|
ferencd@0
|
30 if request_line.start_with? 'POST'
|
|
ferencd@0
|
31 post_req = true
|
|
ferencd@0
|
32 end
|
|
ferencd@0
|
33 # the request line must contain 'HTTP/'
|
|
ferencd@0
|
34 return unless request_line.include?('HTTP/')
|
|
ferencd@0
|
35
|
|
ferencd@0
|
36 buffer << request_line
|
|
ferencd@0
|
37
|
|
ferencd@0
|
38 stream.each_line("\r\n") do |header|
|
|
ferencd@0
|
39 buffer << header
|
|
ferencd@0
|
40 # a header line must contain a ':' character followed by
|
|
ferencd@0
|
41 # linear-white-space (either ' ' or "\t").
|
|
ferencd@0
|
42 unless (header.include?(': ') || header.include?(":\t"))
|
|
ferencd@0
|
43 # if this is not a header line, check if it is the end
|
|
ferencd@0
|
44 # of the request
|
|
ferencd@0
|
45 if header == "\r\n"
|
|
ferencd@0
|
46 if post_req
|
|
ferencd@0
|
47 # end of the request, what comes here is the possible POST data
|
|
ferencd@0
|
48 extra = ''
|
|
ferencd@0
|
49 begin
|
|
ferencd@0
|
50 extra = stream.read_nonblock(256)
|
|
ferencd@0
|
51 rescue
|
|
ferencd@0
|
52 extra = ''
|
|
ferencd@0
|
53 end
|
|
ferencd@0
|
54 buffer = buffer.strip
|
|
ferencd@0
|
55 buffer << "\r\nX-Extra: #{extra}"
|
|
ferencd@0
|
56 buffer += "\r\n\r\n"
|
|
ferencd@0
|
57 end
|
|
ferencd@0
|
58 break
|
|
ferencd@0
|
59 else
|
|
ferencd@0
|
60 # invalid header line
|
|
ferencd@0
|
61 return
|
|
ferencd@0
|
62 end
|
|
ferencd@0
|
63 end
|
|
ferencd@0
|
64 end
|
|
ferencd@0
|
65
|
|
ferencd@0
|
66 rescue IOError, SystemCallError
|
|
ferencd@0
|
67 return
|
|
ferencd@0
|
68 end
|
|
ferencd@0
|
69
|
|
ferencd@0
|
70 return buffer
|
|
ferencd@0
|
71
|
|
ferencd@0
|
72 end
|
|
ferencd@0
|
73
|
|
ferencd@0
|
74 #
|
|
ferencd@0
|
75 # Normalizes the `:uri` part of the request.
|
|
ferencd@0
|
76 #
|
|
ferencd@0
|
77 # @param [Hash] request
|
|
ferencd@0
|
78 # The unnormalized HTTP request.
|
|
ferencd@0
|
79 #
|
|
ferencd@0
|
80 def normalize_uri(request)
|
|
ferencd@0
|
81 uri = request[:uri]
|
|
ferencd@0
|
82
|
|
ferencd@0
|
83 case uri
|
|
ferencd@0
|
84 when Hash
|
|
ferencd@0
|
85 if uri[:scheme]
|
|
ferencd@0
|
86 uri[:port] = unless uri[:port]
|
|
ferencd@0
|
87 DEFAULT_PORTS[uri[:scheme]]
|
|
ferencd@0
|
88 else
|
|
ferencd@0
|
89 uri[:port].to_i
|
|
ferencd@0
|
90 end
|
|
ferencd@0
|
91 end
|
|
ferencd@0
|
92 when '*'
|
|
ferencd@0
|
93 request[:uri] = {}
|
|
ferencd@0
|
94 end
|
|
ferencd@0
|
95 end
|
|
ferencd@0
|
96
|
|
ferencd@0
|
97 #
|
|
ferencd@0
|
98 # Normalizes the `:headers` part of the request.
|
|
ferencd@0
|
99 #
|
|
ferencd@0
|
100 # @param [Hash] request
|
|
ferencd@0
|
101 # The unnormalized HTTP request.
|
|
ferencd@0
|
102 #
|
|
ferencd@0
|
103 def normalize_headers(request)
|
|
ferencd@0
|
104 headers = request[:headers]
|
|
ferencd@0
|
105 normalized_headers = {}
|
|
ferencd@0
|
106
|
|
ferencd@0
|
107 unless headers.empty?
|
|
ferencd@0
|
108 headers.each do |header|
|
|
ferencd@0
|
109 name = header[:name].to_s
|
|
ferencd@0
|
110 value = header[:value].to_s
|
|
ferencd@0
|
111
|
|
ferencd@0
|
112 if normalized_headers.has_key?(name)
|
|
ferencd@0
|
113 previous_value = normalized_headers[name]
|
|
ferencd@0
|
114
|
|
ferencd@0
|
115 if previous_value.kind_of?(Array)
|
|
ferencd@0
|
116 previous_value << value
|
|
ferencd@0
|
117 else
|
|
ferencd@0
|
118 normalized_headers[name] = [previous_value, value]
|
|
ferencd@0
|
119 end
|
|
ferencd@0
|
120 else
|
|
ferencd@0
|
121 normalized_headers[name] = value
|
|
ferencd@0
|
122 end
|
|
ferencd@0
|
123 end
|
|
ferencd@0
|
124 end
|
|
ferencd@0
|
125
|
|
ferencd@0
|
126 request[:headers] = normalized_headers
|
|
ferencd@0
|
127 end
|
|
ferencd@0
|
128
|
|
ferencd@0
|
129 #
|
|
ferencd@0
|
130 # Normalizes a HTTP request.
|
|
ferencd@0
|
131 #
|
|
ferencd@0
|
132 # @param [Hash] request
|
|
ferencd@0
|
133 # The unnormalized HTTP request.
|
|
ferencd@0
|
134 #
|
|
ferencd@0
|
135 def normalize_request(request)
|
|
ferencd@0
|
136 normalize_uri(request)
|
|
ferencd@0
|
137 normalize_headers(request)
|
|
ferencd@0
|
138 end
|
|
ferencd@0
|
139
|
|
ferencd@0
|
140 end
|
|
ferencd@0
|
141 end
|
|
ferencd@0
|
142 end
|
|
ferencd@0
|
143 end
|