infod 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/infod.rb +2 -3
  2. data/infod/Es.rb +31 -67
  3. data/infod/{W/source.rb → Es/code.rb} +6 -10
  4. data/infod/Es/css.rb +21 -0
  5. data/infod/{W → Es}/csv.rb +0 -0
  6. data/infod/Es/du.rb +16 -0
  7. data/infod/{W → Es}/feed.rb +13 -11
  8. data/infod/Es/filter.rb +75 -0
  9. data/infod/Es/find.rb +20 -0
  10. data/infod/Es/fs.rb +145 -136
  11. data/infod/Es/glob.rb +22 -0
  12. data/infod/Es/grep.rb +61 -0
  13. data/infod/Es/groonga.rb +47 -56
  14. data/infod/Es/html.rb +271 -0
  15. data/infod/Es/image.rb +114 -0
  16. data/infod/Es/in.rb +68 -0
  17. data/infod/Es/index.rb +183 -0
  18. data/infod/{W → Es}/json.rb +28 -4
  19. data/infod/Es/kv.rb +60 -0
  20. data/infod/Es/ls.rb +58 -0
  21. data/infod/Es/mail.rb +87 -0
  22. data/infod/Es/man.rb +112 -0
  23. data/infod/Es/mime.rb +59 -0
  24. data/infod/Es/out.rb +52 -0
  25. data/infod/{W/page.rb → Es/pager.rb} +7 -3
  26. data/infod/Es/pdf.rb +19 -0
  27. data/infod/Es/rdf.rb +35 -0
  28. data/infod/Es/schema.rb +99 -0
  29. data/infod/Es/search.rb +24 -0
  30. data/infod/Es/sh.rb +21 -0
  31. data/infod/{W → Es}/text.rb +26 -14
  32. data/infod/H.rb +15 -29
  33. data/infod/H/audio.rb +19 -0
  34. data/infod/H/blog.rb +15 -0
  35. data/infod/{W → H}/cal.rb +2 -31
  36. data/infod/H/edit.rb +88 -0
  37. data/infod/{W/examine/examine.rb → H/facets.rb} +17 -17
  38. data/infod/{W → H}/forum.rb +1 -0
  39. data/infod/{W/examine/sw.rb → H/hf.rb} +12 -12
  40. data/infod/H/histogram.rb +78 -0
  41. data/infod/H/mail.rb +92 -0
  42. data/infod/{W/chat.rb → H/microblog.rb} +21 -16
  43. data/infod/H/threads.rb +77 -0
  44. data/infod/H/time.rb +131 -0
  45. data/infod/H/who.rb +30 -0
  46. data/infod/{W → H}/wiki.rb +0 -0
  47. data/infod/K.rb +28 -60
  48. data/infod/N.rb +151 -74
  49. data/infod/Rb.rb +3 -3
  50. data/infod/Th.rb +27 -101
  51. data/infod/Th/404.rb +29 -36
  52. data/infod/Th/500.rb +36 -5
  53. data/infod/Th/GET.rb +48 -118
  54. data/infod/Th/POST.rb +31 -11
  55. data/infod/Th/perf.rb +37 -0
  56. data/infod/Th/util.rb +89 -0
  57. data/infod/Y.rb +24 -7
  58. data/infod/infod.rb +2 -3
  59. metadata +92 -64
  60. data/infod/Es/redis.rb +0 -3
  61. data/infod/Es/sqlite.rb +0 -3
  62. data/infod/Th/local.rb +0 -22
  63. data/infod/W.rb +0 -34
  64. data/infod/W/audio.rb +0 -56
  65. data/infod/W/blog.rb +0 -3
  66. data/infod/W/color.rb +0 -28
  67. data/infod/W/core.rb +0 -77
  68. data/infod/W/css.rb +0 -24
  69. data/infod/W/du.rb +0 -35
  70. data/infod/W/edit.rb +0 -8
  71. data/infod/W/examine/exhibit.rb +0 -34
  72. data/infod/W/examine/hist.rb +0 -55
  73. data/infod/W/examine/history.rb +0 -19
  74. data/infod/W/examine/normal.rb +0 -31
  75. data/infod/W/examine/protovis.rb +0 -30
  76. data/infod/W/examine/time/graph.rb +0 -86
  77. data/infod/W/examine/time/line.rb +0 -24
  78. data/infod/W/find.rb +0 -24
  79. data/infod/W/grep.rb +0 -27
  80. data/infod/W/html.rb +0 -143
  81. data/infod/W/image.rb +0 -61
  82. data/infod/W/kv.rb +0 -66
  83. data/infod/W/ls.rb +0 -50
  84. data/infod/W/mail.rb +0 -248
  85. data/infod/W/pdf.rb +0 -16
  86. data/infod/W/post.rb +0 -9
  87. data/infod/W/rdf.rb +0 -32
  88. data/infod/W/schema.rb +0 -172
  89. data/infod/W/search.rb +0 -33
  90. data/infod/W/shell.rb +0 -30
  91. data/infod/W/table.rb +0 -87
  92. data/infod/W/tree.rb +0 -26
  93. data/infod/W/vfs.rb +0 -175
