roadforest 0.0.3 → 0.1
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.
- checksums.yaml +7 -0
- data/examples/file-management.rb +7 -9
- data/lib/roadforest/application/dispatcher.rb +44 -31
- data/lib/roadforest/application/parameters.rb +5 -4
- data/lib/roadforest/application/path-provider.rb +14 -1
- data/lib/roadforest/application/route-adapter.rb +15 -4
- data/lib/roadforest/application/services-host.rb +41 -7
- data/lib/roadforest/application.rb +4 -8
- data/lib/roadforest/authorization.rb +231 -0
- data/lib/roadforest/blob-model.rb +2 -11
- data/lib/roadforest/content-handling/engine.rb +12 -6
- data/lib/roadforest/content-handling/handler-wrap.rb +45 -0
- data/lib/roadforest/content-handling/media-type.rb +20 -12
- data/lib/roadforest/content-handling/type-handler.rb +76 -0
- data/lib/roadforest/content-handling/type-handlers/jsonld.rb +2 -146
- data/lib/roadforest/content-handling/type-handlers/rdf-handler.rb +38 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/document-environment.rb +34 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/object-environment.rb +62 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/property-environment.rb +46 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/render-engine.rb +574 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/render-environment.rb +144 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer/subject-environment.rb +80 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa-writer.rb +163 -0
- data/lib/roadforest/content-handling/type-handlers/rdfa.rb +175 -0
- data/lib/roadforest/content-handling/type-handlers/rdfpost.rb +297 -0
- data/lib/roadforest/debug.rb +10 -0
- data/lib/roadforest/http/graph-response.rb +3 -7
- data/lib/roadforest/http/graph-transfer.rb +28 -106
- data/lib/roadforest/http/keychain.rb +79 -0
- data/lib/roadforest/http/message.rb +9 -1
- data/lib/roadforest/http/user-agent.rb +115 -0
- data/lib/roadforest/http.rb +8 -0
- data/lib/roadforest/model.rb +48 -3
- data/lib/roadforest/rdf/graph-focus.rb +5 -3
- data/lib/roadforest/rdf/graph-store.rb +4 -2
- data/lib/roadforest/rdf/normalization.rb +6 -1
- data/lib/roadforest/remote-host.rb +22 -7
- data/lib/roadforest/resource/rdf/read-only.rb +15 -1
- data/lib/roadforest/templates/base/doc.haml +13 -0
- data/lib/roadforest/templates/base/property_value.haml +12 -0
- data/lib/roadforest/templates/base/property_values.haml +6 -0
- data/lib/roadforest/templates/base/subject.haml +4 -0
- data/lib/roadforest/templates/distiller/doc.haml +20 -0
- data/lib/roadforest/templates/distiller/nil-object.haml +1 -0
- data/lib/roadforest/templates/distiller/property_value.haml +7 -0
- data/lib/roadforest/templates/distiller/property_values.haml +7 -0
- data/lib/roadforest/templates/distiller/subject.haml +5 -0
- data/lib/roadforest/templates/min/doc.haml +10 -0
- data/lib/roadforest/templates/min/property_values.haml +7 -0
- data/lib/roadforest/templates/min/subject.haml +2 -0
- data/lib/roadforest/templates/nil-object.haml +0 -0
- data/lib/roadforest/templates/node-object.haml +1 -0
- data/lib/roadforest/templates/object.haml +1 -0
- data/lib/roadforest/templates/uri-object.haml +1 -0
- data/lib/roadforest/templates/xml-literal-object.haml +1 -0
- data/lib/roadforest/utility/class-registry.rb +4 -0
- data/spec/client.rb +119 -77
- data/spec/{excon-adapater.rb → excon-adapter.rb} +4 -0
- data/spec/full-integration.rb +6 -2
- data/spec/graph-store.rb +1 -1
- data/spec/media-types.rb +29 -2
- metadata +102 -125
@@ -0,0 +1,297 @@
|
|
1
|
+
require 'roadforest/content-handling/type-handlers/rdf-handler'
|
2
|
+
|
3
|
+
module RoadForest
|
4
|
+
module MediaType
|
5
|
+
module Handlers
|
6
|
+
#application/x-www-form-urlencoded
|
7
|
+
class RDFPost < RDFHandler
|
8
|
+
|
9
|
+
#c.f. http://www.lsrn.org/semweb/rdfpost.html
|
10
|
+
class Reader < ::RDF::Reader
|
11
|
+
module St
|
12
|
+
class State
|
13
|
+
def initialize(reader)
|
14
|
+
@reader = reader
|
15
|
+
@accept_hash = cleanup(accept_list)
|
16
|
+
end
|
17
|
+
|
18
|
+
def cleanup(accept_list)
|
19
|
+
hash = Hash.new{ accept_list[nil] }
|
20
|
+
accept_list.each_key do |key|
|
21
|
+
next if key.nil?
|
22
|
+
hash[key.to_s] = accept_list[key]
|
23
|
+
end
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def blank_node(name)
|
28
|
+
::RDF::Node.new(name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def base_uri
|
32
|
+
::RDF::URI.intern(@reader.options[:base_uri])
|
33
|
+
end
|
34
|
+
|
35
|
+
def uri(string)
|
36
|
+
base_uri.join(string)
|
37
|
+
end
|
38
|
+
|
39
|
+
def prefix_uri(name)
|
40
|
+
::RDF::URI.intern(@reader.options[:prefixes][name])
|
41
|
+
end
|
42
|
+
|
43
|
+
def clear_subject
|
44
|
+
@reader.subject = nil
|
45
|
+
@reader.subject_prefix = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def clear_predicate
|
49
|
+
@reader.predicate = nil
|
50
|
+
@reader.predicate_prefix = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def clear_object
|
54
|
+
@reader.object = nil
|
55
|
+
@reader.object_prefix = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def consume_next(name)
|
59
|
+
consume
|
60
|
+
next_state(name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def consume
|
64
|
+
@reader.consume_pair
|
65
|
+
end
|
66
|
+
|
67
|
+
def triple_complete
|
68
|
+
@reader.new_triple = true
|
69
|
+
end
|
70
|
+
|
71
|
+
def next_state(name)
|
72
|
+
@reader.current_state = @reader.states.fetch(name)
|
73
|
+
end
|
74
|
+
|
75
|
+
def accept(key, value)
|
76
|
+
#puts "#{[self.class.to_s.sub(/.*:/,''), key,
|
77
|
+
#value.sub(/\s*\Z/,'')].inspect}"
|
78
|
+
@accept_hash[key][value.sub(/\s*\Z/,'')]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class RDF < State
|
83
|
+
def accept_list
|
84
|
+
{ rdf: proc{|v| consume_next(:def_ns_decl) },
|
85
|
+
nil => proc{ consume } }
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class DefNsDecl < State
|
90
|
+
def accept_list
|
91
|
+
{ v: proc {|v| consume_next(:ns_decl); @reader.options[:prefixes][nil] = v},
|
92
|
+
nil => proc{|v| next_state(:ns_decl)}}
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class NsDecl < State
|
97
|
+
def accept_list
|
98
|
+
{ n: proc{|v| consume_next(:ns_decl_suffix); @reader.namespace_prefix = v},
|
99
|
+
nil => proc{|v| next_state(:subject)}}
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class NsDeclSuffix < State
|
104
|
+
def accept_list
|
105
|
+
{ v: proc{|v| consume_next(:ns_decl); @reader.options[:prefixes][@reader.namespace_prefix] = v},
|
106
|
+
nil => proc{ next_state(:ns_decl)}}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class SkipToSubject < State
|
111
|
+
def accept_list
|
112
|
+
next_is_subject = proc{ next_state(:subject); clear_subject; clear_predicate; clear_object }
|
113
|
+
{
|
114
|
+
sb: next_is_subject,
|
115
|
+
su: next_is_subject,
|
116
|
+
sv: next_is_subject,
|
117
|
+
sn: next_is_subject,
|
118
|
+
nil => proc{ consume }
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class SkipToSubjectOrPred < SkipToSubject
|
124
|
+
def accept_list
|
125
|
+
next_is_pred = proc{ next_state(:predicate); clear_predicate; clear_object }
|
126
|
+
super.merge( pu: next_is_pred, pv: next_is_pred, pn: next_is_pred )
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
class Subject < State
|
131
|
+
def accept_list
|
132
|
+
{
|
133
|
+
sb: proc{|v| consume_next(:predicate); @reader.subject = blank_node(v)},
|
134
|
+
su: proc{|v| consume_next(:predicate); @reader.subject = uri(v)},
|
135
|
+
sv: proc{|v| consume_next(:predicate); @reader.subject = prefix_url(nil) / v},
|
136
|
+
sn: proc{|v| consume_next(:subject_suffix); @reader.subject_prefix = prefix_uri(v)},
|
137
|
+
nil => proc{ consume }
|
138
|
+
}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class SubjectSuffix < State
|
143
|
+
def accept_list
|
144
|
+
{
|
145
|
+
sv: proc{|v| consume_next(:predicate); @reader.subject = @reader.subject_prefix/v},
|
146
|
+
nil => proc{ next_state(:skip_to_subject)}
|
147
|
+
}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
class Predicate < State
|
152
|
+
def accept_list
|
153
|
+
{
|
154
|
+
pu: proc {|v| @reader.predicate = uri(v); consume_next(:object)},
|
155
|
+
pv: proc {|v| @reader.predicate = prefix_uri(nil) / v; consume_next(:object)},
|
156
|
+
pn: proc {|v| @reader.predicate_prefix = prefix_uri(v); consume_next(:predicate_suffix)},
|
157
|
+
nil => proc { next_state(:skip_to_subject)}
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class PredicateSuffix < State
|
163
|
+
def accept_list
|
164
|
+
{
|
165
|
+
pv: proc{|v| consume_next(:object); @reader.predicate = @reader.predicate_prefix/v},
|
166
|
+
nil => proc{ next_state(:skip_to_subject)}
|
167
|
+
}
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
class Object < State
|
172
|
+
def accept_list
|
173
|
+
{
|
174
|
+
ob: proc{|v| consume; triple_complete; @reader.object = blank_node(v)},
|
175
|
+
ou: proc{|v| consume; triple_complete; @reader.object = uri(v)},
|
176
|
+
ov: proc{|v| consume; triple_complete; @reader.object = prefix_uri(nil) / v},
|
177
|
+
on: proc{|v| consume_next(:object_suffix); @reader.object_prefix = prefix_uri(v)},
|
178
|
+
ol: proc{|v| consume_next(:type_or_lang); @reader.object = v},
|
179
|
+
ll: proc{|v| consume_next(:object_literal); @reader.object_lang = v},
|
180
|
+
lt: proc{|v| consume_next(:object_literal); @reader.object_type = v},
|
181
|
+
nil => proc{ next_state(:skip_to_subj_or_pred) }
|
182
|
+
}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
class ObjectSuffix < State
|
187
|
+
def accept_list
|
188
|
+
{
|
189
|
+
ov: proc{|v| consume_next(:object); triple_complete; @reader.object = @reader.object_prefix/v},
|
190
|
+
nil => proc{ next_state(:skip_to_subj_or_pred)}
|
191
|
+
}
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class ObjectLiteral < State
|
196
|
+
def accept_list
|
197
|
+
{
|
198
|
+
ol: proc{|v| consume_next(:type_or_lang); @reader.object = v},
|
199
|
+
ll: proc{|v| consume; @reader.object_lang = v},
|
200
|
+
lt: proc{|v| consume; @reader.object_type = v},
|
201
|
+
nil => proc{ next_state(:skip_to_subj_or_pred)}
|
202
|
+
}
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class TypeOrLang < State
|
207
|
+
def accept_list
|
208
|
+
{
|
209
|
+
ll: proc{|v| consume; @reader.object_lang = v},
|
210
|
+
lt: proc{|v| consume; @reader.object_type = v},
|
211
|
+
nil => proc{ next_state(:object); triple_complete }
|
212
|
+
}
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def initialize(input, options=nil, &block)
|
218
|
+
super(input, options||{}, &block)
|
219
|
+
|
220
|
+
@lineno = 0
|
221
|
+
@new_triple = false
|
222
|
+
|
223
|
+
@states = {
|
224
|
+
:rdf => St::RDF.new(self),
|
225
|
+
:def_ns_decl => St::DefNsDecl.new(self),
|
226
|
+
:ns_decl => St::NsDecl.new(self),
|
227
|
+
:ns_decl_suffix => St::NsDeclSuffix.new(self),
|
228
|
+
:skip_to_subject => St::SkipToSubject.new(self),
|
229
|
+
:skip_to_subj_or_pred => St::SkipToSubjectOrPred.new(self),
|
230
|
+
:subject => St::Subject.new(self),
|
231
|
+
:subject_suffix => St::SubjectSuffix.new(self),
|
232
|
+
:predicate => St::Predicate.new(self),
|
233
|
+
:predicate_suffix => St::PredicateSuffix.new(self),
|
234
|
+
:object => St::Object.new(self),
|
235
|
+
:object_suffix => St::ObjectSuffix.new(self),
|
236
|
+
:object_literal => St::ObjectLiteral.new(self),
|
237
|
+
:type_or_lang => St::TypeOrLang.new(self)
|
238
|
+
}
|
239
|
+
|
240
|
+
@current_state = @states.fetch(:rdf)
|
241
|
+
end
|
242
|
+
attr_reader :lineno, :states
|
243
|
+
attr_accessor :current_state
|
244
|
+
attr_accessor :new_triple, :subject, :predicate, :object
|
245
|
+
attr_accessor :object_type, :object_lang
|
246
|
+
attr_accessor :namespace_prefix, :subject_prefix, :predicate_prefix, :object_prefix
|
247
|
+
|
248
|
+
def read_triple
|
249
|
+
if @lineno >= @input.length
|
250
|
+
raise EOFError
|
251
|
+
end
|
252
|
+
while @lineno < @input.length
|
253
|
+
@current_state.accept(*@input[@lineno])
|
254
|
+
if @new_triple
|
255
|
+
@new_triple = false
|
256
|
+
return build_triple
|
257
|
+
end
|
258
|
+
end
|
259
|
+
return build_triple
|
260
|
+
end
|
261
|
+
|
262
|
+
def build_triple
|
263
|
+
object = @object
|
264
|
+
if object.is_a? String
|
265
|
+
object = ::RDF::Literal.new(object, :datatype => object_type, :language => object_lang)
|
266
|
+
end
|
267
|
+
@object_type = nil
|
268
|
+
@object_lang = nil
|
269
|
+
|
270
|
+
[@subject, @predicate, object]
|
271
|
+
end
|
272
|
+
|
273
|
+
def rewind
|
274
|
+
@lineno = 0
|
275
|
+
end
|
276
|
+
|
277
|
+
def consume_pair
|
278
|
+
@lineno += 1
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def local_to_network(base_uri, graph)
|
283
|
+
end
|
284
|
+
|
285
|
+
def network_to_local(base_uri, list)
|
286
|
+
raise "Invalid base uri: #{base_uri.inspect}" if base_uri.nil?
|
287
|
+
graph = ::RDF::Graph.new
|
288
|
+
reader = Reader.new(list, :base_uri => base_uri.to_s)
|
289
|
+
reader.each_statement do |statement|
|
290
|
+
graph.insert(statement)
|
291
|
+
end
|
292
|
+
graph
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
@@ -1,14 +1,10 @@
|
|
1
1
|
module RoadForest
|
2
2
|
module HTTP
|
3
3
|
class BaseResponse
|
4
|
-
attr_reader :
|
4
|
+
attr_reader :url, :response
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@
|
8
|
-
end
|
9
|
-
|
10
|
-
def url
|
11
|
-
request.url
|
6
|
+
def initialize(url, response)
|
7
|
+
@url, @response = url, response
|
12
8
|
end
|
13
9
|
|
14
10
|
def etag
|
@@ -1,26 +1,15 @@
|
|
1
|
-
require 'roadforest/http
|
2
|
-
require 'roadforest/http/graph-response'
|
1
|
+
require 'roadforest/http'
|
3
2
|
require 'roadforest/content-handling/engine'
|
4
3
|
|
5
4
|
module RoadForest
|
6
5
|
module HTTP
|
7
6
|
class GraphTransfer
|
8
|
-
class Retryable < StandardError; end
|
9
|
-
|
10
|
-
attr_accessor :http_client, :trace
|
11
7
|
attr_writer :type_handling
|
12
|
-
|
8
|
+
attr_accessor :user_agent
|
13
9
|
|
14
|
-
def initialize
|
15
|
-
@
|
10
|
+
def initialize(user_agent)
|
11
|
+
@user_agent = user_agent
|
16
12
|
@type_preferences = Hash.new{|h,k| k.nil? ? "*/*" : h[nil]}
|
17
|
-
@graph_cache = Hash.new do |cache, url|
|
18
|
-
cache[url] = {}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def type_handling
|
23
|
-
@type_handling ||= ContentHandling::Engine.default
|
24
13
|
end
|
25
14
|
|
26
15
|
def put(url, graph)
|
@@ -35,121 +24,54 @@ module RoadForest
|
|
35
24
|
make_request("POST", url, graph)
|
36
25
|
end
|
37
26
|
|
38
|
-
def make_request(method, url, graph=
|
39
|
-
|
27
|
+
def make_request(method, url, graph, retry_limit=5)
|
28
|
+
headers = {"Accept" => type_handling.parsers.types.accept_header}
|
29
|
+
body = nil
|
40
30
|
|
41
|
-
|
42
|
-
|
43
|
-
|
31
|
+
if(%w{POST PUT PATCH}.include? method.upcase)
|
32
|
+
content_type = best_type_for(url)
|
33
|
+
renderer = type_handling.choose_renderer(content_type)
|
34
|
+
headers["Content-Type"] = renderer.content_type_header
|
35
|
+
body = renderer.from_graph(url, graph)
|
36
|
+
end
|
44
37
|
|
45
|
-
response =
|
38
|
+
response = user_agent.make_request(method, url, headers, body, retry_limit)
|
46
39
|
|
47
|
-
case response
|
48
|
-
when
|
49
|
-
|
50
|
-
|
51
|
-
return response
|
52
|
-
when GraphResponse
|
53
|
-
return response
|
40
|
+
case response.status
|
41
|
+
when 415 #Type not accepted
|
42
|
+
record_accept_header(url, response.headers["Accept"])
|
43
|
+
raise Retryable
|
54
44
|
end
|
55
|
-
end
|
56
45
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
46
|
+
build_response(url, response)
|
47
|
+
rescue Retryable
|
48
|
+
raise unless (retry_limit -= 1) > 0
|
49
|
+
retry
|
61
50
|
end
|
62
51
|
|
63
|
-
def
|
64
|
-
|
65
|
-
when "GET", "HEAD", "DELETE"
|
66
|
-
raise "Method #{method} requires an empty body" unless graph.nil?
|
67
|
-
when "POST", "PATCH", "PUT"
|
68
|
-
raise "Method #{method} requires a body" if graph.nil?
|
69
|
-
#when "OPTION", "TRACE" #Need to put verbs where they go
|
70
|
-
else
|
71
|
-
raise "Unrecognized method: #{method}"
|
72
|
-
end
|
52
|
+
def type_handling
|
53
|
+
@type_handling ||= ContentHandling::Engine.default
|
73
54
|
end
|
74
55
|
|
75
56
|
def best_type_for(url)
|
76
57
|
return @type_preferences[url]
|
77
58
|
end
|
78
59
|
|
79
|
-
def setup_request(method, url)
|
80
|
-
request = Request.new(method, url)
|
81
|
-
request.headers["Accept"] = type_handling.parsers.types.accept_header
|
82
|
-
add_cache_headers(request)
|
83
|
-
request
|
84
|
-
end
|
85
|
-
|
86
|
-
def add_cache_headers(request)
|
87
|
-
return unless request.method == "GET"
|
88
|
-
return unless graph_cache.has_key?(request.url)
|
89
|
-
cached = graph_cache[request.url]
|
90
|
-
return if cached.empty?
|
91
|
-
request.headers["If-None-Match"] = cached.keys.join(", ")
|
92
|
-
end
|
93
|
-
|
94
|
-
def select_renderer(url)
|
95
|
-
end
|
96
|
-
|
97
60
|
def record_accept_header(url, types)
|
98
61
|
return if types.nil? or types.empty?
|
99
62
|
@type_preferences[nil] = types
|
100
63
|
@type_preferences[url] = types
|
101
64
|
end
|
102
65
|
|
103
|
-
def
|
104
|
-
return unless request.needs_body?
|
105
|
-
|
106
|
-
content_type = best_type_for(request.url)
|
107
|
-
renderer = type_handling.choose_renderer(content_type)
|
108
|
-
request.headers["Content-Type"] = renderer.content_type_header
|
109
|
-
request.body_string = renderer.from_graph(request.url, graph)
|
110
|
-
end
|
111
|
-
|
112
|
-
def trace_message(message)
|
113
|
-
return unless @trace
|
114
|
-
@trace = $stdout unless IO === @trace
|
115
|
-
@trace.puts message.inspect
|
116
|
-
end
|
117
|
-
|
118
|
-
def send_request(request, graph)
|
119
|
-
retry_limit ||= 5
|
120
|
-
#Check expires headers on received
|
121
|
-
render_graph(graph, request)
|
122
|
-
|
123
|
-
trace_message(request)
|
124
|
-
response = http_client.do_request(request)
|
125
|
-
trace_message(response)
|
126
|
-
case response.status
|
127
|
-
when 304 #Not Modified
|
128
|
-
response = graph_cache.fetch(request.url).fetch(response.etag)
|
129
|
-
trace_message(response)
|
130
|
-
return response
|
131
|
-
when 415 #Type not accepted
|
132
|
-
record_accept_header(request.url, response.headers["Accept"])
|
133
|
-
raise Retryable
|
134
|
-
end
|
135
|
-
return response
|
136
|
-
rescue Retryable
|
137
|
-
raise unless (retry_limit -= 1) > 0
|
138
|
-
retry
|
139
|
-
end
|
140
|
-
|
141
|
-
def parse_response(base_uri, response)
|
66
|
+
def build_response(url, response)
|
142
67
|
parser = type_handling.choose_parser(response.headers["Content-Type"])
|
143
|
-
parser.to_graph(
|
144
|
-
end
|
68
|
+
graph = parser.to_graph(url, response.body_string)
|
145
69
|
|
146
|
-
|
147
|
-
graph = parse_response(request.url, response)
|
148
|
-
response = GraphResponse.new(request, response)
|
70
|
+
response = GraphResponse.new(url, response)
|
149
71
|
response.graph = graph
|
150
72
|
return response
|
151
73
|
rescue ContentHandling::UnrecognizedType
|
152
|
-
return UnparseableResponse.new(
|
74
|
+
return UnparseableResponse.new(url, response)
|
153
75
|
end
|
154
76
|
end
|
155
77
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'addressable/uri'
|
3
|
+
|
4
|
+
module RoadForest
|
5
|
+
module HTTP
|
6
|
+
#Manages user credentials for HTTP Basic auth
|
7
|
+
class Keychain
|
8
|
+
class Credentials < Struct.new(:user, :secret)
|
9
|
+
def header_value
|
10
|
+
"Basic #{Base64.strict_encode64("#{user}:#{secret}")}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@realm_for_url = {}
|
16
|
+
@with_realm = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(url, user, secret, realm=nil)
|
20
|
+
creds = Credentials.new(user, secret)
|
21
|
+
add_credentials(url, creds, realm || :default)
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_credentials(url, creds, realm)
|
25
|
+
if url.to_s[-1] != "/"
|
26
|
+
url << "/"
|
27
|
+
end
|
28
|
+
@realm_for_url[url.to_s] = realm
|
29
|
+
|
30
|
+
url = Addressable::URI.parse(url)
|
31
|
+
url.path = "/"
|
32
|
+
@with_realm[[url.to_s,realm]] = creds
|
33
|
+
end
|
34
|
+
|
35
|
+
BASIC_SCHEME = /basic\s+realm=(?<q>['"])(?<realm>(?:(?!['"]).)*)\k<q>/i
|
36
|
+
|
37
|
+
def challenge_response(url, challenge)
|
38
|
+
if (match = BASIC_SCHEME.match(challenge)).nil?
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
realm = match[:realm]
|
42
|
+
|
43
|
+
response(url, realm)
|
44
|
+
end
|
45
|
+
|
46
|
+
def response(url, realm)
|
47
|
+
lookup_url = Addressable::URI.parse(url)
|
48
|
+
lookup_url.path = "/"
|
49
|
+
creds = @with_realm[[lookup_url.to_s,realm]]
|
50
|
+
if creds.nil? and not realm.nil?
|
51
|
+
creds = missing_credentials(url, realm)
|
52
|
+
unless creds.nil?
|
53
|
+
add_credentials(url, creds, realm)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
return nil if creds.nil?
|
58
|
+
|
59
|
+
return creds.header_value
|
60
|
+
end
|
61
|
+
|
62
|
+
def missing_credentials(url, realm)
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def preemptive_response(url)
|
67
|
+
url = Addressable::URI.parse(url)
|
68
|
+
|
69
|
+
while (realm = @realm_for_url[url.to_s]).nil?
|
70
|
+
new_url = url.join("..")
|
71
|
+
break if new_url == url
|
72
|
+
url = new_url
|
73
|
+
end
|
74
|
+
|
75
|
+
return response(url, realm)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -47,9 +47,17 @@ module RoadForest
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def inspection_payload
|
50
|
+
if body.respond_to? :pos
|
51
|
+
[headers.inspect, inspection_stream]
|
52
|
+
else
|
53
|
+
[headers.inspect, body]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def inspection_stream
|
50
58
|
old_pos = body.pos
|
51
59
|
body.rewind
|
52
|
-
|
60
|
+
body.read
|
53
61
|
ensure
|
54
62
|
body.pos = old_pos
|
55
63
|
end
|