infod 0.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.
- data/bin/infod +37 -0
- data/config.ru +3 -0
- data/infod/Es/fs.rb +154 -0
- data/infod/Es/groonga.rb +101 -0
- data/infod/Es/redis.rb +3 -0
- data/infod/Es/sqlite.rb +3 -0
- data/infod/Es.rb +67 -0
- data/infod/H.rb +29 -0
- data/infod/K.rb +197 -0
- data/infod/N.rb +248 -0
- data/infod/Rb.rb +71 -0
- data/infod/Th/404.rb +55 -0
- data/infod/Th/500.rb +10 -0
- data/infod/Th/GET.rb +132 -0
- data/infod/Th/HEAD.rb +5 -0
- data/infod/Th/PATCH.rb +5 -0
- data/infod/Th/POST.rb +19 -0
- data/infod/Th/local.rb +22 -0
- data/infod/Th/uid.rb +24 -0
- data/infod/Th.rb +110 -0
- data/infod/W/audio.rb +56 -0
- data/infod/W/blog.rb +3 -0
- data/infod/W/cal.rb +110 -0
- data/infod/W/chat.rb +81 -0
- data/infod/W/color.rb +28 -0
- data/infod/W/core.rb +77 -0
- data/infod/W/css.rb +24 -0
- data/infod/W/csv.rb +13 -0
- data/infod/W/du.rb +35 -0
- data/infod/W/edit.rb +8 -0
- data/infod/W/examine/examine.rb +59 -0
- data/infod/W/examine/exhibit.rb +34 -0
- data/infod/W/examine/hist.rb +55 -0
- data/infod/W/examine/history.rb +19 -0
- data/infod/W/examine/normal.rb +31 -0
- data/infod/W/examine/protovis.rb +30 -0
- data/infod/W/examine/sw.rb +114 -0
- data/infod/W/examine/time/graph.rb +86 -0
- data/infod/W/examine/time/line.rb +24 -0
- data/infod/W/feed.rb +116 -0
- data/infod/W/find.rb +24 -0
- data/infod/W/forum.rb +3 -0
- data/infod/W/grep.rb +27 -0
- data/infod/W/html.rb +143 -0
- data/infod/W/image.rb +61 -0
- data/infod/W/json.rb +44 -0
- data/infod/W/kv.rb +66 -0
- data/infod/W/ls.rb +50 -0
- data/infod/W/mail.rb +248 -0
- data/infod/W/page.rb +30 -0
- data/infod/W/pdf.rb +16 -0
- data/infod/W/post.rb +9 -0
- data/infod/W/rdf.rb +32 -0
- data/infod/W/schema.rb +172 -0
- data/infod/W/search.rb +33 -0
- data/infod/W/shell.rb +30 -0
- data/infod/W/source.rb +35 -0
- data/infod/W/table.rb +87 -0
- data/infod/W/text.rb +94 -0
- data/infod/W/tree.rb +26 -0
- data/infod/W/vfs.rb +175 -0
- data/infod/W/wiki.rb +18 -0
- data/infod/W.rb +34 -0
- data/infod/Y.rb +17 -0
- data/infod/infod.rb +13 -0
- data/infod.rb +13 -0
- metadata +129 -0
data/infod/N.rb
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
%w{base64 cgi shellwords}.each{|r|require(r)}
|
2
|
+
|
3
|
+
def E e
|
4
|
+
return e if e.class == E
|
5
|
+
return e unless e
|
6
|
+
E.new e
|
7
|
+
end
|
8
|
+
|
9
|
+
class E
|
10
|
+
def E.[] u; E u end
|
11
|
+
def E e=uri; super e end
|
12
|
+
|
13
|
+
attr_reader :uri
|
14
|
+
def initialize uri; @uri = uri.to_s end
|
15
|
+
|
16
|
+
def base
|
17
|
+
File.basename path
|
18
|
+
end
|
19
|
+
|
20
|
+
def bare
|
21
|
+
base.sub(/\.#{ext}$/,'')
|
22
|
+
rescue
|
23
|
+
base
|
24
|
+
end
|
25
|
+
|
26
|
+
def docBase
|
27
|
+
readlink.uri.
|
28
|
+
split(/#/)[0].E.
|
29
|
+
do{|d|
|
30
|
+
d.dirname.as d.bare}
|
31
|
+
end
|
32
|
+
|
33
|
+
def ef; @ef ||= docBase.a('.e') end
|
34
|
+
def nt; @nt ||= docBase.a('.nt') end
|
35
|
+
def ttl; @ttl ||= docBase.a('.ttl') end
|
36
|
+
|
37
|
+
def docBaseURI
|
38
|
+
u = URI uri
|
39
|
+
s = u.scheme
|
40
|
+
p = u.path
|
41
|
+
p = '/' if p.empty?
|
42
|
+
((s ? s + '://' : '') + # scheme
|
43
|
+
u.host + # host
|
44
|
+
File.dirname(p).t + # path
|
45
|
+
File.basename(p)[0..-(File.extname(p).size+1)]).E # doc
|
46
|
+
end
|
47
|
+
|
48
|
+
def frag
|
49
|
+
uri.frag
|
50
|
+
end
|
51
|
+
|
52
|
+
def docs
|
53
|
+
(e ? [self] : []). # directly-referenced
|
54
|
+
concat(docBase.glob ".{e,html,n3,nt,owl,rdf,ttl}"). # docs
|
55
|
+
concat((d? && uri[-1]=='/') ? c : []) # trailing slash -> children
|
56
|
+
end
|
57
|
+
|
58
|
+
def dirname
|
59
|
+
no.dirname.E
|
60
|
+
end
|
61
|
+
|
62
|
+
# generate URL for non-URL identifier (mail ID, Tag URI..)
|
63
|
+
def url
|
64
|
+
path? ? uri : Prefix + (CGI.escape uri)
|
65
|
+
end
|
66
|
+
|
67
|
+
# URI extension :: E -> string
|
68
|
+
def ext
|
69
|
+
File.extname(uri).tail||''
|
70
|
+
end
|
71
|
+
|
72
|
+
def label
|
73
|
+
uri.label
|
74
|
+
end
|
75
|
+
|
76
|
+
def expand
|
77
|
+
uri.expand.E
|
78
|
+
end
|
79
|
+
|
80
|
+
# concatenate URIs with separator
|
81
|
+
# s :: E -> E
|
82
|
+
def s b
|
83
|
+
u.a E(b).path
|
84
|
+
end
|
85
|
+
|
86
|
+
def prependURI s
|
87
|
+
(s + uri).E
|
88
|
+
end
|
89
|
+
|
90
|
+
def appendURI s
|
91
|
+
(uri + s).E
|
92
|
+
end
|
93
|
+
alias_method :a, :appendURI
|
94
|
+
alias_method :+, :appendURI
|
95
|
+
|
96
|
+
def appendSlashURI s
|
97
|
+
E uri.t + s
|
98
|
+
end
|
99
|
+
alias_method :as, :appendSlashURI
|
100
|
+
|
101
|
+
# path? :: E -> Bool
|
102
|
+
def path?
|
103
|
+
uri.path?
|
104
|
+
end
|
105
|
+
|
106
|
+
# path :: E -> String
|
107
|
+
def path
|
108
|
+
@path ||=
|
109
|
+
path? ? (uri.match(/^\//) ?
|
110
|
+
uri : '/'+uri) :
|
111
|
+
'/E/'+uri.h.dive[0..5]+(Base64.urlsafe_encode64 uri)
|
112
|
+
end
|
113
|
+
|
114
|
+
def u
|
115
|
+
@u ||= E (f ? dirname + '/.' + File.basename(path) : path.t + E::S)
|
116
|
+
end
|
117
|
+
|
118
|
+
# E (_ _ o) -> E o
|
119
|
+
def ro
|
120
|
+
uri.split(/#{E::S}\//)[-1].unpath false
|
121
|
+
end
|
122
|
+
|
123
|
+
def sh
|
124
|
+
d.force_encoding('UTF-8').sh
|
125
|
+
end
|
126
|
+
|
127
|
+
# literal -> URI
|
128
|
+
def literal o
|
129
|
+
return literalBlob o unless o.class == String
|
130
|
+
return literalURI o if (Literal[uri] || o.size<=88) && !o.match(/\//)
|
131
|
+
return E o if o.match %r{\A[a-z]+://[^\s]+\Z}
|
132
|
+
literalBlob o
|
133
|
+
end
|
134
|
+
|
135
|
+
# pathname for short literals
|
136
|
+
def literalURI o
|
137
|
+
E "/u/"+(Literal[uri] && o.gsub(/[\.:\-T+]/,'/'))+'/'+o
|
138
|
+
end
|
139
|
+
|
140
|
+
def literalBlobURI o
|
141
|
+
if o.class == String
|
142
|
+
E "/blob/"+o.h.dive
|
143
|
+
else
|
144
|
+
E "/json/"+[o].to_json.h.dive
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# spaceship
|
149
|
+
def <=> c
|
150
|
+
to_s <=> c.to_s
|
151
|
+
end
|
152
|
+
|
153
|
+
# example: E('wiggly').to_s -> "wiggly"
|
154
|
+
def to_s # string
|
155
|
+
uri
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
class Hash
|
161
|
+
def uri
|
162
|
+
self["uri"]
|
163
|
+
end
|
164
|
+
def url; self.E.url end
|
165
|
+
def label
|
166
|
+
self[E::Label] || uri.label
|
167
|
+
end
|
168
|
+
def E
|
169
|
+
E.new uri
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Array
|
174
|
+
def E
|
175
|
+
self[0].E if self[0].class==Hash
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class String
|
180
|
+
def dive
|
181
|
+
self[0..1]+'/'+
|
182
|
+
self[2..3]+'/'+
|
183
|
+
self[4..-1]
|
184
|
+
end
|
185
|
+
|
186
|
+
# expand qname-style identifier to URI
|
187
|
+
Expand={}
|
188
|
+
def expand
|
189
|
+
# memoize lookups
|
190
|
+
(Expand.has_key? self) ?
|
191
|
+
Expand[self] :
|
192
|
+
(Expand[self] =
|
193
|
+
match(/([^:]+):([^\/].*)/).do{|e|
|
194
|
+
(E::Abbrev[e[1]]||e[1]+':')+e[2]} ||
|
195
|
+
self )
|
196
|
+
end
|
197
|
+
|
198
|
+
def sh
|
199
|
+
Shellwords.escape self
|
200
|
+
end
|
201
|
+
|
202
|
+
BaseLen = E::FSbase.size.succ
|
203
|
+
|
204
|
+
def pathToURI r = true
|
205
|
+
self[BaseLen..-1].unpath r
|
206
|
+
end
|
207
|
+
|
208
|
+
# string -> E || literal
|
209
|
+
def unpath r=true # dereference literal?
|
210
|
+
|
211
|
+
if m=(match /^([a-z]+:)\/+(.*)/) # URL
|
212
|
+
(m[1]+'//'+m[2]).E
|
213
|
+
|
214
|
+
elsif match /^blob/ # string
|
215
|
+
r ? ('/'+self).E.r : ('/'+self).E
|
216
|
+
|
217
|
+
elsif match /^json/ # JSON
|
218
|
+
r ? (('/'+self).E.r true) : ('/'+self).E
|
219
|
+
|
220
|
+
elsif match /^u\// # trie
|
221
|
+
r ? (File.basename self) : ('/'+self).E
|
222
|
+
|
223
|
+
elsif match /^E\/..\/..\// # !fs-compatible URI
|
224
|
+
self[8..-1].match(/([^.]+)(.*)/).do{|c|
|
225
|
+
(Base64.urlsafe_decode64 c[1]) + c[2]
|
226
|
+
}.E
|
227
|
+
else # path
|
228
|
+
('/'+self).E
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def E
|
233
|
+
E.new self
|
234
|
+
end
|
235
|
+
|
236
|
+
def path?
|
237
|
+
(match /^(\.|\/|https?:\/)/) && true || false
|
238
|
+
end
|
239
|
+
|
240
|
+
def frag
|
241
|
+
split(/#/).pop()
|
242
|
+
end
|
243
|
+
|
244
|
+
def label
|
245
|
+
split(/[\/#]/)[-1]
|
246
|
+
end
|
247
|
+
|
248
|
+
end
|
data/infod/Rb.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Rb Rubidium
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def head; self[0] end
|
5
|
+
def tail; self[1..-1] end
|
6
|
+
def snd; self[1] end
|
7
|
+
def r; self[rand length] end
|
8
|
+
def h; join.h end
|
9
|
+
def intersperse i
|
10
|
+
inject([]){|a,b|a << b << i}[0..-2]
|
11
|
+
end
|
12
|
+
def sum
|
13
|
+
inject 0, &:+
|
14
|
+
end
|
15
|
+
def cr
|
16
|
+
intersperse "\n"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Hash
|
21
|
+
def except *ks
|
22
|
+
clone.do{|h|
|
23
|
+
ks.map{|k|h.delete k}
|
24
|
+
h}
|
25
|
+
end
|
26
|
+
def has_keys ks; ks.each{|k|
|
27
|
+
return false unless has_key? k
|
28
|
+
}; true
|
29
|
+
end
|
30
|
+
def has_any_key ks; ks.each{|k|
|
31
|
+
return true if has_key? k
|
32
|
+
}; false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Float
|
37
|
+
def max i; i > self ? self : i end
|
38
|
+
def min i; i < self ? self : i end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Fixnum
|
42
|
+
def max i; i > self ? self : i end
|
43
|
+
def min i; i < self ? self : i end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Object
|
47
|
+
def id; self end
|
48
|
+
def do; yield self end
|
49
|
+
end
|
50
|
+
|
51
|
+
class String
|
52
|
+
def h; Digest::SHA1.hexdigest self end
|
53
|
+
def hsub h; map{|e|h[e]||e} end
|
54
|
+
def map; each_char.map{|l| yield l}.join end
|
55
|
+
def tail; self[1..-1] end
|
56
|
+
def to_utf8; encode('UTF-8', undef: :replace) end
|
57
|
+
def t; match(/\/$/) ? self : self+'/' end
|
58
|
+
end
|
59
|
+
class FalseClass
|
60
|
+
def do; nil end
|
61
|
+
def to_s; "" end
|
62
|
+
alias_method :to_str,:to_s
|
63
|
+
end
|
64
|
+
|
65
|
+
class NilClass
|
66
|
+
def do; nil end
|
67
|
+
def to_ary; [] end
|
68
|
+
def to_s; "" end
|
69
|
+
alias_method :to_str,:to_s
|
70
|
+
def method_missing *a; nil; end
|
71
|
+
end
|
data/infod/Th/404.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
# 404 response URI
|
4
|
+
E404 = 'req/'+HTTP+'404'
|
5
|
+
|
6
|
+
# 404 response function
|
7
|
+
fn E404,->e,r{
|
8
|
+
u = e.uri # response URI
|
9
|
+
g = {u => {}} # response graph
|
10
|
+
s = g[u] # resource pointer
|
11
|
+
# add request data to response graph
|
12
|
+
r.map{|k,v| s[k] = [v] }
|
13
|
+
s[Type] = [E[HTTP+'404']]
|
14
|
+
s['uri'] = u
|
15
|
+
s['QUERY'] = [r.q]
|
16
|
+
s['ACCEPT']= [r.accept]
|
17
|
+
s['SERVER_SOFTWARE']=[('//'+r['SERVER_NAME']).E]
|
18
|
+
s['http://buzzword.org.uk/rdf/personal-link-types#edit']=[E[u+'?view=edit&graph=_']]
|
19
|
+
%w{CHARSET LANGUAGE ENCODING}.map{|a|s['ACCEPT_'+a] = [(r.accept_ '_' + a)]}
|
20
|
+
# output
|
21
|
+
r.q.delete 'view' # use 404 view if HTML
|
22
|
+
[404,{'Content-Type'=> r.format},[e.render(r.format,g,r)]]}
|
23
|
+
|
24
|
+
# qs y=404 to force a 404 response
|
25
|
+
F['req/404'] = F[E404]
|
26
|
+
|
27
|
+
fn 'view/'+HTTP+'404',->d,e{
|
28
|
+
[H.css('/css/404'),{_: :h1, c: '404'},d.html]}
|
29
|
+
|
30
|
+
# 404.css if fs content is missing
|
31
|
+
fn '/css/404.css/GET',->e,r{
|
32
|
+
[200,{'Content-Type'=>'text/css'},
|
33
|
+
["body {background-color:#000;color:#fff; font-family: sans-serif}
|
34
|
+
a {font-size:1.05em;background-color:#1ef;color:#000;text-decoration:none;padding:.1em}
|
35
|
+
td.key {text-align:right}
|
36
|
+
td.key .frag {font-weight:bold;background-color:#0f0;color:#000;padding-left:.2em;border-radius:.38em 0 0 .38em}
|
37
|
+
td.key .abbr {color:#eee;font-size:.92em}
|
38
|
+
td.val {border-style:dotted;border-width:0 0 .1em 0;border-color:#00f;}"]]}
|
39
|
+
|
40
|
+
# show response-codes for a list of URIs
|
41
|
+
def checkURIs
|
42
|
+
r = uris.select{|u|u.to_s.match /^http/}.map{|u|
|
43
|
+
c = [`curl -IsA 404? "#{u}"`.lines.to_a[0].match(/\d{3}/)[0].to_i,u] # HEAD
|
44
|
+
#c = [`curl -s -o /dev/null -w %{http_code} "#{u}"`.chomp.to_i,u] # GET
|
45
|
+
puts c.join ' '
|
46
|
+
c # status, uri tuple
|
47
|
+
}
|
48
|
+
puts "\n\n"
|
49
|
+
r.map{|c|
|
50
|
+
# show anomalies
|
51
|
+
puts c.join(' ') unless c[0] == 200
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/infod/Th/500.rb
ADDED
data/infod/Th/GET.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
def GET
|
4
|
+
a = @r.accept.values.flatten # acceptable MIME types
|
5
|
+
send(f ? (if (@r.q.has_any_key(['format','graph','view']) || # user-specified view
|
6
|
+
(MIMEcook[mime] && !@r.q.has_key?('raw')) || # view for MIME-type
|
7
|
+
!(a.empty?||a.member?(mime)||a.member?('*/*'))) # file MIME not accepted
|
8
|
+
:GET_resource # invoke resource handler
|
9
|
+
else
|
10
|
+
:GET_img # continue to file handler
|
11
|
+
end) :
|
12
|
+
:GET_resource)
|
13
|
+
rescue Exception => x
|
14
|
+
$stderr.puts 500,x.message,x.backtrace
|
15
|
+
Fn 'backtrace',x,@r
|
16
|
+
end
|
17
|
+
|
18
|
+
def maybeSend m,b,lH=false
|
19
|
+
send? ? # agent already has this version?
|
20
|
+
b.().do{|b| # continue
|
21
|
+
h = {'Content-Type'=> m, 'ETag'=> @r['ETag']} # response header
|
22
|
+
m.match(/^(audio|image|video)/) && # media MIME-type?
|
23
|
+
h.update({'Cache-Control' => 'no-transform'}) # no further compression
|
24
|
+
h.update({'MS-Author-Via' => 'DAV, SPARQL'}) # authoring
|
25
|
+
lH && h.update({'Link' => '<' + (URI.escape uri) + '?format=text/n3>; rel=meta'}) # Link Header - full URI variant
|
26
|
+
b.class == E ? (Nginx ? # nginx env-var
|
27
|
+
[200,h.update({'X-Accel-Redirect' => '/fs' + b.path}),[]] : # Nginx file-handler
|
28
|
+
Apache ? # Apache env-var
|
29
|
+
[200,h.update({'X-Sendfile' => b.d}),[]] : # Apache file-handler
|
30
|
+
(r = Rack::File.new nil # create Rack file-handler
|
31
|
+
r.instance_variable_set '@path',b.d # set path
|
32
|
+
r.serving(@r).do{|s,m,b|[s,m.update(h),b]}) # Rack file-handler
|
33
|
+
) :
|
34
|
+
[200, h, b]} : # response triple
|
35
|
+
[304,{},[]] # not modified
|
36
|
+
end
|
37
|
+
|
38
|
+
def send?
|
39
|
+
!((m=@r['HTTP_IF_NONE_MATCH']) && m.strip.split(/\s*,\s*/).include?(@r['ETag']))
|
40
|
+
end
|
41
|
+
|
42
|
+
def GET_file
|
43
|
+
@r['ETag'] = [m,size].h
|
44
|
+
maybeSend mime,->{self},:link
|
45
|
+
end
|
46
|
+
|
47
|
+
def GET_img
|
48
|
+
(thumb? ? thumb : self).GET_file
|
49
|
+
end
|
50
|
+
|
51
|
+
def GET_resource # for
|
52
|
+
(F['req/'+@r.q['y']] || # any URI
|
53
|
+
F[@r['REQUEST_PATH'].t+('GET')]||# specific path
|
54
|
+
F[uri.t+('GET')] # specific URI
|
55
|
+
).do{|y|y.(self,@r)} || # custom handler
|
56
|
+
as('index.html').do{|i| # HTML index
|
57
|
+
i.e && # exists?
|
58
|
+
((uri[-1]=='/') ? i.env(@r).GET_file : # are we inside dir?
|
59
|
+
[301, {Location: uri.t}] )} || # rebase to index dir
|
60
|
+
response
|
61
|
+
end
|
62
|
+
|
63
|
+
# graph constructor
|
64
|
+
fn 'graph/',->e,q,m{
|
65
|
+
F['set/' + q['set']][e, q, m]. # doc set
|
66
|
+
map{|u|m[u.uri] ||= u}}
|
67
|
+
|
68
|
+
# document set constructor
|
69
|
+
fn 'set/',->d,e,m{d.docs}
|
70
|
+
|
71
|
+
# construct HTTP response
|
72
|
+
def response
|
73
|
+
|
74
|
+
# request arguments
|
75
|
+
q = @r.q # query-string
|
76
|
+
g = q['graph'] # graph-generation function selector
|
77
|
+
|
78
|
+
# request graph
|
79
|
+
m = {}
|
80
|
+
|
81
|
+
# add resources to request graph
|
82
|
+
F['graph/' + g][self,q,m]
|
83
|
+
|
84
|
+
# empty graph -> 404
|
85
|
+
return F[E404][self,@r] if m.empty?
|
86
|
+
|
87
|
+
# inspect request-graph
|
88
|
+
if q.has_key? 'debug'
|
89
|
+
puts "docs #{m.keys.join ' '}"
|
90
|
+
puts "resources #{m['frag']['res']}" if m['frag']
|
91
|
+
end
|
92
|
+
|
93
|
+
# request-graph identifier
|
94
|
+
s = (q.has_key?('nocache') ? rand.to_s : # random identifier
|
95
|
+
m.sort.map{|u,r|[u, r.respond_to?(:m) && r.m]}).h # canonicalized set signature
|
96
|
+
|
97
|
+
# response identifier
|
98
|
+
@r['ETag'] ||= [s, q, @r.format].h
|
99
|
+
|
100
|
+
# check if client has response
|
101
|
+
maybeSend @r.format, ->{
|
102
|
+
|
103
|
+
# cached response identifier
|
104
|
+
r = E'/E/req/' + @r['ETag'].dive
|
105
|
+
|
106
|
+
if r.e # response already generated
|
107
|
+
r # cached response
|
108
|
+
else
|
109
|
+
|
110
|
+
# cached graph identifier
|
111
|
+
c = E '/E/graph/' + s.dive
|
112
|
+
|
113
|
+
if c.e # cached graph exists
|
114
|
+
m.merge! c.r true # read cache
|
115
|
+
else
|
116
|
+
# construct response graph
|
117
|
+
m.values.map{|r|
|
118
|
+
r.env(@r).graphFromFile m}
|
119
|
+
|
120
|
+
# cache response graph
|
121
|
+
c.w m,true
|
122
|
+
end
|
123
|
+
|
124
|
+
# response graph sorting/filtering
|
125
|
+
E.filter q, m, self
|
126
|
+
|
127
|
+
# response body
|
128
|
+
r.w render @r.format, m, @r
|
129
|
+
end }
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
data/infod/Th/HEAD.rb
ADDED
data/infod/Th/POST.rb
ADDED
data/infod/Th/local.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
class E
|
2
|
+
=begin
|
3
|
+
this code is not loaded by default
|
4
|
+
|
5
|
+
server looks in hostname-root paths for *.rb files, reporting found code:
|
6
|
+
site config http://data.whats-your.name/data.rb
|
7
|
+
|
8
|
+
=end
|
9
|
+
|
10
|
+
# disallow custom-query crawling on all sites
|
11
|
+
fn '/robots.txt/GET',->e,r{
|
12
|
+
[200,{'Content-Type'=>'text/plain'},["User-agent: *\nDisallow: /*?*\n"]]}
|
13
|
+
|
14
|
+
# schema search forward from site root
|
15
|
+
fn 'http://data.whats-your.name/GET',->e,r{
|
16
|
+
[302,{'Location'=>'/schema'},[]]}
|
17
|
+
|
18
|
+
# webize PS(1)
|
19
|
+
fn '/ps/GET',->e,r{
|
20
|
+
[200,{'Content-Type'=>'text/plain'},[`ps aux`]]}
|
21
|
+
|
22
|
+
end
|
data/infod/Th/uid.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
|
3
|
+
module Th
|
4
|
+
FingerprintKeys = %w{
|
5
|
+
HTTP_ACCEPT
|
6
|
+
HTTP_ACCEPT_CHARSET
|
7
|
+
HTTP_ACCEPT_LANGUAGE
|
8
|
+
HTTP_ACCEPT_ENCODING
|
9
|
+
HTTP_USER_AGENT
|
10
|
+
HTTP_ORIGIN_ADDR
|
11
|
+
REMOTE_ADDR
|
12
|
+
}
|
13
|
+
|
14
|
+
def uid
|
15
|
+
('/u/'+FingerprintKeys.map{|i|self[i]}.h.dive).E
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class E
|
20
|
+
|
21
|
+
fn '/whoami/GET',->e,r{
|
22
|
+
[302,{Location: '/@'+r.uid.uri},[]]}
|
23
|
+
|
24
|
+
end
|
data/infod/Th.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
%w{GET HEAD POST PATCH uid 404 500}.map{|i|require_relative 'Th/' + i}
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
class String
|
5
|
+
# parse querystring
|
6
|
+
def qp
|
7
|
+
d={}
|
8
|
+
split(/&/).map{|e|
|
9
|
+
k,v=e.split(/=/,2).map{|x|
|
10
|
+
CGI.unescape x}
|
11
|
+
d[k]=v}
|
12
|
+
d
|
13
|
+
end
|
14
|
+
def hR
|
15
|
+
[200,{'Content-Type'=>'text/html'},[self]]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Tb
|
20
|
+
def q
|
21
|
+
@q ||= self.().qp
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Th
|
26
|
+
def qs
|
27
|
+
(['GET','HEAD'].member? fn) ? self['QUERY_STRING'] : self['rack.input'].read
|
28
|
+
end
|
29
|
+
# read querystring
|
30
|
+
def q
|
31
|
+
@q ||= (qs||'').qp.do{|q|
|
32
|
+
(q['?']).do{|d|
|
33
|
+
E::F['?'][d].do{|g| # expand aliases
|
34
|
+
g.merge q
|
35
|
+
} || q } || q}
|
36
|
+
end
|
37
|
+
# read Accept header
|
38
|
+
def accept_ k=''
|
39
|
+
d={}
|
40
|
+
self['HTTP_ACCEPT'+k].do{|k|
|
41
|
+
k.split(/,/).map{|e|
|
42
|
+
f,q=e.split(/;/)
|
43
|
+
i=q&&q.split(/=/)[1].to_f||1
|
44
|
+
d[i]||=[]
|
45
|
+
d[i].push f}}
|
46
|
+
d
|
47
|
+
end
|
48
|
+
|
49
|
+
def format
|
50
|
+
@format ||= conneg
|
51
|
+
end
|
52
|
+
|
53
|
+
def conneg
|
54
|
+
# choose a preferred content-type
|
55
|
+
return q['format'] if q['format'] && E::F[E::Render+q['format']]
|
56
|
+
accept.sort.reverse.map{|p|p[1].map{|mime|
|
57
|
+
return mime if E::F[E::Render+mime]
|
58
|
+
}}
|
59
|
+
'text/html'
|
60
|
+
end
|
61
|
+
|
62
|
+
def accept; @accept ||= accept_ end
|
63
|
+
|
64
|
+
def fn
|
65
|
+
# request method (Symbol) getter
|
66
|
+
self['REQUEST_METHOD']
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Hash
|
71
|
+
def qs
|
72
|
+
'?'+map{|k,v|k.to_s+'='+(v ? (CGI.escape [*v][0].to_s) : '')}.intersperse("&").join('')
|
73
|
+
end
|
74
|
+
def env r # thread environment through to children
|
75
|
+
@r = r
|
76
|
+
self
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class E
|
81
|
+
|
82
|
+
F['?'] ||= {}
|
83
|
+
|
84
|
+
# access or update request environment
|
85
|
+
def env r=nil
|
86
|
+
r ? (@r = r
|
87
|
+
self) : @r
|
88
|
+
end
|
89
|
+
|
90
|
+
def E.call e
|
91
|
+
dev # check for changed source code
|
92
|
+
e.extend Th # enable request-related utility functions
|
93
|
+
e['HTTP_X_FORWARDED_HOST'].do{|h| e['SERVER_NAME'] = h } # hostname
|
94
|
+
(e['REQUEST_PATH'].force_encoding('UTF-8').do{|u| # path
|
95
|
+
CGI.unescape(u.index(Prefix)==0 ? u[Prefix.size..-1] : # non-local or non-HTTP URI
|
96
|
+
'http://' + e['SERVER_NAME'] + u.gsub('+','%2B')) # HTTP URI
|
97
|
+
}.E.env(e).jail.do{|r| # valid path?
|
98
|
+
e['uri']=r.uri; r.send e.fn # update URI and continue
|
99
|
+
} || [403,{},['invalid path']]). # reject
|
100
|
+
do{|response| puts [ # inspect
|
101
|
+
e.fn, response[0],['http://', e['SERVER_NAME'], e['REQUEST_URI']].join,e['HTTP_USER_AGENT'],e['HTTP_REFERER']].join ' '
|
102
|
+
response }
|
103
|
+
end
|
104
|
+
|
105
|
+
# load site-specific code-base
|
106
|
+
|
107
|
+
E['http:/*/*.rb'].glob.map{|s| puts "site config #{s}"
|
108
|
+
require s.d}
|
109
|
+
|
110
|
+
end
|