data/infod/Rb.rb CHANGED
@@ -4,7 +4,7 @@ class Array
4
4
  def head; self[0] end
5
5
  def tail; self[1..-1] end
6
6
  def snd; self[1] end
7
- def r; self[rand length] end
7
+ def random; self[rand length] end
8
8
  def h; join.h end
9
9
  def intersperse i
10
10
  inject([]){|a,b|a << b << i}[0..-2]
@@ -65,7 +65,7 @@ end
65
65
  class NilClass
66
66
  def do; nil end
67
67
  def to_ary; [] end
68
+ def to_hash; {} end
68
69
  def to_s; "" end
69
- alias_method :to_str,:to_s
70
- def method_missing *a; nil; end
70
+ %w{html to_str}.map{|m|alias_method m,:to_s}
71
71
  end
data/infod/Th.rb CHANGED
@@ -1,110 +1,36 @@
1
- %w{GET HEAD POST PATCH uid 404 500}.map{|i|require_relative 'Th/' + i}
1
+ #watch __FILE__
2
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
3
+ %w{GET HEAD POST PATCH perf uid util 404 500}.map{|i|require_relative 'Th/' + i}
79
4
 
80
5
  class E
81
6
 
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
7
  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 }
8
+ dev
9
+ e.extend Th
10
+ e['HTTP_X_FORWARDED_HOST'].do{|h| e['SERVER_NAME'] = h }
11
+ p = e['REQUEST_PATH'].force_encoding 'UTF-8'
12
+
13
+ uri = CGI.unescape(if (p.index Prefix) == 0
14
+ p[Prefix.size..-1]
15
+ else
16
+ 'http://' + e['SERVER_NAME'] + (p.gsub '+','%2B')
17
+ end).E.env e
18
+
19
+ if (uri.node.expand_path.to_s.index FSbase) == 0
20
+ e['uri'] = uri.uri
21
+ # response
22
+ r = nil # request method
23
+ b = Benchmark.measure{ r = uri.send e.fn }
24
+ F['log'][r[0],e,b.real]
25
+ r
26
+ else
27
+ [403,{},['Forbidden']]
103
28
  end
104
29
 
105
- # load site-specific code-base
106
-
107
- E['http:/*/*.rb'].glob.map{|s| puts "site config #{s}"
108
- require s.d}
30
+ rescue Exception => x
31
+ $stderr.puts 500, e['REQUEST_URI'] ,x.message,x.backtrace
32
+ F['log'][500,e]
33
+ F['E500'][x,@r]
34
+ end
109
35
 
110
36
  end
data/infod/Th/404.rb CHANGED
@@ -1,55 +1,48 @@
1
+ watch __FILE__
1
2
  class E
2
3
 
3
- # 404 response URI
4
4
  E404 = 'req/'+HTTP+'404'
5
5
 
6
- # 404 response function
7
6
  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)]]}
