solutious-stella 0.5.5 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. data/CHANGES.txt +39 -2
  2. data/LICENSE.txt +19 -0
  3. data/README.rdoc +85 -0
  4. data/Rakefile +54 -59
  5. data/bin/example_test.rb +82 -0
  6. data/bin/example_webapp.rb +63 -0
  7. data/lib/{stella/logger.rb → logger.rb} +6 -11
  8. data/lib/stella.rb +76 -58
  9. data/lib/stella/clients.rb +161 -0
  10. data/lib/stella/command/base.rb +4 -24
  11. data/lib/stella/command/form.rb +36 -0
  12. data/lib/stella/command/get.rb +44 -0
  13. data/lib/stella/common.rb +53 -0
  14. data/lib/stella/crypto.rb +88 -0
  15. data/lib/stella/data/domain.rb +2 -2
  16. data/lib/stella/data/http.rb +164 -36
  17. data/lib/stella/environment.rb +66 -0
  18. data/lib/stella/functest.rb +105 -0
  19. data/lib/stella/loadtest.rb +186 -0
  20. data/lib/{utils → stella}/stats.rb +16 -20
  21. data/lib/stella/testplan.rb +237 -0
  22. data/lib/stella/testrunner.rb +64 -0
  23. data/lib/storable.rb +280 -0
  24. data/lib/threadify.rb +171 -0
  25. data/lib/timeunits.rb +65 -0
  26. data/lib/util/httputil.rb +266 -0
  27. data/stella.gemspec +69 -0
  28. data/tryouts/drb/drb_test.rb +65 -0
  29. data/tryouts/drb/open4.rb +19 -0
  30. data/tryouts/drb/slave.rb +27 -0
  31. data/tryouts/oo_tryout.rb +30 -0
  32. metadata +39 -107
  33. data/README.textile +0 -162
  34. data/bin/stella +0 -12
  35. data/bin/stella.bat +0 -12
  36. data/lib/daemonize.rb +0 -56
  37. data/lib/pcaplet.rb +0 -180
  38. data/lib/stella/adapter/ab.rb +0 -337
  39. data/lib/stella/adapter/base.rb +0 -106
  40. data/lib/stella/adapter/httperf.rb +0 -305
  41. data/lib/stella/adapter/pcap_watcher.rb +0 -221
  42. data/lib/stella/adapter/proxy_watcher.rb +0 -76
  43. data/lib/stella/adapter/siege.rb +0 -341
  44. data/lib/stella/cli.rb +0 -258
  45. data/lib/stella/cli/agents.rb +0 -73
  46. data/lib/stella/cli/base.rb +0 -55
  47. data/lib/stella/cli/language.rb +0 -18
  48. data/lib/stella/cli/localtest.rb +0 -78
  49. data/lib/stella/cli/sysinfo.rb +0 -16
  50. data/lib/stella/cli/watch.rb +0 -278
  51. data/lib/stella/command/localtest.rb +0 -358
  52. data/lib/stella/response.rb +0 -85
  53. data/lib/stella/storable.rb +0 -201
  54. data/lib/stella/support.rb +0 -276
  55. data/lib/stella/sysinfo.rb +0 -257
  56. data/lib/stella/test/definition.rb +0 -79
  57. data/lib/stella/test/run/summary.rb +0 -70
  58. data/lib/stella/test/stats.rb +0 -114
  59. data/lib/stella/text.rb +0 -64
  60. data/lib/stella/text/resource.rb +0 -38
  61. data/lib/utils/crypto-key.rb +0 -84
  62. data/lib/utils/domainutil.rb +0 -47
  63. data/lib/utils/escape.rb +0 -302
  64. data/lib/utils/fileutil.rb +0 -78
  65. data/lib/utils/httputil.rb +0 -266
  66. data/lib/utils/mathutil.rb +0 -15
  67. data/lib/utils/textgraph.rb +0 -267
  68. data/lib/utils/timerutil.rb +0 -58
  69. data/lib/win32/Console.rb +0 -970
  70. data/lib/win32/Console/ANSI.rb +0 -305
  71. data/support/kvm.h +0 -91
  72. data/support/ruby-pcap-takuma-notes.txt +0 -19
  73. data/support/ruby-pcap-takuma-patch.txt +0 -30
  74. data/support/text/en.yaml +0 -80
  75. data/support/text/nl.yaml +0 -7
  76. data/support/useragents.txt +0 -75
  77. data/tests/01-util_test.rb +0 -0
  78. data/tests/02-stella-util_test.rb +0 -42
  79. data/tests/10-stella_test.rb +0 -104
  80. data/tests/11-stella-storable_test.rb +0 -68
  81. data/tests/60-stella-command_test.rb +0 -248
  82. data/tests/80-stella-cli_test.rb +0 -45
  83. data/tests/spec-helper.rb +0 -31
