tap-http 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/History CHANGED
@@ -1,3 +1,9 @@
1
+ == 0.3.1 / 2009-02-19
2
+
3
+ Improved handling of multipart/form data.
4
+
5
+ * http_to_yaml returns config file as an attachment.
6
+
1
7
  == 0.3.0 / 2009-02-19
2
8
 
3
9
  Rework of cgi scripts a tap controllers. Nearly complete
data/README CHANGED
@@ -35,16 +35,6 @@ must be installed):
35
35
 
36
36
  Now open a browser and work through the {tutorial}[http://localhost:8080/capture].
37
37
 
38
- === Bugs/Known Issues
39
-
40
- The Tap::Http::Utils#parse_cgi_request (used in parsing redirected requests
41
- into a YAML file) is currently untested because I can't figure a way to setup
42
- the ENV variables in a standard way. Of course I could set them up myself, but
43
- I can't be sure I'm setting up a realistic test environment.
44
-
45
- The capture procedure seems to work in practice, but please report bugs and let
46
- me know if you know a way to setup a CGI environment for testing!
47
-
48
38
  == Installation
49
39
 
50
40
  TapHttp is available as a gem on RubyForge[http://rubyforge.org/projects/tap]. Use:
@@ -2,54 +2,70 @@ require 'tap/controller'
2
2
  require 'tap/http/utils'
3
3
 
4
4
  class CaptureController < Tap::Controller
5
+ include Tap::Http::Utils
5
6
 
6
7
  def index
7
- render 'index.erb'
8
+ tutorial
8
9
  end
9
10
 
11
+ # Brings up a tutorial teaching how to capture and resubmit forms.
12
+ def tutorial
13
+ render 'tutorial.erb'
14
+ end
15
+
16
+ # Say is the target of the tutorial.
10
17
  def say
11
18
  "<pre>#{request.params['words']}</pre>"
12
19
  end
13
20
 
14
- # Echos back redirected HTTP requests as YAML, suitable for use with the
21
+ # Echos back redirected HTTP requests as YAML, suitable for use with the
15
22
  # Tap::Http::Submit task. All HTTP parameters and headers are echoed back
16
23
  # directly, except for the '__original_action' parameter which is used in
17
24
  # conjuction with the 'Referer' header to reconstruct the original url of
18
25
  # the request. The '__original_action' parameter is not echoed.
19
- def http_to_yaml
20
- headers = {}
21
- request.env.each_pair do |key, value|
22
- headers[key] = value unless key =~ /^(rack|tap)/
23
- end
24
- params = request.params
25
-
26
- original_action = params.delete("__original_action").to_s
27
- referer = headers['Referer'].to_s
28
- uri = Tap::Http::Utils.determine_url(original_action, referer)
29
- headers['Host'] = URI.parse(uri).host
30
-
31
- config = {
32
- 'headers' => headers,
33
- 'params' => params,
34
- 'uri' => uri,
35
- 'request_method' => request.request_method
36
- }
26
+ def http_to_yaml(keep_content='true')
27
+ keep_content = keep_content.to_s =~ /true/i
28
+ config = parse_request(keep_content)
29
+
30
+ original_action = config['params'].delete("__original_action").to_s
31
+ referer = config['headers']['Referer'].to_s
32
+ config['uri'] = determine_url(original_action, referer)
33
+ config['headers']['Host'] = URI.parse(config['uri']).host
37
34
 
38
35
  response['Content-Type'] = "text/plain"
39
- %Q{# Save as a configuration file. Resubmit using:
40
- #
41
- # % rap load <config_file> --: submit --: dump --no-audit
42
- #
43
- #{YAML.dump(config)}
44
- }
36
+ response['Content-Disposition'] = "attachment; filename=request.yml;"
37
+
38
+ YAML.dump(config)
45
39
  end
46
40
 
41
+ # Echos back redirected HTTP requests formatted as YAML.
47
42
  def echo
48
- headers = {}
49
- request.env.each_pair do |key, value|
50
- headers[key] = value unless key =~ /^(rack|tap)/
43
+ response['Content-Type'] = "text/plain"
44
+ YAML.dump(config)
45
+ end
46
+
47
+ protected
48
+
49
+ # helper to parse the request into a request hash for
50
+ # use by a Tap::Http::Submit task
51
+ def parse_request(keep_content=true) # :nodoc:
52
+ config = {}
53
+ parse_rack_request(request, keep_content).each_pair do |key, value|
54
+ config[key.to_s] = value
51
55
  end
52
-
53
- "<pre>#{headers.to_yaml}#{request.params.to_yaml}</pre>"
56
+ config
57
+ end
58
+
59
+ # helper to determine the url of a redirected action
60
+ def determine_url(action, referer) # :nodoc:
61
+ base = File.basename(referer)
62
+
63
+ case action
64
+ when /^https?:/ then action
65
+ when /\//
66
+ # only use host of page_url
67
+ File.join(base, action)
68
+ else File.join(base, action)
69
+ end
54
70
  end
55
71
  end
@@ -234,8 +234,8 @@ module Tap
234
234
  body << case value
235
235
  when Hash
236
236
  hash = headerize_keys(value)
237
- filename = hash.delete('Filename') || ""
238
- content = File.exists?(filename) ? File.read(filename) : ""
237
+ filename = hash.delete('Filename')
238
+ content = hash.delete('Content') || (File.exists?(filename) ? File.read(filename) : "")
239
239
 
240
240
  header = "Content-Disposition: form-data; name=\"#{key.to_s}\"; filename=\"#{filename}\"\r\n"
241
241
  hash.each_pair { |key, value| header << "#{key}: #{value}\r\n" }
@@ -22,17 +22,6 @@ module Tap
22
22
  # # :params => {},
23
23
  # # }
24
24
  #
25
- # If splat_values is specified, single-value headers and parameters
26
- # will be hashed as single values. Otherwise, all header and parameter
27
- # values will be arrays.
28
- #
29
- # str = "GET /path?one=a&one=b&two=c HTTP/1.1\n"
30
- # req = parse_http_request(str)
31
- # req[:params] # => {'one' => ['a', 'b'], 'two' => 'c'}
32
- #
33
- # req = parse_http_request(str, false)
34
- # req[:params] # => {'one' => ['a', 'b'], 'two' => ['c']}
35
- #
36
25
  # ==== WEBrick parsing of HTTP format
37
26
  #
38
27
  # WEBrick will parse headers then the body of a request, and currently
@@ -84,21 +73,21 @@ module Tap
84
73
  # TODO: check if there are other headers to capture from
85
74
  # a multipart/form file. Currently only
86
75
  # 'Filename' and 'Content-Type' are added
87
- def parse_http_request(socket, splat_values=true)
76
+ def parse_http_request(socket, keep_content=true)
88
77
  socket = StringIO.new(socket) if socket.kind_of?(String)
89
78
 
90
79
  req = WEBrick::HTTPRequest.new(WEBrick::Config::HTTP)
91
80
  req.parse(socket)
92
81
 
93
- parse_webrick_request(req, splat_values)
82
+ parse_webrick_request(req, keep_content)
94
83
  end
95
84
 
96
85
  # Parses a WEBrick::HTTPRequest, with the same activity as
97
86
  # parse_http_request.
98
- def parse_webrick_request(req, splat_values=true)
87
+ def parse_webrick_request(req, keep_content=true)
99
88
  headers = {}
100
89
  req.header.each_pair do |key, values|
101
- headers[headerize(key)] = splat_values ? splat(values) : values
90
+ headers[headerize(key)] = splat(values)
102
91
  end if req.header
103
92
 
104
93
  params = {}
@@ -112,13 +101,15 @@ module Tap
112
101
  values = []
113
102
  value.each_data do |data|
114
103
  values << if data.filename
115
- {'Filename' => data.filename, 'Content-Type' => data['Content-Type']}
104
+ hash = {'Filename' => data.filename, 'Content-Type' => data['Content-Type']}
105
+ hash['Content'] = data.to_a.join("\n") if keep_content
106
+ hash
116
107
  else
117
108
  data.to_s
118
109
  end
119
110
  end
120
111
 
121
- params[key] = splat_values ? splat(values) : values
112
+ params[key] = splat(values)
122
113
  end if req.query
123
114
 
124
115
  { :url => headers['Host'] ? File.join("http://", headers['Host'], req.path_info) : req.path_info,
@@ -128,77 +119,57 @@ module Tap
128
119
  :params => params}
129
120
  end
130
121
 
131
- # Parses the input CGI into a hash that may be resubmitted by Dispatch.
132
- # To work properly, the standard CGI environmental variables must be
133
- # set in ENV.
134
- #
135
- def parse_cgi_request(cgi, splat_values=true)
122
+ # Parses a Rack::Request, with the same activity as parse_http_request.
123
+ def parse_rack_request(request, keep_content=true)
136
124
  headers = {}
137
- ENV.each_pair do |key, values|
125
+ request.env.each_pair do |key, value|
138
126
  key = case key
139
127
  when "HTTP_VERSION" then next
140
128
  when /^HTTP_(.*)/ then $1
141
129
  when 'CONTENT_TYPE' then key
142
130
  else next
143
131
  end
144
-
145
- headers[headerize(key)] = splat_values ? splat(values) : values
132
+
133
+ headers[headerize(key)] = value
146
134
  end
147
-
135
+
148
136
  params = {}
149
- cgi.params.each_pair do |key, values|
150
- values = values.collect do |value|
151
- case
152
- when !value.respond_to?(:read)
153
- value
154
- when value.original_filename.empty?
155
- value.read
156
- else
157
- {'Filename' => value.original_filename, 'Content-Type' => value.content_type}
137
+ request.params.each_pair do |key, value|
138
+ params[key] = each_member(value) do |obj|
139
+ if obj.kind_of?(Hash)
140
+ file = {'Content-Type' => value[:type], 'Filename' => value[:filename]}
141
+ file['Content'] = value[:tempfile].read if keep_content
142
+ file
143
+ else value
158
144
  end
159
145
  end
160
-
161
- params[key] = splat_values ? splat(values) : values
162
146
  end
163
-
164
- { :url => File.join("http://", headers['Host'], ENV['PATH_INFO']),
165
- :request_method => ENV['REQUEST_METHOD'],
166
- :version => ENV['HTTP_VERSION'] =~ /^HTTP\/(.*)$/ ? $1.to_f : ENV['HTTP_VERSION'],
147
+
148
+ {
149
+ :uri => File.join("http://", headers['Host'], request.env['PATH_INFO']),
150
+ :request_method => request.request_method,
151
+ :version => request.env['HTTP_VERSION'] =~ /^HTTP\/(.*)$/ ? $1.to_f : request.env['HTTP_VERSION'],
167
152
  :headers => headers,
168
- :params => params}
153
+ :params => params
154
+ }
169
155
  end
170
156
 
171
- def determine_url(action, referer)
172
- base = File.basename(referer)
173
-
174
- case action
175
- when /^https?:/ then action
176
- when /\//
177
- # only use host of page_url
178
- File.join(base, action)
179
- else File.join(base, action)
180
- end
181
- end
182
-
183
- # Headerizes an underscored string. The input is be converted to
184
- # a string using to_s.
185
- #
186
- # headerize('SOME_STRING') # => 'Some-String'
187
- # headerize('some string') # => 'Some-String'
188
- # headerize('Some-String') # => 'Some-String'
189
- #
190
- def headerize(str)
191
- str.to_s.gsub(/\s|-/, "_").split("_").collect do |s|
192
- s =~ /^(.)(.*)/
193
- $1.upcase + $2.downcase
194
- end.join("-")
157
+ # Yields each member of an input array to the block and collects the
158
+ # result. If obj is not an array, the value is simply yielded to the
159
+ # block.
160
+ def each_member(obj)
161
+ if obj.kind_of?(Array)
162
+ obj.collect {|value| yield(value) }
163
+ else
164
+ yield(obj)
165
+ end
195
166
  end
196
167
 
197
168
  # Returns the first member of arrays length <= 1, or the array in all
198
- # other cases. Splat is useful to simplify hashes of http headers
169
+ # other cases. Splat is useful to simplify hashes of http headers
199
170
  # and parameters that may have multiple values, but typically only
200
171
  # have one.
201
- #
172
+ #
202
173
  # splat([]) # => nil
203
174
  # splat([:one]) # => :one
204
175
  # splat([:one, :two]) # => [:one, :two]
@@ -212,6 +183,20 @@ module Tap
212
183
  else array
213
184
  end
214
185
  end
186
+
187
+ # Headerizes an underscored string. The input is be converted to
188
+ # a string using to_s.
189
+ #
190
+ # headerize('SOME_STRING') # => 'Some-String'
191
+ # headerize('some string') # => 'Some-String'
192
+ # headerize('Some-String') # => 'Some-String'
193
+ #
194
+ def headerize(str)
195
+ str.to_s.gsub(/\s|-/, "_").split("_").collect do |s|
196
+ s =~ /^(.)(.*)/
197
+ $1.upcase + $2.downcase
198
+ end.join("-")
199
+ end
215
200
 
216
201
  # Inflates (ie unzips) a gzip string, as may be returned by requests
217
202
  # that accept 'gzip' and 'deflate' content encoding.
@@ -21,10 +21,20 @@ else you need to capture the parameters of a form):
21
21
 
22
22
  <p>
23
23
  You should now see some notification that you're redirecting this page.
24
- Write what you want to say, hit submit, then follow the instructions.
24
+ Write what you want to say, hit submit, and you'll be asked to save a
25
+ configuration file for the request. Save it, then on the command line
26
+ execute this:
25
27
  </p>
26
28
 
27
- <form action='say'>
29
+ <pre><code> % rap load &lt;config_file&gt; --: submit --: dump --no-audit</code></pre>
30
+
31
+ <p>
32
+ The request will be submitted and you'll get a printout of the response
33
+ body. To prove everything worked right, click the close link in the
34
+ redirection bar and resubmit the form.
35
+ </p>
36
+
37
+ <form action='/capture/say'>
28
38
  What you want to say: <input name='words' />
29
39
  <input type="submit" value="submit">
30
40
  </form>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tap-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Chiang
@@ -49,7 +49,7 @@ files:
49
49
  - lib/tap/http/submit.rb
50
50
  - lib/tap/http/utils.rb
51
51
  - lib/tap/test/http_test.rb
52
- - views/capture_controller/index.erb
52
+ - views/capture_controller/tutorial.erb
53
53
  - tap.yml
54
54
  - README
55
55
  - MIT-LICENSE