7
+ id = e.uri # response URI
8
+ g = {id=>{}} # response graph
9
+ s = g[id] # resource pointer
10
+ fn = r['REQUEST_METHOD']
23
11
 
24
- # qs y=404 to force a 404 response
25
- F['req/404'] = F[E404]
12
+ # request environment -> graph
13
+ r.map{|k,v| s[Header + k] = k == 'uri' ? v : [v] }
14
+ %w{CHARSET LANGUAGE ENCODING}.map{|a|
15
+ s[Header+'ACCEPT_'+a] = [r.accept_('_' + a)]}
16
+ s[Header+'ACCEPT'] = [r.accept]
17
+ s[Type] = [E[HTTP+'Response']]
18
+ s[HTTP+'statusCodeValue'] = [404]
19
+ s[Header+'HTTP_HOST'] = [E['http://' + s[Header+'HTTP_HOST'][0]]]
20
+ s[Edit] = [E[r['REQUEST_PATH']+'?view=edit&graph=editable']]
21
+ s['#query'] = [r.q]
22
+ s['#seeAlso'] = [e.parent,*e.a('*').glob]
23
+ r.q['view'] = '404'
26
24
 
27
- fn 'view/'+HTTP+'404',->d,e{
28
- [H.css('/css/404'),{_: :h1, c: '404'},d.html]}
25
+ [404,{'Content-Type'=> r.format},[e.render(r.format,g,r)]]}
26
+
27
+ fn 'view/404',->d,e{
28
+ [H.css('/css/404'),{_: :style, c: "a {background-color:#{E.cs}}"},
29
+ d.html]}
29
30
 
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;}"]]}
31
+ # a small non-empty graph
32
+ fn 'protograph/_',->d,_,m{
33
+ m[d.uri] = {}
34
+ rand.to_s.h}
39
35
 
40
- # show response-codes for a list of URIs
36
+ # check response-codes for a list of URIs (linebreak-separated *.u files)
41
37
  def checkURIs
42
38
  r = uris.select{|u|u.to_s.match /^http/}.map{|u|
43
39
  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
40
  puts c.join ' '
46
- c # status, uri tuple
47
- }
41
+ c } # status, uri tuple
48
42
  puts "\n\n"
49
43
  r.map{|c|
50
44
  # show anomalies
51
- puts c.join(' ') unless c[0] == 200
52
- }
45
+ puts c.join(' ') unless c[0] == 200 }
53
46
  end
54
47
 
55
48
  end
data/infod/Th/500.rb CHANGED
@@ -1,10 +1,41 @@
1
- #watch __FILE__
2
1
  class E
3
2
 