@@ -1,47 +0,0 @@
1
-
2
- require 'net/dns/packet'
3
-
4
- module DomainUtil
5
-
6
- def DomainUtil.parse_domain_request(data=[])
7
- return unless data && !data.empty?
8
- data = data.split(/\r?\n/) unless data.kind_of? Array
9
- data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
10
-
11
- dns_data = Net::DNS::Packet.parse( data.join($/) )
12
- return unless dns_data.header.query?
13
- domain_name = dns_data.question[0].qName
14
- return dns_data, domain_name, dns_data.header
15
- end
16
-
17
- def DomainUtil.parse_domain_response(data=[])
18
- return unless data && !data.empty?
19
- data = data.split(/\r?\n/) unless data.kind_of? Array
20
- data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
21
-
22
- # This is the heavy lifting.
23
- dns_data = Net::DNS::Packet.parse( data.join($/) )
24
-
25
- # We don't want queries or empty answers
26
- return if dns_data.header.query? || dns_data.answer.nil? || dns_data.answer.empty?
27
-
28
- domain_name = dns_data.answer[0].name
29
-
30
- # Empty the lists if they are already populated
31
- addresses = []
32
- cnames = []
33
-
34
- # Store the CNAMEs associated to this domain. Can be empty.
35
- dns_data.each_cname do |cname|
36
- cnames << cname.to_s
37
- end
38
-
39
- # Store the IP address for this domain. If empty, the lookup was unsuccessful.
40
- dns_data.each_address do |ip|
41
- addresses << ip.to_s
42
- end
43
-
44
- return dns_data, domain_name, dns_data.header, addresses, cnames
45
- end
46
-
47
- end
data/lib/utils/escape.rb DELETED
@@ -1,302 +0,0 @@
1
- # escape.rb - escape/unescape library for several formats
2
- #
3
- # Copyright (C) 2006,2007 Tanaka Akira <akr@fsij.org>
4
- #
5
- # Redistribution and use in source and binary forms, with or without
6
- # modification, are permitted provided that the following conditions are met:
7
- #
8
- # 1. Redistributions of source code must retain the above copyright notice, this
9
- # list of conditions and the following disclaimer.
10
- # 2. Redistributions in binary form must reproduce the above copyright notice,
11
- # this list of conditions and the following disclaimer in the documentation
12
- # and/or other materials provided with the distribution.
13
- # 3. The name of the author may not be used to endorse or promote products
14
- # derived from this software without specific prior written permission.
15
- #
16
- # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
- # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
- # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20
- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
21
- # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
- # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
- # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24
- # IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
25
- # OF SUCH DAMAGE.
26
-
27
- # Escape module provides several escape functions.
28
- # * URI
29
- # * HTML
30
- # * shell command
31
- module EscapeUtil
32
- module_function
33
-
34
- class StringWrapper
35
- class << self
36
- alias new_no_dup new
37
- def new(str)
38
- new_no_dup(str.dup)
39
- end
40
- end
41
-
42
- def initialize(str)
43
- @str = str
44
- end
45
-
46
- def to_s
47
- @str.dup
48
- end
49
-
50
- def inspect
51
- "\#<#{self.class}: #{@str}>"
52
- end
53
-
54
- def ==(other)
55
- other.class == self.class && @str == other.instance_variable_get(:@str)
56
- end
57
- alias eql? ==
58
-
59
- def hash
60
- @str.hash
61
- end
62
- end
63
-
64
- class ShellEscaped < StringWrapper
65
- end
66
-
67
- # Escape.shell_command composes
68
- # a sequence of words to
69
- # a single shell command line.
70
- # All shell meta characters are quoted and
71
- # the words are concatenated with interleaving space.
72
- # It returns an instance of ShellEscaped.
73
- #
74
- # Escape.shell_command(["ls", "/"]) #=> #<Escape::ShellEscaped: ls />
75
- # Escape.shell_command(["echo", "*"]) #=> #<Escape::ShellEscaped: echo '*'>
76
- #
77
- # Note that system(*command) and
78
- # system(Escape.shell_command(command)) is roughly same.
79
- # There are two exception as follows.
80
- # * The first is that the later may invokes /bin/sh.
81
- # * The second is an interpretation of an array with only one element:
82
- # the element is parsed by the shell with the former but
83
- # it is recognized as single word with the later.
84
- # For example, system(*["echo foo"]) invokes echo command with an argument "foo".
85
- # But system(Escape.shell_command(["echo foo"])) invokes "echo foo" command without arguments (and it probably fails).
86
- def shell_command(command)
87
- s = command.map {|word| shell_single_word(word) }.join(' ')
88
- ShellEscaped.new_no_dup(s)
89
- end
90
-
91
- # Escape.shell_single_word quotes shell meta characters.
92
- # It returns an instance of ShellEscaped.
93
- #
94
- # The result string is always single shell word, even if
95
- # the argument is "".
96
- # Escape.shell_single_word("") returns #<Escape::ShellEscaped: ''>.
97
- #
98
- # Escape.shell_single_word("") #=> #<Escape::ShellEscaped: ''>
99
- # Escape.shell_single_word("foo") #=> #<Escape::ShellEscaped: foo>
100
- # Escape.shell_single_word("*") #=> #<Escape::ShellEscaped: '*'>
101
- def shell_single_word(str)
102
- if str && str.empty?
103
- ShellEscaped.new_no_dup("''")
104
- elsif %r{\A[0-9A-Za-z+,./:=@_-]+\z} =~ str
105
- ShellEscaped.new(str)
106
- else
107
- result = ''
108
- str.scan(/('+)|[^']+/) {
109
- if $1
110
- result << %q{\'} * $1.length
111
- else
112
- result << "'#{$&}'"
113
- end
114
- }
115
- ShellEscaped.new_no_dup(result)
116
- end
117
- end
118
-
119
- class PercentEncoded < StringWrapper
120
- end
121
-
122
- # Escape.uri_segment escapes URI segment using percent-encoding.
123
- # It returns an instance of PercentEncoded.
124
- #
125
- # Escape.uri_segment("a/b") #=> #<Escape::PercentEncoded: a%2Fb>
126
- #
127
- # The segment is "/"-splitted element after authority before query in URI, as follows.
128
- #
129
- # scheme://authority/segment1/segment2/.../segmentN?query#fragment
130
- #
131
- # See RFC 3986 for details of URI.
132
- def uri_segment(str)
133
- # pchar - pct-encoded = unreserved / sub-delims / ":" / "@"
134
- # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
135
- # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
136
- s = str.gsub(%r{[^A-Za-z0-9\-._~!$&'()*+,;=:@]}n) {
137
- '%' + $&.unpack("H2")[0].upcase
138
- }
139
- PercentEncoded.new_no_dup(s)
140
- end
141
-
142
- # Escape.uri_path escapes URI path using percent-encoding.
143
- # The given path should be a sequence of (non-escaped) segments separated by "/".
144
- # The segments cannot contains "/".
145
- # It returns an instance of PercentEncoded.
146
- #
147
- # Escape.uri_path("a/b/c") #=> #<Escape::PercentEncoded: a/b/c>
148
- # Escape.uri_path("a?b/c?d/e?f") #=> #<Escape::PercentEncoded: a%3Fb/c%3Fd/e%3Ff>
149
- #
150
- # The path is the part after authority before query in URI, as follows.
151
- #
152
- # scheme://authority/path#fragment
153
- #
154
- # See RFC 3986 for details of URI.
155
- #
156
- # Note that this function is not appropriate to convert OS path to URI.
157
- def uri_path(str)
158
- s = str.gsub(%r{[^/]+}n) { uri_segment($&) }
159
- PercentEncoded.new_no_dup(s)
160
- end
161
-
162
- # :stopdoc:
163
- def html_form_fast(pairs, sep='&')
164
- s = pairs.map {|k, v|
165
- # query-chars - pct-encoded - x-www-form-urlencoded-delimiters =
166
- # unreserved / "!" / "$" / "'" / "(" / ")" / "*" / "," / ":" / "@" / "/" / "?"
167
- # query-char - pct-encoded = unreserved / sub-delims / ":" / "@" / "/" / "?"
168
- # query-char = pchar / "/" / "?" = unreserved / pct-encoded / sub-delims / ":" / "@" / "/" / "?"
169
- # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
170
- # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
171
- # x-www-form-urlencoded-delimiters = "&" / "+" / ";" / "="
172
- k = k.gsub(%r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n) {
173
- '%' + $&.unpack("H2")[0].upcase
174
- }
175
- v = v.gsub(%r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n) {
176
- '%' + $&.unpack("H2")[0].upcase
177
- }
178
- "#{k}=#{v}"
179
- }.join(sep)
180
- PercentEncoded.new_no_dup(s)
181
- end
182
- # :startdoc:
183
-
184
- # Escape.html_form composes HTML form key-value pairs as a x-www-form-urlencoded encoded string.
185
- # It returns an instance of PercentEncoded.
186
- #
187
- # Escape.html_form takes an array of pair of strings or
188
- # an hash from string to string.
189
- #
190
- # Escape.html_form([["a","b"], ["c","d"]]) #=> #<Escape::PercentEncoded: a=b&c=d>
191
- # Escape.html_form({"a"=>"b", "c"=>"d"}) #=> #<Escape::PercentEncoded: a=b&c=d>
192
- #
193
- # In the array form, it is possible to use same key more than once.
194
- # (It is required for a HTML form which contains
195
- # checkboxes and select element with multiple attribute.)
196
- #
197
- # Escape.html_form([["k","1"], ["k","2"]]) #=> #<Escape::PercentEncoded: k=1&k=2>
198
- #
199
- # If the strings contains characters which must be escaped in x-www-form-urlencoded,
200
- # they are escaped using %-encoding.
201
- #
202
- # Escape.html_form([["k=","&;="]]) #=> #<Escape::PercentEncoded: k%3D=%26%3B%3D>
203
- #
204
- # The separator can be specified by the optional second argument.
205
- #
206
- # Escape.html_form([["a","b"], ["c","d"]], ";") #=> #<Escape::PercentEncoded: a=b;c=d>
207
- #
208
- # See HTML 4.01 for details.
209
- def html_form(pairs, sep='&')
210
- r = ''
211
- first = true
212
- pairs.each {|k, v|
213
- # query-chars - pct-encoded - x-www-form-urlencoded-delimiters =
214
- # unreserved / "!" / "$" / "'" / "(" / ")" / "*" / "," / ":" / "@" / "/" / "?"
215
- # query-char - pct-encoded = unreserved / sub-delims / ":" / "@" / "/" / "?"
216
- # query-char = pchar / "/" / "?" = unreserved / pct-encoded / sub-delims / ":" / "@" / "/" / "?"
217
- # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
218
- # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
219
- # x-www-form-urlencoded-delimiters = "&" / "+" / ";" / "="
220
- r << sep if !first
221
- first = false
222
- k.each_byte {|byte|
223
- ch = byte.chr
224
- if %r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n =~ ch
225
- r << "%" << ch.unpack("H2")[0].upcase
226
- else
227
- r << ch
228
- end
229
- }
230
- r << '='
231
- v.each_byte {|byte|
232
- ch = byte.chr
233
- if %r{[^0-9A-Za-z\-\._~:/?@!\$'()*,]}n =~ ch
234
- r << "%" << ch.unpack("H2")[0].upcase
235
- else
236
- r << ch
237
- end
238
- }
239
- }
240
- PercentEncoded.new_no_dup(r)
241
- end
242
-
243
- class HTMLEscaped < StringWrapper
244
- end
245
-
246
- # :stopdoc:
247
- HTML_TEXT_ESCAPE_HASH = {
248
- '&' => '&amp;',
249
- '<' => '&lt;',
250
- '>' => '&gt;',
251
- }
252
- # :startdoc:
253
-
254
- # Escape.html_text escapes a string appropriate for HTML text using character references.
255
- # It returns an instance of HTMLEscaped.
256
- #
257
- # It escapes 3 characters:
258
- # * '&' to '&amp;'
259
- # * '<' to '&lt;'
260
- # * '>' to '&gt;'
261
- #
262
- # Escape.html_text("abc") #=> #<Escape::HTMLEscaped: abc>
263
- # Escape.html_text("a & b < c > d") #=> #<Escape::HTMLEscaped: a &amp; b &lt; c &gt; d>
264
- #
265
- # This function is not appropriate for escaping HTML element attribute
266
- # because quotes are not escaped.
267
- def html_text(str)
268
- s = str.gsub(/[&<>]/) {|ch| HTML_TEXT_ESCAPE_HASH[ch] }
269
- HTMLEscaped.new_no_dup(s)
270
- end
271
-
272
- # :stopdoc:
273
- HTML_ATTR_ESCAPE_HASH = {
274
- '&' => '&amp;',
275
- '<' => '&lt;',
276
- '>' => '&gt;',
277
- '"' => '&quot;',
278
- }
279
- # :startdoc:
280
-
281
- class HTMLAttrValue < StringWrapper
282
- end
283
-
284
- # Escape.html_attr_value encodes a string as a double-quoted HTML attribute using character references.
285
- # It returns an instance of HTMLAttrValue.
286
- #
287
- # Escape.html_attr_value("abc") #=> #<Escape::HTMLAttrValue: "abc">
288
- # Escape.html_attr_value("a&b") #=> #<Escape::HTMLAttrValue: "a&amp;b">
289
- # Escape.html_attr_value("ab&<>\"c") #=> #<Escape::HTMLAttrValue: "ab&amp;&lt;&gt;&quot;c">
290
- # Escape.html_attr_value("a'c") #=> #<Escape::HTMLAttrValue: "a'c">
291
- #
292
- # It escapes 4 characters:
293
- # * '&' to '&amp;'
294
- # * '<' to '&lt;'
295
- # * '>' to '&gt;'
296
- # * '"' to '&quot;'
297
- #
298
- def html_attr_value(str)
299
- s = '"' + str.gsub(/[&<>"]/) {|ch| HTML_ATTR_ESCAPE_HASH[ch] } + '"'
300
- HTMLAttrValue.new_no_dup(s)
301
- end
302
- end
@@ -1,78 +0,0 @@
1
- require 'fileutils'
2
-
3
- module FileUtil
4
-
5
- def FileUtil.read_file(path)
6
- FileUtil.read_file_to_array(path).join('')
7
- end
8
-
9
- def FileUtil.read_file_to_array(path)
10
- contents = []
11
- return contents unless File.exists?(path)
12
-
13
- open(path, 'r') do |l|
14
- contents = l.readlines
15
- end
16
-
17
- contents
18
- end
19
- def FileUtil.read_binary_file(path)
20
- contents = ''
21
- return contents unless File.exists?(path)
22
-
23
- open(path, 'rb') do |l|
24
- while (!l.eof?)
25
- contents << l.read(4096)
26
- end
27
- end
28
-
29
- contents
30
- end
31
-
32
-
33
-
34
- def FileUtil.write_file(path, content, flush=true)
35
- FileUtil.write_or_append_file('w', path, content, flush)
36
- end
37
-
38
- def FileUtil.append_file(path, content, flush=true)
39
- FileUtil.write_or_append_file('a', path, content, flush)
40
- end
41
-
42
- def FileUtil.write_or_append_file(write_or_append, path, content = '', flush = true)
43
- #STDERR.puts "Writing to #{ path }..."
44
- FileUtil.create_dir(File.dirname(path))
45
-
46
- open(path, write_or_append) do |f|
47
- f.puts content
48
- f.flush if flush;
49
- end
50
- File.chmod(0600, path)
51
- end
52
-
53
- def FileUtil.create_file(filepath, perm='w', file_perms=nil, force=false)
54
- raise Exception.new("File #{filepath} already exists!") if File.exists?(filepath) && !force
55
-
56
- newfile = File.new(filepath, perm)
57
- begin
58
- if file_perms && File.exists?(file_perms)
59
- File.chown(File.stat(file_perms).uid.to_i, File.stat(file_perms).gid.to_i, filepath)
60
- end
61
- rescue NotImplementedError => ex
62
- end
63
-
64
- newfile
65
- end
66
-
67
- def FileUtil.create_dir(dirpath, dir_perms=nil)
68
- return if File.directory?(dirpath)
69
-
70
- #STDERR.puts "Creating #{ dirpath }"
71
- FileUtils.makedirs(dirpath)
72
-
73
- if dir_perms && File.exists?(dir_perms)
74
- File.chown(File.stat(dir_perms).uid.to_i, File.stat(dir_perms).gid.to_i, dirpath)
75
- end
76
-
77
- end
78
- end
@@ -1,266 +0,0 @@
1
-
2
- require 'uri'
3
- require 'timeout'
4
- require 'net/http'
5
-
6
- module HTTPUtil
7
- VALID_METHODS = %w{GET HEAD POST PUT DELETE}
8
- @@timeout = 20
9
-
10
- # Takes a string. See WEBrick::parse_header(string).
11
- def HTTPUtil.parse_header(raw)
12
- header = Hash.new([].freeze)
13
- raw.each_line do |line|
14
- case line
15
- when /\A(.+?):\s+(.+)\z/om
16
- name, value = $1, $2
17
- name = name.tr('-', '_').to_sym
18
- value.strip!
19
-
20
- header[name] = [] unless header.has_key?(name)
21
- header[name] << value
22
- end
23
- end
24
- header
25
- end
26
-
27
- # Takes a string or array. See parse_header_body for further info.
28
- # Returns +method+, +http_version+, +uri+, +header+, +body+
29
- def HTTPUtil.parse_http_request(data, host=:unknown, port=80)
30
- return unless data && !data.empty?
31
- data = data.split(/\r?\n/) unless data.kind_of? Array
32
- data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
33
- request_line = data.shift # i.e. GET /path HTTP/1.1
34
- method, path, http_version = nil
35
-
36
- # With WEBrick and other proxies, the entire URI is included in HTTP requests.
37
- # i.e. GET http://stellaaahhhh.com/streetcar.png HTTP/1.1
38
- # The parser is expecting just the absolute path.
39
- if request_line =~ /^(\S+)\s+(http:\/\/.+)\s+(HTTP.+)?/mo
40
- uri = URI.parse($2)
41
- request_line = "#{$1} #{uri.request_uri} #{$3}"
42
- host = uri.host
43
- end
44
-
45
- if request_line =~ /^(\S+)\s+(\S+)(?:\s+HTTP\/(\d+\.\d+))?/mo
46
- method = $1
47
- http_version = $3 # Comes before $2 b/c the split resets the numbered vars
48
- path, query_string = $2.split('?')
49
-
50
- # We only process the header and body data when we know we're
51
- # starting from the beginning of a request string. We don't
52
- # want no partials.
53
- header, body = HTTPUtil.parse_header_body(data)
54
- query = HTTPUtil.parse_query(method, query_string)
55
-
56
-
57
- # TODO: Parse username/password
58
- uri = URI::HTTP.build({
59
- :scheme => 'http',
60
- :host => header[:Host][0] || host.to_s,
61
- :port => port,
62
- :path => path,
63
- :query => query_string
64
- })
65
-
66
- else
67
- rl = request_line.sub(/\x0d?\x0a\z/o, '')
68
- raise "Bad Request-Line `#{rl}'."
69
- end
70
-
71
- return method, http_version, uri, header, body
72
- end
73
-
74
-
75
- # Takes a string or array. See parse_header_body for further info.
76
- # Returns +status+, +http_version+, +message+, +header+, +body+
77
- def HTTPUtil.parse_http_response(data=[])
78
- return unless data && !data.empty?
79
- data = data.split(/\r?\n/) unless data.kind_of? Array
80
- data.shift while (data[0].empty? || data[0].nil?) # Remove leading empties
81
- status_line = data.shift # ie. HTTP/1.1 200 OK
82
- http_version, status, message = nil
83
-
84
- if status_line =~ /^HTTP\/(\d.+?)(\s+(\d\d\d)\s+(.+))?$/mo
85
- http_version = $1
86
- status = $2
87
- message = $4
88
-
89
- header, body, query = HTTPUtil.parse_header_body(data)
90
-
91
- else
92
- raise "Bad Response-Line `#{status_line}'."
93
- end
94
-
95
- return status, http_version, message, header, body
96
- end
97
-
98
- # Process everything after the first line of an HTTP request or response:
99
- # GET / HTTP/1.1
100
- # HTTP/1.1 200 OK
101
- # etc...
102
- # Used by parse_http_request and parse_http_response but can be used separately.
103
- # Takes a string or array of strings. A string should be formatted like an HTTP
104
- # request or response. If a body is present it should be separated by two newlines.
105
- # An array of string should contain an empty or nil element between the header
106
- # and body content. This will happen naturally if the raw lines were split by
107
- # a single line terminator. (i.e. /\n/ rather than /\n\n/)
108
- # Returns header (hash), body (string)
109
- def HTTPUtil.parse_header_body(data=[])
110
- header, body = {}, nil
111
- data = data.split(/\r?\n/) unless data.kind_of? Array
112
- data.shift while (data[0].nil? || data[0].empty?) # Remove leading empties
113
-
114
- return header, body unless data && !data.empty?
115
-
116
- #puts data.to_yaml
117
-
118
- # Skip that first line if it exists
119
- data.shift if data[0].match(/\AHTTP|GET|POST|DELETE|PUT|HEAD/mo)
120
-
121
- header_lines = []
122
- header_lines << data.shift while (!data[0].nil? && !data[0].empty?)
123
- header = HTTPUtil::parse_header(header_lines.join($/))
124
-
125
- # We omit the blank line that delimits the header from the body
126
- body = data[1..-1].join($/) unless data.empty?
127
-
128
- return header, body
129
- end
130
-
131
- def HTTPUtil.parse_query(request_method, query_string, content_type='', body='')
132
- query = Hash.new([].freeze)
133
-
134
- if request_method == "GET" || request_method == "HEAD"
135
- query = HTTPUtil::parse_query_from_string(query_string)
136
- elsif content_type =~ /^application\/x-www-form-urlencoded/
137
- query = HTTPUtil::parse_query_from_string(body)
138
- elsif content_type =~ /^multipart\/form-data; boundary=(.+)/
139
- boundary = $1.tr('"', '')
140
- query = HTTPUtil::parse_form_data(body, boundary)
141
- else
142
- query
143
- end
144
-
145
- query
146
- end
147
-
148
- def HTTPUtil.validate_method(meth='GET')
149
- (VALID_METHODS.member? meth.upcase) ? meth : VALID_METHODS[0]
150
- end
151
-
152
- # Parses a query string by breaking it up at the '&'
153
- # and ';' characters. You can also use this to parse
154
- # cookies by changing the characters used in the second
155
- # parameter (which defaults to '&;'.
156
- # Stolen from Mongrel
157
- def HTTPUtil.parse_query_from_string(qs, d = '&;')
158
- params = {}
159
- (qs||'').split(/[#{d}] */n).inject(params) { |h,p|
160
- k, v=unescape(p).split('=',2)
161
- next unless k
162
- k = k.tr('-', '_').to_sym
163
- if cur = params[k]
164
- if cur.class == Array
165
- params[k] << v
166
- else
167
- params[k] = [cur, v]
168
- end
169
- else
170
- params[k] = v
171
- end
172
- }
173
-
174
- return params
175
- end
176
-
177
-
178
-
179
- # Based on WEBrick::HTTPutils::parse_form_data
180
- def HTTPUtil.parse_form_data(io, boundary)
181
- boundary_regexp = /\A--#{boundary}(--)?#{$/}\z/
182
- form_data = Hash.new
183
- return form_data unless io
184
- data = nil
185
- io.each_line{|line|
186
- if boundary_regexp =~ line
187
- if data
188
- data.chop!
189
- key = data.name.tr('-', '_').to_sym
190
- if form_data.has_key?(key)
191
- form_data[key].append_data(data)
192
- else
193
- form_data[key] = data
194
- end
195
- end
196
- data = FormData.new
197
- next
198
- else
199
- if data
200
- data << line
201
- end
202
- end
203
- }
204
- return form_data
205
- end
206
-
207
-
208
- # Extend the basic query string parser provided by the cgi module.
209
- # converts single valued params (the most common case) to
210
- # objects instead of arrays
211
- #
212
- # Input:
213
- # the query string
214
- #
215
- # Output:
216
- # hash of parameters, contains arrays for multivalued parameters
217
- # (multiselect, checkboxes , etc)
218
- # If no query string is provided (nil or "") returns an empty hash.
219
- def HTTPUtil.query_to_hash(query_string)
220
- return {} unless query_string
221
-
222
- query_parameters = HTTPUtil.parse_query(query_string)
223
-
224
- query_parameters.each { |key, val|
225
- # replace the array with an object
226
- query_parameters[key] = val[0] if 1 == val.length
227
- }
228
-
229
- # set default value to nil! cgi sets this to []
230
- query_parameters.default = nil
231
-
232
- return query_parameters
233
- end
234
-
235
- def HTTPUtil.hash_to_query(parameters)
236
- return '' unless parameters
237
- pairs = []
238
- parameters.each do |param, value|
239
- pairs << "#{param}=#{URI.escape(value.to_s)}"
240
- end
241
- return pairs.join('&')
242
- #return pairs.join(";")
243
- end
244
-
245
-
246
-
247
- # Performs URI escaping so that you can construct proper
248
- # query strings faster. Use this rather than the cgi.rb
249
- # version since it's faster. (Stolen from Mongrel/Camping).
250
- def HTTPUtil.escape(s)
251
- s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
252
- '%'+$1.unpack('H2'*$1.size).join('%').upcase
253
- }.tr(' ', '+')
254
- end
255
-
256
-
257
- # Unescapes a URI escaped string. (Stolen from Mongrel/Camping).
258
- def HTTPUtil.unescape(s)
259
- s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
260
- [$1.delete('%')].pack('H*')
261
- }
262
- end
263
-
264
-
265
- end
266
-