hayabusa 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +59 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/bin/check_running.rb +69 -0
- data/bin/hayabusa_benchmark.rb +82 -0
- data/bin/hayabusa_cgi.rb +84 -0
- data/bin/hayabusa_fcgi.fcgi +159 -0
- data/bin/hayabusa_fcgi.rb +159 -0
- data/bin/knjappserver_start.rb +42 -0
- data/conf/apache2_cgi_rhtml_conf.conf +10 -0
- data/conf/apache2_fcgi_rhtml_conf.conf +22 -0
- data/hayabusa.gemspec +151 -0
- data/lib/hayabusa.rb +518 -0
- data/lib/hayabusa_cgi_session.rb +128 -0
- data/lib/hayabusa_cgi_tools.rb +102 -0
- data/lib/hayabusa_custom_io.rb +22 -0
- data/lib/hayabusa_database.rb +125 -0
- data/lib/hayabusa_erb_handler.rb +27 -0
- data/lib/hayabusa_ext/cleaner.rb +140 -0
- data/lib/hayabusa_ext/cmdline.rb +52 -0
- data/lib/hayabusa_ext/errors.rb +135 -0
- data/lib/hayabusa_ext/logging.rb +404 -0
- data/lib/hayabusa_ext/mailing.rb +158 -0
- data/lib/hayabusa_ext/sessions.rb +71 -0
- data/lib/hayabusa_ext/threadding.rb +96 -0
- data/lib/hayabusa_ext/threadding_timeout.rb +101 -0
- data/lib/hayabusa_ext/translations.rb +43 -0
- data/lib/hayabusa_ext/web.rb +190 -0
- data/lib/hayabusa_http_server.rb +102 -0
- data/lib/hayabusa_http_session.rb +361 -0
- data/lib/hayabusa_http_session_contentgroup.rb +176 -0
- data/lib/hayabusa_http_session_page_environment.rb +66 -0
- data/lib/hayabusa_http_session_post_multipart.rb +135 -0
- data/lib/hayabusa_http_session_request.rb +219 -0
- data/lib/hayabusa_http_session_response.rb +144 -0
- data/lib/hayabusa_models.rb +8 -0
- data/lib/kernel_ext/gettext_methods.rb +22 -0
- data/lib/kernel_ext/magic_methods.rb +61 -0
- data/lib/models/log.rb +130 -0
- data/lib/models/log_access.rb +88 -0
- data/lib/models/log_data.rb +27 -0
- data/lib/models/log_data_link.rb +3 -0
- data/lib/models/log_data_value.rb +21 -0
- data/lib/models/log_link.rb +65 -0
- data/lib/models/session.rb +35 -0
- data/pages/benchmark.rhtml +0 -0
- data/pages/benchmark_print.rhtml +14 -0
- data/pages/benchmark_simple.rhtml +3 -0
- data/pages/benchmark_threadded_content.rhtml +21 -0
- data/pages/debug_database_connections.rhtml +46 -0
- data/pages/debug_http_sessions.rhtml +40 -0
- data/pages/debug_memory_usage.rhtml +16 -0
- data/pages/error_notfound.rhtml +12 -0
- data/pages/logs_latest.rhtml +57 -0
- data/pages/logs_show.rhtml +32 -0
- data/pages/spec.rhtml +41 -0
- data/pages/spec_post.rhtml +3 -0
- data/pages/spec_test_multiple_clients.rhtml +3 -0
- data/pages/spec_thread_joins.rhtml +21 -0
- data/pages/spec_threadded_content.rhtml +40 -0
- data/pages/tests.rhtml +14 -0
- data/spec/cgi_spec.rb +47 -0
- data/spec/custom_urls_spec.rb +35 -0
- data/spec/fcgi_multiple_processes_spec.rb +32 -0
- data/spec/fcgi_spec.rb +69 -0
- data/spec/hayabusa_spec.rb +194 -0
- data/spec/spec_helper.rb +12 -0
- data/tests/cgi_test/config_cgi.rb +6 -0
- data/tests/cgi_test/threadded_content_test.rhtml +23 -0
- data/tests/cgi_test/vars_get_test.rhtml +4 -0
- data/tests/cgi_test/vars_header_test.rhtml +3 -0
- data/tests/cgi_test/vars_post_test.rhtml +4 -0
- data/tests/fcgi_test/config_fcgi.rb +6 -0
- data/tests/fcgi_test/index.rhtml +3 -0
- data/tests/fcgi_test/sleeper.rhtml +4 -0
- data/tests/fcgi_test/threadded_content_test.rhtml +23 -0
- data/tests/fcgi_test/vars_get_test.rhtml +4 -0
- data/tests/fcgi_test/vars_header_test.rhtml +3 -0
- data/tests/fcgi_test/vars_post_test.rhtml +4 -0
- metadata +257 -0
@@ -0,0 +1,176 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
#This class handels the adding of content and writing to socket. Since this can be done with multiple threads and multiple IO's it can get complicated.
|
4
|
+
class Hayabusa::Http_session::Contentgroup
|
5
|
+
attr_reader :done, :cur_data
|
6
|
+
attr_accessor :chunked, :socket
|
7
|
+
NL = "\r\n"
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
@socket = args[:socket]
|
11
|
+
@chunked = args[:chunked]
|
12
|
+
@resp = args[:resp]
|
13
|
+
@httpsession = args[:httpsession]
|
14
|
+
@mutex = Mutex.new
|
15
|
+
@debug = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def init
|
19
|
+
@done = false
|
20
|
+
@thread = nil
|
21
|
+
@cur_data = {
|
22
|
+
:str => "",
|
23
|
+
:done => false
|
24
|
+
}
|
25
|
+
@ios = [@cur_data]
|
26
|
+
end
|
27
|
+
|
28
|
+
def reset
|
29
|
+
@ios = []
|
30
|
+
@done = false
|
31
|
+
@thread = nil
|
32
|
+
@forced = false
|
33
|
+
|
34
|
+
@mutex.synchronize do
|
35
|
+
self.new_io
|
36
|
+
end
|
37
|
+
|
38
|
+
self.register_thread
|
39
|
+
end
|
40
|
+
|
41
|
+
def new_io(obj = "")
|
42
|
+
@cur_data[:done] = true if @cur_data
|
43
|
+
@cur_data = {:str => obj, :done => false}
|
44
|
+
@ios << @cur_data
|
45
|
+
end
|
46
|
+
|
47
|
+
#Forces the content to be the input - nothing else can be added after calling this.
|
48
|
+
def force_content(newcont)
|
49
|
+
@ios = [{:str => newcont, :done => true}]
|
50
|
+
end
|
51
|
+
|
52
|
+
def register_thread
|
53
|
+
Thread.current[:hayabusa] = {} if !Thread.current[:hayabusa]
|
54
|
+
Thread.current[:hayabusa][:contentgroup] = self
|
55
|
+
end
|
56
|
+
|
57
|
+
def new_thread
|
58
|
+
cgroup = Hayabusa::Http_session::Contentgroup.new(:socket => @socket, :chunked => @chunked)
|
59
|
+
cgroup.init
|
60
|
+
|
61
|
+
@mutex.synchronize do
|
62
|
+
@ios << cgroup
|
63
|
+
self.new_io
|
64
|
+
end
|
65
|
+
|
66
|
+
self.register_thread
|
67
|
+
return cgroup
|
68
|
+
end
|
69
|
+
|
70
|
+
def write_begin
|
71
|
+
begin
|
72
|
+
@resp.write if @httpsession.meta["METHOD"] != "HEAD"
|
73
|
+
rescue Errno::ECONNRESET, Errno::ENOTCONN, Errno::EPIPE
|
74
|
+
#Ignore - the user probaly left.
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def write(cont)
|
79
|
+
@mutex.synchronize do
|
80
|
+
@cur_data[:str] << cont
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def write_output
|
85
|
+
return nil if @thread
|
86
|
+
|
87
|
+
@mutex.synchronize do
|
88
|
+
@thread = Thread.new do
|
89
|
+
begin
|
90
|
+
self.write_begin
|
91
|
+
rescue => e
|
92
|
+
STDERR.puts "Error while writing."
|
93
|
+
STDERR.puts e.inspect
|
94
|
+
STDERR.puts e.backtrace
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def write_force
|
101
|
+
@mutex.synchronize do
|
102
|
+
@forced = true if !@thread
|
103
|
+
end
|
104
|
+
|
105
|
+
if @thread
|
106
|
+
@thread.join
|
107
|
+
else
|
108
|
+
self.write_begin
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def mark_done
|
113
|
+
@cur_data[:done] = true
|
114
|
+
@done = true
|
115
|
+
end
|
116
|
+
|
117
|
+
def join
|
118
|
+
return nil if @forced
|
119
|
+
sleep 0.1 while !@thread
|
120
|
+
@thread.join
|
121
|
+
end
|
122
|
+
|
123
|
+
def write_to_socket
|
124
|
+
count = 0
|
125
|
+
|
126
|
+
@ios.each do |data|
|
127
|
+
if data.is_a?(Hayabusa::Http_session::Contentgroup)
|
128
|
+
data.write_to_socket
|
129
|
+
elsif data.key?(:str)
|
130
|
+
if data[:str].is_a?(Hash) and data[:str][:type] == :file
|
131
|
+
File.open(data[:str][:path], "r") do |file|
|
132
|
+
loop do
|
133
|
+
begin
|
134
|
+
buf = file.sysread(16384)
|
135
|
+
rescue EOFError
|
136
|
+
break
|
137
|
+
end
|
138
|
+
|
139
|
+
if @chunked
|
140
|
+
@socket.write("#{buf.length.to_s(16)}#{NL}#{buf}#{NL}")
|
141
|
+
else
|
142
|
+
@socket.write(buf)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
else
|
147
|
+
loop do
|
148
|
+
break if data[:done] and data[:str].size <= 0
|
149
|
+
sleep 0.1 while data[:str].size < 512 and !data[:done]
|
150
|
+
|
151
|
+
str = nil
|
152
|
+
@mutex.synchronize do
|
153
|
+
str = data[:str].bytes
|
154
|
+
data[:str] = ""
|
155
|
+
end
|
156
|
+
|
157
|
+
#512 could take a long time for big pages. 16384 seems to be an optimal number.
|
158
|
+
str.each_slice(16384) do |slice|
|
159
|
+
buf = slice.pack("C*")
|
160
|
+
|
161
|
+
if @chunked
|
162
|
+
@socket.write("#{buf.length.to_s(16)}#{NL}#{buf}#{NL}")
|
163
|
+
else
|
164
|
+
@socket.write(buf)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
else
|
170
|
+
raise "Unknown object: '#{data.class.name}'."
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
count += 1
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#This class handels all the magic-methods in a different way - by defining them as methods on the binding for the .rhtml-pages.
|
2
|
+
class Hayabusa::Http_session::Page_environment
|
3
|
+
def initialize(args = {})
|
4
|
+
@args = args
|
5
|
+
end
|
6
|
+
|
7
|
+
def get_binding
|
8
|
+
return binding
|
9
|
+
end
|
10
|
+
|
11
|
+
def _buf
|
12
|
+
return $stdout
|
13
|
+
end
|
14
|
+
|
15
|
+
def _cookie
|
16
|
+
return @args[:httpsession].cookie
|
17
|
+
end
|
18
|
+
|
19
|
+
def _db
|
20
|
+
return @args[:hb].db_handler
|
21
|
+
end
|
22
|
+
|
23
|
+
def _get
|
24
|
+
return @args[:httpsession].get
|
25
|
+
end
|
26
|
+
|
27
|
+
def _hb
|
28
|
+
return @args[:hb]
|
29
|
+
end
|
30
|
+
|
31
|
+
alias _requestdata _hb
|
32
|
+
|
33
|
+
def _hb_vars
|
34
|
+
return @args[:hb].vars
|
35
|
+
end
|
36
|
+
|
37
|
+
def _httpsession
|
38
|
+
return @args[:httpsession]
|
39
|
+
end
|
40
|
+
|
41
|
+
def _httpsession_var
|
42
|
+
return @args[:httpsession].httpsession_var
|
43
|
+
end
|
44
|
+
|
45
|
+
def _post
|
46
|
+
return @args[:httpsession].post
|
47
|
+
end
|
48
|
+
|
49
|
+
def _meta
|
50
|
+
return @args[:httpsession].meta
|
51
|
+
end
|
52
|
+
|
53
|
+
alias _server _meta
|
54
|
+
|
55
|
+
def _session
|
56
|
+
return @args[:httpsession].session.sess_data
|
57
|
+
end
|
58
|
+
|
59
|
+
def _session_hash
|
60
|
+
return @args[:httpsession].session_hash
|
61
|
+
end
|
62
|
+
|
63
|
+
def _session_obj
|
64
|
+
return @args[:httpsession].session
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
#This class parses and handels post-multipart requests.
|
2
|
+
class Hayabusa::Http_session::Post_multipart
|
3
|
+
attr_reader :return
|
4
|
+
|
5
|
+
def initialize(args)
|
6
|
+
@args = args
|
7
|
+
boundary_regexp = /\A--#{@args["boundary"]}(--)?#{@args["crlf"]}\z/
|
8
|
+
@return = {}
|
9
|
+
@data = nil
|
10
|
+
@mode = nil
|
11
|
+
@headers = {}
|
12
|
+
@counts = {}
|
13
|
+
|
14
|
+
@args["io"].each do |line|
|
15
|
+
if boundary_regexp =~ line
|
16
|
+
#Finish the data we were writing.
|
17
|
+
self.finish_data if @data
|
18
|
+
|
19
|
+
@data = ""
|
20
|
+
@mode = "headers"
|
21
|
+
elsif @mode == "headers"
|
22
|
+
if match = line.match(/^(.+?):\s+(.+)#{@args["crlf"]}$/)
|
23
|
+
@headers[match[1].to_s.downcase] = match[2]
|
24
|
+
elsif line == @args["crlf"]
|
25
|
+
@mode = "body"
|
26
|
+
else
|
27
|
+
raise "Could not match header from: '#{line}'."
|
28
|
+
end
|
29
|
+
elsif @mode == "body"
|
30
|
+
@data << line
|
31
|
+
else
|
32
|
+
raise "Invalid mode: '#{@mode}'."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
self.finish_data if @data and @data.to_s.length > 0
|
37
|
+
|
38
|
+
@data = nil
|
39
|
+
@headers = nil
|
40
|
+
@mode = nil
|
41
|
+
@args = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
#Add the current treated data to the return-hash.
|
45
|
+
def finish_data
|
46
|
+
@data.chop!
|
47
|
+
name = nil
|
48
|
+
|
49
|
+
disp = @headers["content-disposition"]
|
50
|
+
raise "No 'content-disposition' was given." if !disp
|
51
|
+
|
52
|
+
|
53
|
+
#Figure out value-name in post-hash.
|
54
|
+
match_name = disp.match(/name=\"(.+?)\"/)
|
55
|
+
raise "Could not match name." if !match_name
|
56
|
+
name = match_name[1]
|
57
|
+
|
58
|
+
|
59
|
+
#Fix count with name if given as increamental [].
|
60
|
+
if match = name.match(/^(.+)\[\]$/)
|
61
|
+
if !@counts.key?(match[1])
|
62
|
+
@counts[match[1]] = 0
|
63
|
+
else
|
64
|
+
@counts[match[1]] += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
name = "#{match[1]}[#{@counts[match[1]]}]"
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
#Figure out actual filename.
|
72
|
+
match_fname = disp.match(/filename=\"(.+?)\"/)
|
73
|
+
|
74
|
+
if match_fname
|
75
|
+
obj = Hayabusa::Http_session::Post_multipart::File_upload.new(
|
76
|
+
"fname" => match_fname[1],
|
77
|
+
"headers" => @headers,
|
78
|
+
"data" => @data
|
79
|
+
)
|
80
|
+
@return[name] = obj
|
81
|
+
@data = nil
|
82
|
+
@headers = {}
|
83
|
+
@mode = nil
|
84
|
+
else
|
85
|
+
@return[name] = @data
|
86
|
+
@data = nil
|
87
|
+
@headers = {}
|
88
|
+
@mode = nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
#This is the actual returned object for fileuploads. It is able to do various user-friendly things like save the content to a given path, return the filename, returns the content to a string and more.
|
94
|
+
class Hayabusa::Http_session::Post_multipart::File_upload
|
95
|
+
def initialize(args)
|
96
|
+
@args = args
|
97
|
+
end
|
98
|
+
|
99
|
+
#Returns the size of the upload.
|
100
|
+
def size
|
101
|
+
return @args["data"].length
|
102
|
+
end
|
103
|
+
|
104
|
+
#Returns the size of the fileupload.
|
105
|
+
def length
|
106
|
+
return @args["data"].length
|
107
|
+
end
|
108
|
+
|
109
|
+
#Returns the filename given for the fileupload.
|
110
|
+
def filename
|
111
|
+
return @args["fname"]
|
112
|
+
end
|
113
|
+
|
114
|
+
#Returns the headers given for the fileupload. Type and more should be here.
|
115
|
+
def headers
|
116
|
+
return @args["headers"]
|
117
|
+
end
|
118
|
+
|
119
|
+
#Returns the content of the file-upload as a string.
|
120
|
+
def to_s
|
121
|
+
return @args["data"]
|
122
|
+
end
|
123
|
+
|
124
|
+
#Saves the content of the fileupload to a given path.
|
125
|
+
def save_to(filepath)
|
126
|
+
File.open(filepath, "w") do |fp|
|
127
|
+
fp.write(self.to_s)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#This methods prevents the object from being converted to JSON. This can make some serious bugs.
|
132
|
+
def to_json(*args)
|
133
|
+
raise "File_upload-objects should not be converted to json."
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require "knj/web"
|
2
|
+
|
3
|
+
#If we are running on JRuby or Rubinius this will seriously speed things up if we are behind a proxy.
|
4
|
+
if RUBY_PLATFORM == "java" or RUBY_ENGINE == "rbx"
|
5
|
+
BasicSocket.do_not_reverse_lookup = true
|
6
|
+
end
|
7
|
+
|
8
|
+
#This class parses the various HTTP requests into easy programmable objects. Get, post, cookie, meta and so on...
|
9
|
+
class Hayabusa::Http_session::Request
|
10
|
+
attr_reader :get, :post, :cookie, :meta, :page_path, :headers, :http_version, :read, :clength, :speed, :percent, :secs_left
|
11
|
+
|
12
|
+
#Sets the various required data on the object. Hayabusa, crlf and arguments.
|
13
|
+
def initialize(args)
|
14
|
+
@args = args
|
15
|
+
@hb = @args[:hb]
|
16
|
+
@crlf = "\r\n"
|
17
|
+
end
|
18
|
+
|
19
|
+
#Reads content from the socket until the end of headers. Also does various error-checks.
|
20
|
+
def read_socket(socket, cont)
|
21
|
+
loop do
|
22
|
+
raise Errno::ECONNRESET, "Socket closed." if socket.closed?
|
23
|
+
read = socket.gets
|
24
|
+
raise Errno::ECONNRESET, "Socket returned non-string: '#{read.class.name}'." if !read.is_a?(String)
|
25
|
+
cont << read
|
26
|
+
break if cont[-4..-1] == "\r\n\r\n" or cont[-2..-1] == "\n\n"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset
|
31
|
+
@modified_since = nil
|
32
|
+
@get = nil
|
33
|
+
@post = nil
|
34
|
+
@cookie = nil
|
35
|
+
@meta = nil
|
36
|
+
@page_path = nil
|
37
|
+
@headers = nil
|
38
|
+
@http_version = nil
|
39
|
+
@read = nil
|
40
|
+
@clength = nil
|
41
|
+
@speec = nil
|
42
|
+
@percent = nil
|
43
|
+
@secs_left = nil
|
44
|
+
end
|
45
|
+
|
46
|
+
#Generates data on object from the given socket.
|
47
|
+
def socket_parse(socket)
|
48
|
+
self.reset
|
49
|
+
cont = ""
|
50
|
+
self.read_socket(socket, cont)
|
51
|
+
|
52
|
+
#Parse URI (page_path and get).
|
53
|
+
match = cont.match(/^(GET|POST|HEAD)\s+(.+)\s+HTTP\/1\.(\d+)\s*/)
|
54
|
+
raise "Could not parse request: '#{cont.split("\n").first}'." if !match
|
55
|
+
|
56
|
+
@http_version = "1.#{match[3]}"
|
57
|
+
|
58
|
+
method = match[1]
|
59
|
+
cont = cont.gsub(match[0], "")
|
60
|
+
|
61
|
+
uri = Knj::Web.parse_uri(match[2])
|
62
|
+
|
63
|
+
page_filepath = Knj::Web.urldec(uri[:path])
|
64
|
+
if page_filepath.length <= 0 or page_filepath == "/" or File.directory?("#{@hb.config[:doc_root]}/#{page_filepath}")
|
65
|
+
page_filepath = "#{page_filepath}/#{@hb.config[:default_page]}"
|
66
|
+
end
|
67
|
+
|
68
|
+
@page_path = "#{@hb.config[:doc_root]}/#{page_filepath}"
|
69
|
+
@get = Knj::Web.parse_urlquery(uri[:query], {:urldecode => true, :force_utf8 => true})
|
70
|
+
|
71
|
+
if @get["_hb_httpsession_id"]
|
72
|
+
@hb.httpsessions_ids[@get["_hb_httpsession_id"]] = @args[:httpsession]
|
73
|
+
end
|
74
|
+
|
75
|
+
begin
|
76
|
+
#Parse headers, cookies and meta.
|
77
|
+
@headers = {}
|
78
|
+
@cookie = {}
|
79
|
+
@meta = {
|
80
|
+
"REQUEST_METHOD" => method,
|
81
|
+
"QUERY_STRING" => uri[:query],
|
82
|
+
"REQUEST_URI" => match[2],
|
83
|
+
"SCRIPT_NAME" => uri[:path]
|
84
|
+
}
|
85
|
+
|
86
|
+
cont.scan(/^(\S+):\s*(.+)\r\n/) do |header_match|
|
87
|
+
key = header_match[0].downcase
|
88
|
+
val = header_match[1]
|
89
|
+
|
90
|
+
@headers[key] = [] if !@headers.has_key?(key)
|
91
|
+
@headers[key] << val
|
92
|
+
|
93
|
+
case key
|
94
|
+
when "cookie"
|
95
|
+
Knj::Web.parse_cookies(val).each do |key, val|
|
96
|
+
@cookie[key] = val
|
97
|
+
end
|
98
|
+
when "content-length"
|
99
|
+
@clength = val.to_i
|
100
|
+
else
|
101
|
+
key = key.upcase.gsub("-", "_")
|
102
|
+
@meta["HTTP_#{key}"] = val
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
#Parse post
|
108
|
+
@post = {}
|
109
|
+
|
110
|
+
if method == "POST"
|
111
|
+
post_treated = {}
|
112
|
+
|
113
|
+
@speed = nil
|
114
|
+
@read = 0
|
115
|
+
post_data = ""
|
116
|
+
|
117
|
+
Thread.new do
|
118
|
+
begin
|
119
|
+
time_cur = Time.now
|
120
|
+
read_last = 0
|
121
|
+
sleep 0.1
|
122
|
+
|
123
|
+
while @clength and @read != nil and @read < @clength
|
124
|
+
break if !@clength or !@read
|
125
|
+
|
126
|
+
time_now = Time.now
|
127
|
+
time_betw = time_now.to_f - time_cur.to_f
|
128
|
+
read_betw = @read - read_last
|
129
|
+
|
130
|
+
time_cur = time_now
|
131
|
+
read_last = @read
|
132
|
+
|
133
|
+
@percent = @read.to_f / @clength.to_f
|
134
|
+
@speed = read_betw.to_f / time_betw.to_f
|
135
|
+
|
136
|
+
bytes_left = @clength - read
|
137
|
+
|
138
|
+
if @speed > 0 and bytes_left > 0
|
139
|
+
@secs_left = bytes_left.to_f / @speed
|
140
|
+
else
|
141
|
+
@secs_left = false
|
142
|
+
end
|
143
|
+
|
144
|
+
sleep 2
|
145
|
+
end
|
146
|
+
rescue => e
|
147
|
+
if @hb
|
148
|
+
@hb.handle_error(e)
|
149
|
+
else
|
150
|
+
STDOUT.print Knj::Errors.error_str(e)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
while @read < @clength
|
156
|
+
read_size = @clength - @read
|
157
|
+
read_size = 4096 if read_size > 4096
|
158
|
+
|
159
|
+
raise Errno::ECONNRESET, "Socket closed." if socket.closed?
|
160
|
+
read = socket.read(read_size)
|
161
|
+
raise Errno::ECONNRESET, "Socket returned non-string: '#{read.class.name}'." if !read.is_a?(String)
|
162
|
+
post_data << read
|
163
|
+
@read += read.length
|
164
|
+
end
|
165
|
+
|
166
|
+
if @headers["content-type"] and match = @headers["content-type"].first.match(/^multipart\/form-data; boundary=(.+)\Z/)
|
167
|
+
post_treated = Hayabusa::Http_session::Post_multipart.new(
|
168
|
+
"io" => StringIO.new("#{post_data}"),
|
169
|
+
"boundary" => match[1],
|
170
|
+
"crlf" => @crlf
|
171
|
+
).return
|
172
|
+
|
173
|
+
self.convert_post(@post, post_treated, {:urldecode => false})
|
174
|
+
else
|
175
|
+
post_data.split("&").each do |splitted|
|
176
|
+
splitted = splitted.split("=")
|
177
|
+
key = Knj::Web.urldec(splitted[0]).to_s.encode("utf-8")
|
178
|
+
val = splitted[1].to_s.encode("utf-8")
|
179
|
+
post_treated[key] = val
|
180
|
+
end
|
181
|
+
|
182
|
+
self.convert_post(@post, post_treated, {:urldecode => true})
|
183
|
+
end
|
184
|
+
end
|
185
|
+
ensure
|
186
|
+
@read = nil
|
187
|
+
@speed = nil
|
188
|
+
@clength = nil
|
189
|
+
@percent = nil
|
190
|
+
@secs_left = nil
|
191
|
+
|
192
|
+
#If it doesnt get unset we could have a serious memory reference GC problem.
|
193
|
+
if @get["_hb_httpsession_id"]
|
194
|
+
@hb.httpsessions_ids.delete(@get["_hb_httpsession_id"])
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
#Parses the if-modified-since header and returns it as a Time-object. Returns false is no if-modified-since-header is given or raises an RuntimeError if it cant be parsed.
|
200
|
+
def modified_since
|
201
|
+
return @modified_since if @modified_since
|
202
|
+
return false if !@meta["HTTP_IF_MODIFIED_SINCE"]
|
203
|
+
|
204
|
+
mod_match = @meta["HTTP_IF_MODIFIED_SINCE"].match(/^([A-z]+),\s+(\d+)\s+([A-z]+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(.+)$/)
|
205
|
+
raise "Could not parse 'HTTP_IF_MODIFIED_SINCE'." if !mod_match
|
206
|
+
|
207
|
+
month_no = Datet.month_str_to_no(mod_match[3])
|
208
|
+
@modified_since = Time.utc(mod_match[4].to_i, month_no, mod_match[2].to_i, mod_match[5].to_i, mod_match[6].to_i, mod_match[7].to_i)
|
209
|
+
|
210
|
+
return @modified_since
|
211
|
+
end
|
212
|
+
|
213
|
+
#Converts post-result to the right type of hash.
|
214
|
+
def convert_post(seton, post_val, args = {})
|
215
|
+
post_val.each do |varname, value|
|
216
|
+
Knj::Web.parse_name(seton, varname, value, args)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|