4
- fn 'backtrace',->x,r{
3
+ fn '/css/500.css/GET',->e,r{
4
+ [200,{'Content-Type'=>'text/css'},["
5
+ body {margin:0; font-family: sans-serif; background-color:#fff; color:#000}
6
+ h1 {padding:.2em; background-color:#f00; color:#fff; margin:0}
7
+ div {display:inline}
8
+ table {border-spacing:0;margin:0}
9
+ b {background-color:#eee;color:#500;padding:.1em .3em .1em .3em}
10
+ .frag {font-weight:bold; color:#000; background-color:#{E.cs}}
11
+ td.space {background-color:#ddd}
12
+ td.message {background-color:#009;color:#fff}
13
+ td.path {text-align:right}
14
+ td.index {text-align:right;border-color:#000;border-width:0 0 .1em 0;border-style:dotted;background-color:#ddd;color:#000}
15
+ td.context {border-color:#ddd;border-width:0 0 .1em 0;border-style:dotted;padding:.15em}"]]}
16
+
17
+ F['/E/error/GET'] = ->e,r{1/0}
18
+
19
+ fn 'E500',->x,r{
5
20
  [500,{'Content-Type'=>'text/html'},
6
- ['<html><head><title>500</title></head><body><h1>500</h1><pre>',
7
- [x.class.to_s,x.message,*x.backtrace].join("\n").hrefs,
8
- '</pre></body></html>']]}
21
+ [H[{_: :html,
22
+ c: [{_: :head,
23
+ c: [{_: :title, c: 500},(H.css '/css/500')]},
24
+ {_: :body,
25
+ c: [{_: :h1, c: 500},
26
+ {_: :table,
27
+ c: [{_: :tr,
28
+ c: [{_: :td, c: {_: :b, c: x.class}},
29
+ {_: :td, class: :space},
30
+ {_: :td, class: :message, c: x.message.hrefs}
31
+ ]},
32
+ x.backtrace.map{|p|
33
+ p = p.split /:/, 3
34
+ {_: :tr,
35
+ c: [{_: :td, class: :path, c: F['abbrURI'][p[0]]},
36
+ {_: :td, class: :index, c: p[1]},
37
+ {_: :td, class: :context, c: (p[2]||'').hrefs}].cr}}.cr]}]}]}]]]}
9
38
 
39
+ F['/500/GET'] = ->e,r{H([Errors.sort_by{|u,r|r[:time]}.reverse.html,H.css('/css/500')]).hR}
40
+
10
41
  end
data/infod/Th/GET.rb CHANGED
@@ -1,132 +1,62 @@
1
1
  class E
2
2
 
3
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
4
+ # bespoke handler ||
5
+ # raw file ||
6
+ # resource
7
+ if reqFn = F['req/'+@r.q['y']]
8
+ reqFn[self,@r]
9
+ elsif file = [self,pathSegment].compact.find(&:f)
10
+ a = @r.accept.values.flatten
11
+ accepted = a.empty? || (a.member? file.mimeP) || (a.member? '*/*')
12
+ (@r.q.has_any_key(%w{format view}) ||
13
+ MIMEcook[file.mimeP] || !accepted) ? resource : (file.env @r).getFile
14
+ else
15
+ resource
16
+ end
36
17
  end
37
18
 
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
19
+ def getFile
43
20
  @r['ETag'] = [m,size].h
44
- maybeSend mime,->{self},:link
21
+ maybeSend mimeP,->{self},:link
45
22
  end
46
23
 
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
24
+ def resource
25
+ # bubble up site then global tree until handled (false return-value to pass)
26
+ pathSegment.do{|path|
27
+ lambdas = path.cascade.map{|p| p.uri.t + 'GET' }
28
+ ['http://'+@r['SERVER_NAME'],""].map{|h| lambdas.map{|p|
29
+ F[h + p].do{|fn| fn[self,@r].do{|r| return r}}}}}
30
+
31
+ # default handler
60
32
  response
61
33
  end
62
34
 
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 }
35
+ def send?
36
+ !((m=@r['HTTP_IF_NONE_MATCH']) && m.strip.split(/\s*,\s*/).include?(@r['ETag']))
130
37
  end
131
38
 
39
+ def maybeSend m,b,lH=false
40
+ # agent need this version?
41
+ send? ?
42
+ # continue
43
+ b[].do{|b|
44
+ # response metadata
45
+ h = {'Content-Type'=> m,
46
+ 'ETag'=> @r['ETag']}
47
+ h.update({'Cache-Control' => 'no-transform'}) if m.match /^(audio|image|video)/
48
+ h.update({'Link' => '<' + @r['uri'] + '?view=base>; rel=meta'}) if lH
49
+
50
+ b.class == E ? (Nginx ? # nginx enabled
51
+ [200,h.update({'X-Accel-Redirect' => '/fs' + b.path}),[]] : # Nginx file-handler
52
+ Apache ? # Apache enabled
53
+ [200,h.update({'X-Sendfile' => b.d}),[]] : # Apache file-handler
54
+ (r = Rack::File.new nil # Rack file-handler
55
+ r.instance_variable_set '@path',b.d
56
+ r.serving(@r).do{|s,m,b|[s,m.update(h),b]})
57
+ ) :
58
+ [200, h, b]} : # normal (unaccelerated) response
59
+ [304,{},[]] # client has response version
60
+ end
61
+
132
62
  end