rackful 0.0.2 → 0.1.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.
- data/README.md +14 -2
- data/example/config.ru +19 -13
- data/example/config2.ru +41 -0
- data/lib/rackful/header_spoofing.rb +39 -32
- data/lib/rackful/method_spoofing.rb +56 -58
- data/lib/rackful/relative_location.rb +35 -21
- data/lib/rackful.rb +6 -934
- data/lib/rackful_http_status.rb +288 -0
- data/lib/rackful_path.rb +112 -0
- data/lib/rackful_request.rb +268 -0
- data/lib/rackful_resource.rb +454 -0
- data/lib/rackful_serializer.rb +318 -0
- data/lib/rackful_server.rb +124 -0
- data/rackful.gemspec +3 -1
- metadata +49 -52
data/README.md
CHANGED
@@ -1,2 +1,14 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
Rackful
|
2
|
+
=======
|
3
|
+
|
4
|
+
Library for creating Rackful web services
|
5
|
+
|
6
|
+
The latest documentation is always available
|
7
|
+
[here, as GitHub pages](http://pieterb.github.com/Rackful/).
|
8
|
+
|
9
|
+
Licensing
|
10
|
+
---------
|
11
|
+
Copyright ©2011-2012 Pieter van Beek <pieterb@sara.nl>
|
12
|
+
|
13
|
+
Licensed under the Apache License 2.0. You should have received a copy of the
|
14
|
+
license as part of this distribution.
|
data/example/config.ru
CHANGED
@@ -1,26 +1,28 @@
|
|
1
|
-
require
|
1
|
+
require 'rackful'
|
2
|
+
require 'rackful/header_spoofing'
|
3
|
+
require 'rackful/method_spoofing'
|
4
|
+
require 'rackful/relative_location'
|
2
5
|
require 'digest/md5'
|
3
6
|
|
4
7
|
# The class of the object we're going to serve:
|
5
8
|
class Root
|
6
9
|
include Rackful::Resource
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def do_GET request, response
|
12
|
-
response['Content-Type'] = 'text/plain'
|
13
|
-
response.write @content
|
10
|
+
attr_reader :to_rackful
|
11
|
+
def initialize
|
12
|
+
self.path = '/'
|
13
|
+
@to_rackful = 'Hello world!'
|
14
14
|
end
|
15
15
|
def do_PUT request, response
|
16
|
-
@
|
17
|
-
response.status = status_code :no_content
|
16
|
+
@to_rackful = request.body.read.encode( Encoding::UTF_8 )
|
18
17
|
end
|
19
|
-
def
|
20
|
-
'"' + Digest::MD5.new.update(
|
18
|
+
def get_etag
|
19
|
+
'"' + Digest::MD5.new.update(to_rackful).to_s + '"'
|
21
20
|
end
|
21
|
+
add_serializer Rackful::XHTML, 1.0
|
22
|
+
add_serializer Rackful::JSON, 1.0
|
23
|
+
add_media_type 'text/plain'
|
22
24
|
end
|
23
|
-
$root_resource = Root.new
|
25
|
+
$root_resource = Root.new
|
24
26
|
|
25
27
|
# Rackful::Server needs a resource factory which can map URIs to resource objects:
|
26
28
|
class ResourceFactory
|
@@ -32,4 +34,8 @@ class ResourceFactory
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
37
|
+
use Rackful::MethodSpoofing
|
38
|
+
use Rackful::HeaderSpoofing
|
39
|
+
use Rackful::RelativeLocation
|
40
|
+
|
35
41
|
run Rackful::Server.new ResourceFactory.new
|
data/example/config2.ru
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rack'
|
2
|
+
require 'rackful/header_spoofing'
|
3
|
+
require 'rackful/method_spoofing'
|
4
|
+
require 'rackful/relative_location'
|
5
|
+
|
6
|
+
# The class of the object we're going to serve:
|
7
|
+
class Root
|
8
|
+
include Rackful::Resource
|
9
|
+
def initialize *args
|
10
|
+
super
|
11
|
+
@content = 'Hello world!'
|
12
|
+
end
|
13
|
+
def do_GET request, response
|
14
|
+
response['Content-Type'] = 'text/plain'
|
15
|
+
response.write @content
|
16
|
+
end
|
17
|
+
def do_PUT request, response
|
18
|
+
@content = request.body.read
|
19
|
+
response.status = status_code :no_content
|
20
|
+
end
|
21
|
+
def etag
|
22
|
+
'"' + Digest::MD5.new.update(@content).to_s + '"'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
$root_resource = Root.new '/'
|
26
|
+
|
27
|
+
# Rackful::Server needs a resource factory which can map URIs to resource objects:
|
28
|
+
class ResourceFactory
|
29
|
+
def [] uri
|
30
|
+
case uri
|
31
|
+
when '/'; $root_resource
|
32
|
+
else; nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
use Rackful::MethodSpoofing
|
38
|
+
use Rackful::HeaderSpoofing
|
39
|
+
use Rackful::RelativeLocation
|
40
|
+
|
41
|
+
run Rackful::Server.new ResourceFactory.new
|
@@ -12,9 +12,9 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
15
|
+
require 'rackful'
|
16
16
|
|
17
|
-
=begin
|
17
|
+
=begin markdown
|
18
18
|
Rack middleware that provides header spoofing.
|
19
19
|
|
20
20
|
If you use this middleware, then clients are allowed to spoof an HTTP header
|
@@ -26,41 +26,48 @@ This can be useful if you want to specify certain request headers from within
|
|
26
26
|
a normal web browser.
|
27
27
|
@example Using this middleware
|
28
28
|
use Rackful::HeaderSpoofing
|
29
|
+
@since 0.0.1
|
29
30
|
=end
|
30
|
-
|
31
|
+
class Rackful::HeaderSpoofing
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def initialize app
|
34
|
+
@app = app
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
def call env
|
38
|
+
before_call env
|
39
|
+
r = @app.call env
|
40
|
+
after_call env
|
41
|
+
r
|
42
|
+
end
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
56
|
-
}.
|
57
|
-
collect { |p| p.join('=') }.
|
58
|
-
join('&')
|
59
|
-
if original_query_string != env['QUERY_STRING']
|
60
|
-
env['rackful.header_spoofing.query_string'] ||= original_query_string
|
44
|
+
def before_call env
|
45
|
+
original_query_string = env['QUERY_STRING']
|
46
|
+
env['QUERY_STRING'] = original_query_string.
|
47
|
+
split('&', -1).
|
48
|
+
collect { |s| s.split('=', -1) }.
|
49
|
+
select {
|
50
|
+
|p|
|
51
|
+
if /\A_http_([a-z]+(?:[\-_][a-z]+)*)\z/i === p[0]
|
52
|
+
header_name = p.shift.gsub('-', '_').upcase[1..-1]
|
53
|
+
env[header_name] = p.join('=')
|
54
|
+
false
|
55
|
+
else
|
56
|
+
true
|
61
57
|
end
|
62
|
-
|
58
|
+
}.
|
59
|
+
collect { |p| p.join('=') }.
|
60
|
+
join('&')
|
61
|
+
if original_query_string != env['QUERY_STRING']
|
62
|
+
env['rackful.header_spoofing.query_string'] ||= original_query_string
|
63
|
+
end
|
64
|
+
end
|
63
65
|
|
66
|
+
def after_call env
|
67
|
+
if original_query_string = env['rackful.header_spoofing.query_string']
|
68
|
+
env['rackful.header_spoofing.query_string'] = env['QUERY_STRING']
|
69
|
+
env['QUERY_STRING'] = original_query_string
|
64
70
|
end
|
71
|
+
end
|
65
72
|
|
66
|
-
end
|
73
|
+
end # Rackful::HeaderSpoofing
|
@@ -14,9 +14,7 @@
|
|
14
14
|
|
15
15
|
require 'rackful'
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
=begin
|
17
|
+
=begin markdown
|
20
18
|
Rack middleware that provides method spoofing.
|
21
19
|
|
22
20
|
If you use this middleware, then clients are allowed to spoof an HTTP method
|
@@ -37,68 +35,68 @@ In that case, you can put the parameters in a `POST` body, like this:
|
|
37
35
|
param_1=hello¶m_2=world¶m_3=...
|
38
36
|
@example Using this middleware
|
39
37
|
use Rackful::MethodSpoofing
|
38
|
+
@since 0.0.1
|
40
39
|
=end
|
41
|
-
|
40
|
+
class Rackful::MethodSpoofing
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
42
|
+
def initialize app
|
43
|
+
@app = app
|
44
|
+
end
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
def call env
|
47
|
+
before_call env
|
48
|
+
r = @app.call env
|
49
|
+
after_call env
|
50
|
+
r
|
51
|
+
end
|
52
52
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
}.
|
71
|
-
collect { |p| p.join('=') }.
|
72
|
-
join('&')
|
73
|
-
if new_method
|
74
|
-
if 'GET' == new_method &&
|
75
|
-
'POST' == env['REQUEST_METHOD'] &&
|
76
|
-
'application/x-www-form-urlencoded' == env['CONTENT_TYPE']
|
77
|
-
unless env['QUERY_STRING'].empty
|
78
|
-
env['QUERY_STRING'] = env['QUERY_STRING'] + '&'
|
79
|
-
end
|
80
|
-
begin
|
81
|
-
env['QUERY_STRING'] = env['QUERY_STRING'] + env['rack.input'].read
|
82
|
-
env['rack.input'].rewind
|
83
|
-
end
|
84
|
-
env['rackful.method_spoofing.input'] = env['rack.input']
|
85
|
-
env.delete 'rack.input'
|
86
|
-
env.delete 'CONTENT_TYPE'
|
87
|
-
env.delete 'CONTENT_LENGTH' if env.key? 'CONTENT_LENGTH'
|
88
|
-
end
|
89
|
-
env['rackful.method_spoofing.QUERY_STRING'] ||= original_query_string
|
90
|
-
env['rackful.method_spoofing.REQUEST_METHOD'] = env['REQUEST_METHOD']
|
91
|
-
env['REQUEST_METHOD'] = new_method
|
53
|
+
def before_call env
|
54
|
+
return unless ['GET', 'POST'].include? env['REQUEST_METHOD']
|
55
|
+
original_query_string = env['QUERY_STRING']
|
56
|
+
new_method = nil
|
57
|
+
env['QUERY_STRING'] = original_query_string.
|
58
|
+
split('&', -1).
|
59
|
+
collect { |s| s.split('=', -1) }.
|
60
|
+
select {
|
61
|
+
|p|
|
62
|
+
if /_method/i === p[0] &&
|
63
|
+
/\A[A-Z]+\z/ === ( method = p[1..-1].join('=').upcase ) &&
|
64
|
+
! new_method
|
65
|
+
new_method = method
|
66
|
+
false
|
67
|
+
else
|
68
|
+
true
|
92
69
|
end
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
env
|
70
|
+
}.
|
71
|
+
collect { |p| p.join('=') }.
|
72
|
+
join('&')
|
73
|
+
if new_method
|
74
|
+
if 'GET' == new_method &&
|
75
|
+
'POST' == env['REQUEST_METHOD'] &&
|
76
|
+
'application/x-www-form-urlencoded' == env['CONTENT_TYPE']
|
77
|
+
unless env['QUERY_STRING'].empty
|
78
|
+
env['QUERY_STRING'] = env['QUERY_STRING'] + '&'
|
79
|
+
end
|
80
|
+
begin
|
81
|
+
env['QUERY_STRING'] = env['QUERY_STRING'] + env['rack.input'].read
|
82
|
+
env['rack.input'].rewind
|
99
83
|
end
|
84
|
+
env['rackful.method_spoofing.input'] = env['rack.input']
|
85
|
+
env.delete 'rack.input'
|
86
|
+
env.delete 'CONTENT_TYPE'
|
87
|
+
env.delete 'CONTENT_LENGTH' if env.key? 'CONTENT_LENGTH'
|
100
88
|
end
|
101
|
-
|
89
|
+
env['rackful.method_spoofing.QUERY_STRING'] ||= original_query_string
|
90
|
+
env['rackful.method_spoofing.REQUEST_METHOD'] = env['REQUEST_METHOD']
|
91
|
+
env['REQUEST_METHOD'] = new_method
|
102
92
|
end
|
93
|
+
end
|
103
94
|
|
95
|
+
def after_call env
|
96
|
+
if env.key? 'rackful.method_spoofing.input'
|
97
|
+
env['rack.input'] = env['rackful.method_spoofing.input']
|
98
|
+
env.delete 'rackful.method_spoofing.input'
|
99
|
+
end
|
104
100
|
end
|
101
|
+
|
102
|
+
end # Rackful::MethodSpoofing
|
@@ -12,11 +12,9 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
|
-
|
15
|
+
require 'rackful'
|
16
16
|
require 'rack/request'
|
17
17
|
|
18
|
-
module Rackful
|
19
|
-
|
20
18
|
=begin markdown
|
21
19
|
Rack middleware, inspired by {Rack::RelativeRedirect}.
|
22
20
|
|
@@ -31,28 +29,44 @@ Differences with {Rack::RelativeRedirect}:
|
|
31
29
|
- uses Rack::Request::base_url for creating absolute URIs.
|
32
30
|
- the `Location:` header, if present, is always rectified, independent of the
|
33
31
|
HTTP status code.
|
32
|
+
@since 0.0.1
|
34
33
|
=end
|
35
|
-
class RelativeLocation
|
34
|
+
class Rackful::RelativeLocation
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
def initialize(app)
|
37
|
+
@app = app
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
res[1]['Location'] = request.base_url + location
|
40
|
+
def call_old(env)
|
41
|
+
res = @app.call(env)
|
42
|
+
if ( location = res[1]['Location'] ) and
|
43
|
+
! %r{\A[a-z]+://}.match(location)
|
44
|
+
request = Rack::Request.new env
|
45
|
+
unless '/' == location[0, 1]
|
46
|
+
path = ( res[1]['Content-Location'] || request.path ).dup
|
47
|
+
path[ %r{[^/]*\z} ] = ''
|
48
|
+
location = File.expand_path( location, path )
|
52
49
|
end
|
53
|
-
|
50
|
+
if '/' == location[0, 1]
|
51
|
+
location = request.base_url + location
|
52
|
+
end
|
53
|
+
res[1]['Location'] = location
|
54
54
|
end
|
55
|
+
res
|
56
|
+
end
|
55
57
|
|
56
|
-
|
58
|
+
def call(env)
|
59
|
+
res = @app.call(env)
|
60
|
+
request = nil
|
61
|
+
['Location', 'Content-Location'].each do
|
62
|
+
|header|
|
63
|
+
if ( location = res[1][header] ) and
|
64
|
+
'/' == location[0,1]
|
65
|
+
request ||= Rack::Request.new env
|
66
|
+
res[1][header] = request.base_url + location
|
67
|
+
end
|
68
|
+
end
|
69
|
+
res
|
70
|
+
end
|
57
71
|
|
58
|
-
end # Rackful
|
72
|
+
end # Rackful::RelativeLocation
|