infod 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|