knjappserver 0.0.6
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/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/check_running.rb +71 -0
- data/bin/knjappserver_start.rb +50 -0
- data/knjappserver.gemspec +107 -0
- data/lib/conf/README +1 -0
- data/lib/conf/conf_example.rb +109 -0
- data/lib/conf/conf_vars_example.rb +3 -0
- data/lib/files/database_schema.rb +111 -0
- data/lib/files/run/README +1 -0
- data/lib/include/class_customio.rb +21 -0
- data/lib/include/class_erbhandler.rb +36 -0
- data/lib/include/class_httpresp.rb +91 -0
- data/lib/include/class_httpserver.rb +91 -0
- data/lib/include/class_httpsession.rb +350 -0
- data/lib/include/class_httpsession_knjengine.rb +189 -0
- data/lib/include/class_httpsession_mongrel.rb +75 -0
- data/lib/include/class_httpsession_webrick.rb +75 -0
- data/lib/include/class_knjappserver.rb +455 -0
- data/lib/include/class_knjappserver_cleaner.rb +109 -0
- data/lib/include/class_knjappserver_errors.rb +117 -0
- data/lib/include/class_knjappserver_logging.rb +272 -0
- data/lib/include/class_knjappserver_mailing.rb +97 -0
- data/lib/include/class_knjappserver_threadding.rb +87 -0
- data/lib/include/class_knjappserver_web.rb +23 -0
- data/lib/include/class_log.rb +81 -0
- data/lib/include/class_log_access.rb +103 -0
- data/lib/include/class_log_data.rb +42 -0
- data/lib/include/class_log_data_link.rb +16 -0
- data/lib/include/class_log_data_value.rb +34 -0
- data/lib/include/class_log_link.rb +51 -0
- data/lib/include/class_session.rb +43 -0
- data/lib/include/gettext_funcs.rb +10 -0
- data/lib/include/magic_methods.rb +59 -0
- data/lib/knjappserver.rb +1 -0
- data/lib/pages/logs_latest.rhtml +57 -0
- data/lib/pages/logs_show.rhtml +32 -0
- data/lib/pages/spec.rhtml +10 -0
- data/spec/knjappserver_spec.rb +110 -0
- data/spec/spec_helper.rb +12 -0
- metadata +188 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
class Knjappserver::ERBHandler
|
2
|
+
def initialize
|
3
|
+
@connected = {}
|
4
|
+
end
|
5
|
+
|
6
|
+
def erb_handler(data)
|
7
|
+
#Hack the Knj::Thread to accept data - this is how get, post and etc. are set.
|
8
|
+
Thread.current[:knjappserver] = data
|
9
|
+
eruby = data[:httpsession].eruby
|
10
|
+
|
11
|
+
if !@connected[eruby.__id__]
|
12
|
+
eruby.connect("error") do |e|
|
13
|
+
_kas.handle_error(e)
|
14
|
+
end
|
15
|
+
|
16
|
+
@connected[eruby.__id__] = true
|
17
|
+
end
|
18
|
+
|
19
|
+
cont = eruby.load_return(data[:filepath], {
|
20
|
+
:with_headers => false,
|
21
|
+
:custom_io => true
|
22
|
+
})
|
23
|
+
headers = eruby.headers
|
24
|
+
eruby.reset_headers
|
25
|
+
|
26
|
+
headers_ret = {}
|
27
|
+
headers.each do |header|
|
28
|
+
headers_ret[header[0]] = [header[1]]
|
29
|
+
end
|
30
|
+
|
31
|
+
Thread.current[:knjappserver].clear
|
32
|
+
Thread.current[:knjappserver] = nil
|
33
|
+
|
34
|
+
return {:headers => headers}
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "time"
|
2
|
+
|
3
|
+
class Knjappserver::Httpresp
|
4
|
+
attr_accessor :body, :nl, :status
|
5
|
+
|
6
|
+
STATUS_CODES = {
|
7
|
+
100 => "Continue",
|
8
|
+
200 => "OK",
|
9
|
+
201 => "Created",
|
10
|
+
202 => "Accepted",
|
11
|
+
204 => "No Content",
|
12
|
+
205 => "Reset Content",
|
13
|
+
206 => "Partial Content",
|
14
|
+
301 => "Moved Permanently",
|
15
|
+
302 => "Found",
|
16
|
+
303 => "See Other",
|
17
|
+
304 => "Not Modified",
|
18
|
+
307 => "Temporary Redirect",
|
19
|
+
400 => "Bad Request",
|
20
|
+
401 => "Unauthorized",
|
21
|
+
403 => "Forbidden",
|
22
|
+
404 => "Not Found",
|
23
|
+
500 => "Internal Server Error"
|
24
|
+
}
|
25
|
+
NL = "\r\n"
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@status = 200
|
29
|
+
@headers = {
|
30
|
+
"Content-Type" => "text/html",
|
31
|
+
"Date" => Time.now.httpdate,
|
32
|
+
"Connection" => "Keep-Alive",
|
33
|
+
"Transfer-Encoding" => "chunked",
|
34
|
+
"Keep-Alive" => "timeout=30, max=100"
|
35
|
+
}
|
36
|
+
@cookies = []
|
37
|
+
end
|
38
|
+
|
39
|
+
def header(key, val)
|
40
|
+
@headers[key] = val
|
41
|
+
end
|
42
|
+
|
43
|
+
def cookie(cookie)
|
44
|
+
@cookies << cookie
|
45
|
+
end
|
46
|
+
|
47
|
+
def header_str
|
48
|
+
res = "HTTP/1.1 #{@status}"
|
49
|
+
code = STATUS_CODES[@status]
|
50
|
+
res += " #{code}" if code
|
51
|
+
res += NL
|
52
|
+
#res += "Content-Length: #{@body.length}#{NL}"
|
53
|
+
|
54
|
+
@headers.each do |key, val|
|
55
|
+
res += "#{key}: #{val}#{NL}"
|
56
|
+
end
|
57
|
+
|
58
|
+
@cookies.each do |cookie|
|
59
|
+
res += "Set-Cookie: #{cookie}#{NL}"
|
60
|
+
end
|
61
|
+
|
62
|
+
res += NL
|
63
|
+
|
64
|
+
return res
|
65
|
+
end
|
66
|
+
|
67
|
+
def write_chunked(socket)
|
68
|
+
socket.write(self.header_str)
|
69
|
+
|
70
|
+
@body.each do |part|
|
71
|
+
while buf = part.read(1024)
|
72
|
+
next if buf.empty?
|
73
|
+
socket.write("#{format("%x", buf.bytesize)}#{NL}#{buf}#{NL}")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
socket.write("0#{NL}#{NL}")
|
78
|
+
end
|
79
|
+
|
80
|
+
def content
|
81
|
+
str = self.header_str + @body.string + "\n\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
def destroy
|
85
|
+
@status = nil
|
86
|
+
@status_codes = nil
|
87
|
+
@body = nil
|
88
|
+
@cookies = nil
|
89
|
+
@headers = nil
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Knjappserver::Httpserver
|
2
|
+
attr_accessor :working_count
|
3
|
+
attr_reader :kas, :http_sessions, :thread_accept, :thread_restart, :server
|
4
|
+
|
5
|
+
def initialize(kas)
|
6
|
+
@kas = kas
|
7
|
+
@http_sessions = []
|
8
|
+
@working_count = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def start
|
12
|
+
@server = TCPServer.new(@kas.config[:host], @kas.config[:port])
|
13
|
+
|
14
|
+
@thread_accept = Knj::Thread.new do
|
15
|
+
loop do
|
16
|
+
if !@server or @server.closed?
|
17
|
+
sleep 1
|
18
|
+
next
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
self.spawn_httpsession(@server.accept)
|
23
|
+
STDOUT.print "Starting new HTTP-request.\n" if @kas.config[:debug]
|
24
|
+
rescue => e
|
25
|
+
STDOUT.puts e.inspect
|
26
|
+
STDOUT.puts e.backtrace
|
27
|
+
STDOUT.print "\n"
|
28
|
+
STDOUT.print "Could not accept HTTP-request - waiting 0.5 sec and then trying again.\n"
|
29
|
+
sleep 0.5
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@thread_restart = Knj::Thread.new do
|
35
|
+
loop do
|
36
|
+
sleep 10
|
37
|
+
break if @kas.should_restart and @kas.should_restart_done
|
38
|
+
|
39
|
+
if !@kas.should_restart and (!@server or @server.closed?)
|
40
|
+
STDOUT.print "Socket does not exist or is closed - restarting HTTP-server!\n"
|
41
|
+
@server = TCPServer.new(@kas.config[:host], @kas.config[:port])
|
42
|
+
STDOUT.print "Done.\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def stop
|
49
|
+
begin
|
50
|
+
STDOUT.print "Stopping accept-thread.\n" if @kas.config[:debug]
|
51
|
+
@thread_accept.kill if @thread_accept and @thread_accept.alive?
|
52
|
+
@thread_restart.kill if @thread_restart and @thread_restart.alive?
|
53
|
+
rescue => e
|
54
|
+
STDOUT.print "Could not stop accept-thread.\n" if @kas.config[:debug]
|
55
|
+
STDOUT.puts e.inspect
|
56
|
+
STDOUT.puts e.backtrace
|
57
|
+
end
|
58
|
+
|
59
|
+
STDOUT.print "Stopping all HTTP sessions.\n" if @kas.config[:debug]
|
60
|
+
@http_sessions.each do |httpsession|
|
61
|
+
httpsession.destruct
|
62
|
+
end
|
63
|
+
|
64
|
+
begin
|
65
|
+
STDOUT.print "Stopping TCPServer.\n" if @kas.config[:debug]
|
66
|
+
@server.close if @server and !@server.closed?
|
67
|
+
STDOUT.print "TCPServer was closed.\n" if @kas.config[:debug]
|
68
|
+
rescue Timeout::Error
|
69
|
+
raise "Could not close TCPserver.\n"
|
70
|
+
rescue IOError => e
|
71
|
+
if e.message == "closed stream"
|
72
|
+
#ignore - it should be closed.
|
73
|
+
else
|
74
|
+
raise e
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def spawn_httpsession(socket)
|
80
|
+
@http_sessions << Knjappserver::Httpsession.new(self, socket)
|
81
|
+
end
|
82
|
+
|
83
|
+
def handle_request(&block)
|
84
|
+
@working_count += 1
|
85
|
+
begin
|
86
|
+
block.call
|
87
|
+
ensure
|
88
|
+
@working_count -= 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
require "digest"
|
2
|
+
|
3
|
+
class Knjappserver::Httpsession
|
4
|
+
attr_accessor :data
|
5
|
+
attr_reader :session, :session_id, :session_hash, :kas, :active, :out, :eruby, :browser, :debug
|
6
|
+
|
7
|
+
def initialize(httpserver, socket)
|
8
|
+
@data = {}
|
9
|
+
@socket = socket
|
10
|
+
@httpserver = httpserver
|
11
|
+
@kas = httpserver.kas
|
12
|
+
@active = true
|
13
|
+
@eruby = Knj::Eruby.new(:cache_hash => @kas.eruby_cache)
|
14
|
+
@debug = @kas.config[:debug]
|
15
|
+
self.reset
|
16
|
+
|
17
|
+
if @kas.config[:engine_webrick]
|
18
|
+
require "#{File.dirname(__FILE__)}/class_httpsession_webrick"
|
19
|
+
@handler = Knjappserver::Httpsession::Webrick.new(:kas => @kas)
|
20
|
+
elsif @kas.config[:engine_mongrel]
|
21
|
+
require "#{File.dirname(__FILE__)}/class_httpsession_mongrel"
|
22
|
+
@handler = Knjappserver::Httpsession::Mongrel.new(:kas => @kas)
|
23
|
+
elsif @kas.config[:engine_knjengine]
|
24
|
+
require "#{File.dirname(__FILE__)}/class_httpsession_knjengine"
|
25
|
+
@handler = Knjappserver::Httpsession::Knjengine.new(:kas => @kas)
|
26
|
+
else
|
27
|
+
raise "Unknown handler."
|
28
|
+
end
|
29
|
+
|
30
|
+
Dir.chdir(@kas.config[:doc_root])
|
31
|
+
ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc) if @debug
|
32
|
+
STDOUT.print "New httpsession #{self.__id__} (total: #{@httpserver.http_sessions.count}).\n" if @debug
|
33
|
+
|
34
|
+
@thread_request = Knj::Thread.new do
|
35
|
+
@kas.db_handler.get_and_register_thread if @kas.db_handler.opts[:threadsafe]
|
36
|
+
@kas.ob.db.get_and_register_thread if @kas.ob.db.opts[:threadsafe]
|
37
|
+
|
38
|
+
begin
|
39
|
+
while @active
|
40
|
+
begin
|
41
|
+
Timeout.timeout(30) do
|
42
|
+
@handler.socket_parse(@socket)
|
43
|
+
end
|
44
|
+
|
45
|
+
sleep 0.1 while @kas.paused? #Check if we should be waiting with executing the pending request.
|
46
|
+
|
47
|
+
if @kas.config[:max_requests_working]
|
48
|
+
while @httpserver.working_count >= @kas.config[:max_requests_working]
|
49
|
+
STDOUT.print "Maximum amounts of requests are working (#{@httpserver.working_count}, #{@kas.config[:max_requests_working]}) - sleeping.\n" if @debug
|
50
|
+
sleep 0.1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
@httpserver.handle_request do
|
55
|
+
self.serve
|
56
|
+
end
|
57
|
+
ensure
|
58
|
+
@kas.served += 1 if @kas
|
59
|
+
self.reset
|
60
|
+
end
|
61
|
+
end
|
62
|
+
rescue WEBrick::HTTPStatus::RequestTimeout, WEBrick::HTTPStatus::EOFError, Errno::ECONNRESET, Errno::EPIPE, Timeout::Error => e
|
63
|
+
#Ignore - the user probaly left.
|
64
|
+
#STDOUT.puts e.inspect
|
65
|
+
#STDOUT.puts e.backtrace
|
66
|
+
rescue SystemExit, Interrupt => e
|
67
|
+
raise e
|
68
|
+
rescue RuntimeError, Exception => e
|
69
|
+
first = e.backtrace.first
|
70
|
+
|
71
|
+
if first.index("webrick/httprequest.rb") != nil or first.index("webrick/httpresponse.rb") != nil
|
72
|
+
if debug
|
73
|
+
STDOUT.print "Notice: Webrick error - properly faulty request - ignoring!\n"
|
74
|
+
STDOUT.puts e.inspect
|
75
|
+
STDOUT.puts e.backtrace
|
76
|
+
end
|
77
|
+
else
|
78
|
+
STDOUT.puts e.inspect
|
79
|
+
STDOUT.puts e.backtrace
|
80
|
+
end
|
81
|
+
ensure
|
82
|
+
@kas.db_handler.free_thread if @kas and @kas.db_handler.opts[:threadsafe]
|
83
|
+
@kas.ob.db.free_thread if @kas and @kas.ob.db.opts[:threadsafe]
|
84
|
+
self.destruct
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def threadded_content(block)
|
90
|
+
raise "No block was given." if !block
|
91
|
+
@out = StringIO.new
|
92
|
+
|
93
|
+
thread_out = StringIO.new
|
94
|
+
thread = Thread.new(Thread.current[:knjappserver].clone) do |data|
|
95
|
+
Thread.current[:knjappserver] = data
|
96
|
+
Thread.current[:knjappserver][:stringio] = thread_out
|
97
|
+
Thread.current[:knjappserver][:db] = @kas.db_handler
|
98
|
+
|
99
|
+
@kas.db_handler.get_and_register_thread if @kas.db_handler.opts[:threadsafe]
|
100
|
+
@kas.ob.db.get_and_register_thread if @kas.ob.db.opts[:threadsafe]
|
101
|
+
|
102
|
+
begin
|
103
|
+
block.call
|
104
|
+
ensure
|
105
|
+
@kas.ob.db.free_thread if @kas.ob.db.opts[:threadsafe]
|
106
|
+
@kas.db_handler.free_thread if @kas.db_handler.opts[:threadsafe]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
@parts << {
|
111
|
+
:thread => thread,
|
112
|
+
:stringio => thread_out
|
113
|
+
}
|
114
|
+
@parts << @out
|
115
|
+
end
|
116
|
+
|
117
|
+
def reset
|
118
|
+
@out.close if @out
|
119
|
+
@out = StringIO.new
|
120
|
+
@parts = [@out]
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.finalize(id)
|
124
|
+
STDOUT.print "Httpsession finalize #{id}.\n" if @debug
|
125
|
+
end
|
126
|
+
|
127
|
+
def destruct
|
128
|
+
STDOUT.print "Httpsession destruct (#{@httpserver.http_sessions.length})\n" if @debug and @httpserver
|
129
|
+
|
130
|
+
begin
|
131
|
+
@socket.close if @socket and !@socket.closed?
|
132
|
+
rescue => e
|
133
|
+
STDOUT.puts e.inspect
|
134
|
+
STDOUT.puts e.backtrace
|
135
|
+
#ignore if it fails...
|
136
|
+
end
|
137
|
+
|
138
|
+
@httpserver.http_sessions.delete(self) if @httpserver
|
139
|
+
@httpserver = nil
|
140
|
+
|
141
|
+
@data = nil
|
142
|
+
@kas = nil
|
143
|
+
@active = nil
|
144
|
+
@session = nil
|
145
|
+
@session_id = nil
|
146
|
+
@session_hash = nil
|
147
|
+
@out = nil
|
148
|
+
@socket = nil
|
149
|
+
@browser = nil
|
150
|
+
|
151
|
+
@eruby.destroy if @eruby
|
152
|
+
@eruby = nil
|
153
|
+
|
154
|
+
@handler.destroy if @handler
|
155
|
+
@handler = nil
|
156
|
+
|
157
|
+
thread = @thread_request
|
158
|
+
@thread_request = nil
|
159
|
+
thread.kill if thread and thread.alive?
|
160
|
+
end
|
161
|
+
|
162
|
+
def serve
|
163
|
+
resp = Knjappserver::Httpresp.new
|
164
|
+
|
165
|
+
meta = @handler.meta
|
166
|
+
cookie = @handler.cookie
|
167
|
+
page_path = @handler.page_path
|
168
|
+
|
169
|
+
pinfo = Knj::Php.pathinfo(page_path)
|
170
|
+
ext = pinfo["extension"].downcase
|
171
|
+
|
172
|
+
ctype = @kas.types[ext.to_sym] if @kas.types[ext.to_sym]
|
173
|
+
ctype = @kas.config[:default_filetype] if !ctype and @kas.config.has_key?(:default_filetype)
|
174
|
+
resp.header("Content-Type", ctype)
|
175
|
+
|
176
|
+
@browser = Knj::Web.browser(meta)
|
177
|
+
@ip = nil
|
178
|
+
@ip = meta["HTTP_X_FORWARDED_FOR"].split(",")[0].strip if !@ip and meta["HTTP_X_FORWARDED_FOR"]
|
179
|
+
@ip = meta["REMOTE_ADDR"] if !@ip and meta["REMOTE_ADDR"]
|
180
|
+
|
181
|
+
@ips = [meta["REMOTE_ADDR"]]
|
182
|
+
@ips << meta["HTTP_X_FORWARDED_FOR"].split(",")[0].strip if meta["HTTP_X_FORWARDED_FOR"]
|
183
|
+
|
184
|
+
@session_id = nil
|
185
|
+
@session_id = "bot" if @browser["browser"] == "bot"
|
186
|
+
@session_id = cookie["KnjappserverSession"] if cookie["KnjappserverSession"].to_s.length > 0
|
187
|
+
|
188
|
+
if !@session_id
|
189
|
+
@session_id = Digest::MD5.hexdigest("#{Time.new.to_f}_#{meta["HTTP_HOST"]}_#{meta["REMOTE_HOST"]}_#{meta["HTTP_X_FORWARDED_SERVER"]}_#{meta["HTTP_X_FORWARDED_FOR"]}_#{meta["HTTP_X_FORWARDED_HOST"]}_#{meta["REMOTE_ADDR"]}_#{meta["HTTP_USER_AGENT"]}")
|
190
|
+
|
191
|
+
resp.cookie(CGI::Cookie.new(
|
192
|
+
"name" => "KnjappserverSession",
|
193
|
+
"value" => @session_id,
|
194
|
+
"path" => "/",
|
195
|
+
"expires" => (Knj::Datet.new.months + 12).time
|
196
|
+
).to_s)
|
197
|
+
end
|
198
|
+
|
199
|
+
session = @kas.session_fromid(:idhash => @session_id, :ip => @ip)
|
200
|
+
|
201
|
+
@session = session[:dbobj]
|
202
|
+
@session_hash = session[:hash]
|
203
|
+
|
204
|
+
if @kas.config[:logging] and @kas.config[:logging][:access_db]
|
205
|
+
@kas.logs_access_pending << {
|
206
|
+
:session_id => @session.id,
|
207
|
+
:date_request => Knj::Datet.new.dbstr,
|
208
|
+
:ips => @ips,
|
209
|
+
:get => @handler.get,
|
210
|
+
:post => @handler.post,
|
211
|
+
:meta => meta,
|
212
|
+
:cookie => cookie
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
time_start = Time.now if @debug
|
217
|
+
serv_data = self.serve_real(
|
218
|
+
:filepath => page_path,
|
219
|
+
:get => @handler.get,
|
220
|
+
:post => @handler.post,
|
221
|
+
:cookie => cookie,
|
222
|
+
:meta => meta,
|
223
|
+
:headers => {},
|
224
|
+
:ctype => ctype,
|
225
|
+
:ext => ext,
|
226
|
+
:session => @session,
|
227
|
+
:session_id => @session_id,
|
228
|
+
:session_hash => @session_hash,
|
229
|
+
:httpsession => self,
|
230
|
+
:db => @kas.db_handler,
|
231
|
+
:kas => @kas
|
232
|
+
)
|
233
|
+
|
234
|
+
serv_data[:headers].each do |header|
|
235
|
+
key = header[0]
|
236
|
+
val = header[1]
|
237
|
+
keystr = key.to_s.strip.downcase
|
238
|
+
|
239
|
+
if keystr.match(/^set-cookie/)
|
240
|
+
WEBrick::Cookie.parse_set_cookies(val).each do |cookie|
|
241
|
+
resp.cookie(cookie.to_s)
|
242
|
+
end
|
243
|
+
else
|
244
|
+
resp.header(key, val)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
body_parts = []
|
249
|
+
@parts.each do |part|
|
250
|
+
if part.is_a?(Hash) and part[:thread]
|
251
|
+
part[:thread].join
|
252
|
+
part[:stringio].rewind
|
253
|
+
body_parts << part[:stringio]
|
254
|
+
elsif part.is_a?(StringIO) or part.is_a?(File)
|
255
|
+
part.rewind
|
256
|
+
body_parts << part
|
257
|
+
else
|
258
|
+
raise "Unknown object: '#{part.class.name}'."
|
259
|
+
end
|
260
|
+
end
|
261
|
+
resp.body = body_parts
|
262
|
+
|
263
|
+
if serv_data[:lastmod]
|
264
|
+
resp.header("Last-Modified", serv_data[:lastmod].time)
|
265
|
+
resp.header("Expires", Time.now + (3600 * 24))
|
266
|
+
end
|
267
|
+
|
268
|
+
if serv_data[:cache]
|
269
|
+
resp.status = 304
|
270
|
+
resp.header("Last-Modified", serv_data[:lastmod].time)
|
271
|
+
resp.header("Expires", Time.now + (3600 * 24))
|
272
|
+
end
|
273
|
+
|
274
|
+
resp.status = serv_data[:statuscode] if serv_data[:statuscode]
|
275
|
+
STDOUT.print "Served '#{meta["REQUEST_URI"]}' in #{Time.now.to_f - time_start.to_f} secs.\n" if @debug
|
276
|
+
|
277
|
+
resp.write_chunked(@socket) if meta["METHOD"] != "HEAD"
|
278
|
+
resp.destroy
|
279
|
+
|
280
|
+
#Letting them be nil is simply not enough (read that on a forum) - knj.
|
281
|
+
serv_data.clear
|
282
|
+
end
|
283
|
+
|
284
|
+
def serve_real(details)
|
285
|
+
request = details[:request]
|
286
|
+
headers = {}
|
287
|
+
cont = ""
|
288
|
+
statuscode = nil
|
289
|
+
lastmod = false
|
290
|
+
max_age = 365 * 24
|
291
|
+
|
292
|
+
cache = false
|
293
|
+
cache_control = {}
|
294
|
+
cache_use = true
|
295
|
+
|
296
|
+
if @handler.headers["cache-control"] and @handler.headers["cache-control"][0]
|
297
|
+
@handler.headers["cache-control"][0].scan(/(.+)=(.+)/) do |match|
|
298
|
+
cache_control[match[1]] = match[2]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
cache_use = false if cache_control["max-age"].to_i <= 0
|
303
|
+
|
304
|
+
#check if we should use a handler for this request.
|
305
|
+
handler_use = false
|
306
|
+
@kas.config[:handlers].each do |handler_info|
|
307
|
+
if handler_info[:file_ext] and handler_info[:file_ext] == details[:ext]
|
308
|
+
handler_use = true
|
309
|
+
ret = handler_info[:callback].call(details)
|
310
|
+
cont = ret[:content] if ret[:content]
|
311
|
+
headers = ret[:headers] if ret[:headers]
|
312
|
+
break
|
313
|
+
elsif handler_info[:path] and handler_info[:mount] and details[:meta]["SCRIPT_NAME"].slice(0, handler_info[:path].length) == handler_info[:path]
|
314
|
+
details[:filepath] = "#{handler_info[:mount]}#{details[:meta]["SCRIPT_NAME"].slice(handler_info[:path].length, details[:meta]["SCRIPT_NAME"].length)}"
|
315
|
+
break
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
if !handler_use
|
320
|
+
if !File.exists?(details[:filepath])
|
321
|
+
statuscode = 404
|
322
|
+
headers["Content-Type"] = "text/html"
|
323
|
+
@parts << StringIO.new("File you are looking for was not found: '#{details[:meta]["REQUEST_URI"]}'.")
|
324
|
+
else
|
325
|
+
lastmod = Knj::Datet.new(File.new(details[:filepath]).mtime)
|
326
|
+
|
327
|
+
if cache_use and @handler.headers["if-modified-since"] and @handler.headers["if-modified-since"][0]
|
328
|
+
request_mod = Knj::Datet.parse(@handler.headers["if-modified-since"][0])
|
329
|
+
if request_mod == lastmod
|
330
|
+
cache = true
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
if !cache
|
335
|
+
@parts << File.new(details[:filepath]) #get plain content from file.
|
336
|
+
end
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
details.clear
|
341
|
+
|
342
|
+
return {
|
343
|
+
:statuscode => statuscode,
|
344
|
+
:content => cont,
|
345
|
+
:headers => headers,
|
346
|
+
:lastmod => lastmod,
|
347
|
+
:cache => cache
|
348
|
+
}
|
349
|
+
end
|
350
|
+
end
|