kirk 0.1.8-java → 0.2.0.beta.1-java
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/lib/kirk.rb +24 -26
- data/lib/kirk/cli.rb +1 -1
- data/lib/kirk/client.rb +57 -18
- data/lib/kirk/client/exchange.rb +146 -26
- data/lib/kirk/client/group.rb +94 -0
- data/lib/kirk/client/request.rb +42 -6
- data/lib/kirk/client/response.rb +20 -4
- data/lib/kirk/jetty.rb +22 -14
- data/lib/kirk/jetty/{jetty-client-7.2.2.v20101205.jar → jetty-client-7.3.0.v20110203.jar} +0 -0
- data/lib/kirk/jetty/jetty-continuation-7.3.0.v20110203.jar +0 -0
- data/lib/kirk/jetty/jetty-http-7.3.0.v20110203.jar +0 -0
- data/lib/kirk/jetty/jetty-io-7.3.0.v20110203.jar +0 -0
- data/lib/kirk/jetty/jetty-server-7.3.0.v20110203.jar +0 -0
- data/lib/kirk/jetty/jetty-util-7.3.0.v20110203.jar +0 -0
- data/lib/kirk/native.jar +0 -0
- data/lib/kirk/native.rb +12 -0
- data/lib/kirk/server.rb +11 -1
- data/lib/kirk/server/application_config.rb +56 -0
- data/lib/kirk/server/bootstrap.rb +59 -0
- data/lib/kirk/server/builder.rb +145 -0
- data/lib/kirk/{applications → server}/deploy_watcher.rb +1 -1
- data/lib/kirk/server/handler.rb +140 -0
- data/lib/kirk/server/hot_deployable.rb +63 -0
- data/lib/kirk/server/input_stream.rb +114 -0
- data/lib/kirk/{applications → server}/redeploy_client.rb +1 -1
- data/lib/kirk/version.rb +1 -1
- metadata +19 -16
- data/lib/kirk/applications/config.rb +0 -50
- data/lib/kirk/applications/hot_deployable.rb +0 -65
- data/lib/kirk/applications/rack.rb +0 -21
- data/lib/kirk/bootstrap.rb +0 -59
- data/lib/kirk/builder.rb +0 -143
- data/lib/kirk/client/connection.rb +0 -18
- data/lib/kirk/client/session.rb +0 -40
- data/lib/kirk/handler.rb +0 -129
- data/lib/kirk/input_stream.rb +0 -264
- data/lib/kirk/jetty/jetty-server-7.2.2.v20101205.jar +0 -0
data/lib/kirk/client/response.rb
CHANGED
@@ -1,10 +1,26 @@
|
|
1
1
|
class Kirk::Client
|
2
2
|
class Response
|
3
|
-
|
3
|
+
attr_accessor :version, :status, :body, :headers, :exception
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
@
|
5
|
+
def initialize(buffer_body)
|
6
|
+
@status = nil
|
7
|
+
@version = nil
|
8
|
+
@headers = {}
|
9
|
+
@buffer_body = buffer_body
|
10
|
+
@body = buffer_body ? "" : nil
|
11
|
+
@exception = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def buffer_body?
|
15
|
+
@buffer_body
|
16
|
+
end
|
17
|
+
|
18
|
+
def success?
|
19
|
+
@status && @status < 400 && !@exception
|
20
|
+
end
|
21
|
+
|
22
|
+
def exception?
|
23
|
+
@exception
|
8
24
|
end
|
9
25
|
end
|
10
26
|
end
|
data/lib/kirk/jetty.rb
CHANGED
@@ -1,23 +1,31 @@
|
|
1
|
-
require "
|
2
|
-
|
3
|
-
require "kirk/jetty/servlet-api-2.5"
|
1
|
+
# Only require jars if in the "master" process
|
2
|
+
unless Kirk.sub_process?
|
3
|
+
require "kirk/jetty/servlet-api-2.5"
|
4
|
+
|
5
|
+
%w(util http io continuation server client).each do |mod|
|
6
|
+
require "kirk/jetty/jetty-#{mod}-7.3.0.v20110203"
|
7
|
+
end
|
8
|
+
end
|
4
9
|
|
5
10
|
module Kirk
|
6
11
|
module Jetty
|
7
12
|
# Gimme Jetty
|
8
|
-
|
9
|
-
|
13
|
+
java_import "org.eclipse.jetty.client.HttpClient"
|
14
|
+
java_import "org.eclipse.jetty.client.HttpExchange"
|
15
|
+
java_import "org.eclipse.jetty.client.ContentExchange"
|
16
|
+
|
17
|
+
java_import "org.eclipse.jetty.io.ByteArrayBuffer"
|
10
18
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
19
|
+
java_import "org.eclipse.jetty.server.nio.SelectChannelConnector"
|
20
|
+
java_import "org.eclipse.jetty.server.handler.AbstractHandler"
|
21
|
+
java_import "org.eclipse.jetty.server.handler.ContextHandler"
|
22
|
+
java_import "org.eclipse.jetty.server.handler.ContextHandlerCollection"
|
23
|
+
java_import "org.eclipse.jetty.server.Server"
|
16
24
|
|
17
|
-
|
18
|
-
|
19
|
-
|
25
|
+
java_import "org.eclipse.jetty.util.component.LifeCycle"
|
26
|
+
java_import "org.eclipse.jetty.util.log.Log"
|
27
|
+
java_import "org.eclipse.jetty.util.log.JavaUtilLog"
|
20
28
|
|
21
|
-
Log.set_log Jetty::JavaUtilLog.new
|
29
|
+
Log.set_log Jetty::JavaUtilLog.new unless Kirk.sub_process?
|
22
30
|
end
|
23
31
|
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/kirk/native.jar
CHANGED
Binary file
|
data/lib/kirk/native.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'kirk/native.jar'
|
2
|
+
|
3
|
+
module Kirk
|
4
|
+
Jetty # Trigger the jetty autoload
|
5
|
+
|
6
|
+
module Native
|
7
|
+
java_import "com.strobecorp.kirk.ApplicationConfig"
|
8
|
+
java_import "com.strobecorp.kirk.HotDeployableApplication"
|
9
|
+
java_import "com.strobecorp.kirk.LogFormatter"
|
10
|
+
java_import "com.strobecorp.kirk.RewindableInputStream"
|
11
|
+
end
|
12
|
+
end
|
data/lib/kirk/server.rb
CHANGED
@@ -1,5 +1,15 @@
|
|
1
|
+
require 'kirk'
|
2
|
+
|
1
3
|
module Kirk
|
2
4
|
class Server
|
5
|
+
autoload :ApplicationConfig, 'kirk/server/application_config'
|
6
|
+
autoload :Builder, 'kirk/server/builder'
|
7
|
+
autoload :DeployWatcher, 'kirk/server/deploy_watcher'
|
8
|
+
autoload :Handler, 'kirk/server/handler'
|
9
|
+
autoload :HotDeployable, 'kirk/server/hot_deployable'
|
10
|
+
autoload :InputStream, 'kirk/server/input_stream'
|
11
|
+
autoload :RedeployClient, 'kirk/server/redeploy_client'
|
12
|
+
|
3
13
|
def self.build(file = nil, &blk)
|
4
14
|
root = File.dirname(file) if file
|
5
15
|
builder = Builder.new(root)
|
@@ -27,7 +37,7 @@ module Kirk
|
|
27
37
|
if Jetty::AbstractHandler === handler
|
28
38
|
@handler = handler
|
29
39
|
elsif handler.respond_to?(:call)
|
30
|
-
@handler =
|
40
|
+
@handler = Handler.new(handler)
|
31
41
|
else
|
32
42
|
raise "#{handler.inspect} is not a valid Rack application"
|
33
43
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Kirk
|
2
|
+
class Server::ApplicationConfig
|
3
|
+
include Native::ApplicationConfig
|
4
|
+
|
5
|
+
attr_accessor :env,
|
6
|
+
:hosts,
|
7
|
+
:listen,
|
8
|
+
:watch,
|
9
|
+
:rackup,
|
10
|
+
:application_path,
|
11
|
+
:bootstrap_path
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@env = {}
|
15
|
+
@hosts = []
|
16
|
+
@listen = listen
|
17
|
+
end
|
18
|
+
|
19
|
+
def application_path
|
20
|
+
@application_path || File.dirname(rackup)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Handle the java interface
|
24
|
+
alias getApplicationPath application_path
|
25
|
+
alias getRackupPath rackup
|
26
|
+
alias getBootstrapPath bootstrap_path
|
27
|
+
|
28
|
+
def getEnvironment
|
29
|
+
map = java.util.HashMap.new
|
30
|
+
env = ENV.dup
|
31
|
+
|
32
|
+
self.env.each do |key, val|
|
33
|
+
env[key.to_s] = val
|
34
|
+
end
|
35
|
+
|
36
|
+
env.each do |key, val|
|
37
|
+
next unless val
|
38
|
+
|
39
|
+
key = key.to_java_string
|
40
|
+
val = val.to_s.to_java_string
|
41
|
+
|
42
|
+
map.put(key, val)
|
43
|
+
end
|
44
|
+
|
45
|
+
map
|
46
|
+
end
|
47
|
+
|
48
|
+
def getKirkVersionStamper
|
49
|
+
<<-RUBY
|
50
|
+
module Kirk
|
51
|
+
PARENT_VERSION = #{VERSION.inspect}.freeze
|
52
|
+
end
|
53
|
+
RUBY
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'java'
|
2
|
+
|
3
|
+
module Kirk
|
4
|
+
class Server
|
5
|
+
class Bootstrap
|
6
|
+
def warmup(application_path)
|
7
|
+
Dir.chdir File.expand_path(application_path)
|
8
|
+
|
9
|
+
load_rubygems
|
10
|
+
|
11
|
+
load_bundle.tap do
|
12
|
+
add_kirk_to_load_path
|
13
|
+
|
14
|
+
load_rack
|
15
|
+
load_kirk
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def run(rackup)
|
20
|
+
app, options = Rack::Builder.parse_file(rackup)
|
21
|
+
|
22
|
+
Handler.new(app)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def load_rubygems
|
28
|
+
require 'rubygems'
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_bundle
|
32
|
+
if File.exist?('Gemfile')
|
33
|
+
require 'bundler/setup'
|
34
|
+
|
35
|
+
if File.exist?('Gemfile.lock')
|
36
|
+
require 'digest/sha1'
|
37
|
+
str = File.read('Gemfile') + File.read('Gemfile.lock')
|
38
|
+
Digest::SHA1.hexdigest(str)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_kirk_to_load_path
|
44
|
+
$:.unshift File.expand_path('../../..', __FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def load_rack
|
48
|
+
gem "rack", ">= 1.0.0"
|
49
|
+
require 'rack'
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_kirk
|
53
|
+
require 'kirk/server'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
Kirk::Server::Bootstrap.new
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Kirk
|
2
|
+
class MissingConfigFile < RuntimeError ; end
|
3
|
+
class MissingRackupFile < RuntimeError ; end
|
4
|
+
|
5
|
+
class Server
|
6
|
+
class Builder
|
7
|
+
|
8
|
+
VALID_LOG_LEVELS = %w(severe warning info config fine finer finest all)
|
9
|
+
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
def initialize(root = nil)
|
13
|
+
@root = root || Dir.pwd
|
14
|
+
@current = nil
|
15
|
+
@configs = []
|
16
|
+
@options = {
|
17
|
+
:watcher => DeployWatcher.new("/tmp/kirk.sock")
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def load(glob)
|
22
|
+
glob = expand_path(glob)
|
23
|
+
|
24
|
+
files = Dir[glob].select { |f| File.file?(f) }
|
25
|
+
|
26
|
+
if files.empty?
|
27
|
+
raise MissingConfigFile, "glob `#{glob}` did not match any files"
|
28
|
+
end
|
29
|
+
|
30
|
+
files.each do |file|
|
31
|
+
with_root File.dirname(file) do
|
32
|
+
instance_eval(File.read(file), file)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def log(opts = {})
|
38
|
+
level = opts[:level]
|
39
|
+
raise "Invalid log level" unless VALID_LOG_LEVELS.include?(level.to_s)
|
40
|
+
@options[:log_level] = level.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def rack(rackup)
|
44
|
+
rackup = expand_path(rackup)
|
45
|
+
|
46
|
+
unless File.exist?(rackup)
|
47
|
+
raise MissingRackupFile, "rackup file `#{rackup}` does not exist"
|
48
|
+
end
|
49
|
+
|
50
|
+
@current = new_config
|
51
|
+
@current.rackup = rackup
|
52
|
+
|
53
|
+
yield if block_given?
|
54
|
+
|
55
|
+
ensure
|
56
|
+
@configs << @current
|
57
|
+
@current = nil
|
58
|
+
end
|
59
|
+
|
60
|
+
def env(env)
|
61
|
+
@current.env.merge!(env)
|
62
|
+
end
|
63
|
+
|
64
|
+
def hosts(*hosts)
|
65
|
+
@current.hosts.concat hosts
|
66
|
+
end
|
67
|
+
|
68
|
+
def listen(listen)
|
69
|
+
listen = listen.to_s
|
70
|
+
listen = ":#{listen}" unless listen.index(':')
|
71
|
+
listen = "0.0.0.0#{listen}" if listen.index(':') == 0
|
72
|
+
|
73
|
+
@current.listen = listen
|
74
|
+
end
|
75
|
+
|
76
|
+
def watch(*watch)
|
77
|
+
@current.watch = watch
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_handler
|
81
|
+
handlers = @configs.map do |c|
|
82
|
+
Jetty::ContextHandler.new.tap do |ctx|
|
83
|
+
# Set the virtual hosts
|
84
|
+
unless c.hosts.empty?
|
85
|
+
ctx.set_virtual_hosts(c.hosts)
|
86
|
+
end
|
87
|
+
|
88
|
+
application = HotDeployable.new(c)
|
89
|
+
application.add_watcher(watcher)
|
90
|
+
|
91
|
+
ctx.set_connector_names [c.listen]
|
92
|
+
ctx.set_handler application
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Jetty::ContextHandlerCollection.new.tap do |collection|
|
97
|
+
collection.set_handlers(handlers)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_connectors
|
102
|
+
@connectors = {}
|
103
|
+
|
104
|
+
@configs.each do |config|
|
105
|
+
next if @connectors.key?(config.listen)
|
106
|
+
|
107
|
+
host, port = config.listen.split(':')
|
108
|
+
|
109
|
+
connector = Jetty::SelectChannelConnector.new
|
110
|
+
connector.set_host(host)
|
111
|
+
connector.set_port(port.to_i)
|
112
|
+
|
113
|
+
@connectors[config.listen] = connector
|
114
|
+
end
|
115
|
+
|
116
|
+
@connectors.values
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def watcher
|
122
|
+
@options[:watcher]
|
123
|
+
end
|
124
|
+
|
125
|
+
def with_root(root)
|
126
|
+
old, @root = @root, root
|
127
|
+
yield
|
128
|
+
ensure
|
129
|
+
@root = old
|
130
|
+
end
|
131
|
+
|
132
|
+
def expand_path(path)
|
133
|
+
File.expand_path(path, @root)
|
134
|
+
end
|
135
|
+
|
136
|
+
def new_config
|
137
|
+
ApplicationConfig.new.tap do |config|
|
138
|
+
config.listen = '0.0.0.0:9090'
|
139
|
+
config.watch = [ ]
|
140
|
+
config.bootstrap_path = File.expand_path('../bootstrap.rb', __FILE__)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module Kirk
|
2
|
+
class Server
|
3
|
+
class Handler < Jetty::AbstractHandler
|
4
|
+
java_import 'java.util.zip.GZIPInputStream'
|
5
|
+
java_import 'java.util.zip.InflaterInputStream'
|
6
|
+
|
7
|
+
# Trigger the autoload so that the first access to the class
|
8
|
+
# does not happen in a thread.
|
9
|
+
InputStream
|
10
|
+
|
11
|
+
# Required environment variable keys
|
12
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
13
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
14
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
15
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
16
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
17
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
18
|
+
CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
19
|
+
CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
|
20
|
+
REQUEST_URI = 'REQUEST_URI'.freeze
|
21
|
+
REMOTE_HOST = 'REMOTE_HOST'.freeze
|
22
|
+
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
23
|
+
REMOTE_USER = 'REMOTE_USER'.freeze
|
24
|
+
|
25
|
+
# Rack specific variable keys
|
26
|
+
RACK_VERSION = 'rack.version'.freeze
|
27
|
+
RACK_URL_SCHEME = 'rack.url_scheme'.freeze
|
28
|
+
RACK_INPUT = 'rack.input'.freeze
|
29
|
+
RACK_ERRORS = 'rack.errors'.freeze
|
30
|
+
RACK_MULTITHREAD = 'rack.multithread'.freeze
|
31
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
|
32
|
+
RACK_RUN_ONCE = 'rack.run_once'.freeze
|
33
|
+
HTTP_PREFIX = 'HTTP_'.freeze
|
34
|
+
|
35
|
+
# Rack response header names
|
36
|
+
CONTENT_TYPE_RESP = 'Content-Type'
|
37
|
+
CONTENT_LENGTH_RESP = 'Content-Length'
|
38
|
+
|
39
|
+
# Kirk information
|
40
|
+
SERVER = "#{NAME} #{VERSION}".freeze
|
41
|
+
SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
|
42
|
+
|
43
|
+
DEFAULT_RACK_ENV = {
|
44
|
+
SERVER_SOFTWARE => SERVER,
|
45
|
+
|
46
|
+
# Rack stuff
|
47
|
+
RACK_ERRORS => STDERR,
|
48
|
+
RACK_MULTITHREAD => true,
|
49
|
+
RACK_MULTIPROCESS => false,
|
50
|
+
RACK_RUN_ONCE => false,
|
51
|
+
}
|
52
|
+
|
53
|
+
CONTENT_LENGTH_TYPE_REGEXP = /^Content-(?:Type|Length)$/i
|
54
|
+
|
55
|
+
def self.new(app)
|
56
|
+
inst = super()
|
57
|
+
inst.app = app
|
58
|
+
inst
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_accessor :app
|
62
|
+
|
63
|
+
def handle(target, base_request, request, response)
|
64
|
+
begin
|
65
|
+
env = DEFAULT_RACK_ENV.merge(
|
66
|
+
SCRIPT_NAME => "",
|
67
|
+
PATH_INFO => request.get_path_info,
|
68
|
+
REQUEST_URI => request.getRequestURI,
|
69
|
+
REQUEST_METHOD => request.get_method || "GET",
|
70
|
+
RACK_URL_SCHEME => request.get_scheme || "http",
|
71
|
+
QUERY_STRING => request.get_query_string || "",
|
72
|
+
SERVER_NAME => request.get_server_name || "",
|
73
|
+
REMOTE_HOST => request.get_remote_host || "",
|
74
|
+
REMOTE_ADDR => request.get_remote_addr || "",
|
75
|
+
REMOTE_USER => request.get_remote_user || "",
|
76
|
+
SERVER_PORT => request.get_server_port.to_s,
|
77
|
+
RACK_VERSION => ::Rack::VERSION)
|
78
|
+
|
79
|
+
# Process the content length
|
80
|
+
if (content_length = request.get_content_length) >= 0
|
81
|
+
env[CONTENT_LENGTH] = content_length.to_s
|
82
|
+
else
|
83
|
+
env[CONTENT_LENGTH] = "0"
|
84
|
+
end
|
85
|
+
|
86
|
+
if (content_type = request.get_content_type)
|
87
|
+
env[CONTENT_TYPE] = content_type unless content_type == ''
|
88
|
+
end
|
89
|
+
|
90
|
+
request.get_header_names.each do |header|
|
91
|
+
next if header =~ CONTENT_LENGTH_TYPE_REGEXP
|
92
|
+
value = request.get_header(header)
|
93
|
+
|
94
|
+
header.tr! '-', '_'
|
95
|
+
header.upcase!
|
96
|
+
|
97
|
+
header = "#{HTTP_PREFIX}#{header}"
|
98
|
+
env[header] = value unless env.key?(header) || value == ''
|
99
|
+
end
|
100
|
+
|
101
|
+
input = request.get_input_stream
|
102
|
+
|
103
|
+
case env['HTTP_CONTENT_ENCODING']
|
104
|
+
when 'deflate' then input = InflaterInputStream.new(input)
|
105
|
+
when 'gzip' then input = GZIPInputStream.new(input)
|
106
|
+
end
|
107
|
+
|
108
|
+
input = InputStream.new(input)
|
109
|
+
env[RACK_INPUT] = input
|
110
|
+
|
111
|
+
# Dispatch the request
|
112
|
+
status, headers, body = @app.call(env)
|
113
|
+
|
114
|
+
response.set_status(status.to_i)
|
115
|
+
|
116
|
+
headers.each do |header, value|
|
117
|
+
case header
|
118
|
+
when CONTENT_TYPE_RESP
|
119
|
+
response.set_content_type(value)
|
120
|
+
when CONTENT_LENGTH_RESP
|
121
|
+
response.set_content_length(value.to_i)
|
122
|
+
else
|
123
|
+
response.set_header(header, value)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
buffer = response.get_output_stream
|
128
|
+
body.each do |s|
|
129
|
+
buffer.write(s.to_java_bytes)
|
130
|
+
end
|
131
|
+
|
132
|
+
body.close if body.respond_to?(:close)
|
133
|
+
ensure
|
134
|
+
input.recycle if input.respond_to?(:recycle)
|
135
|
+
request.set_handled(true)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|