adminix 0.1.49 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/adminix.gemspec +1 -4
- data/app/assets/images/logo.png +0 -0
- data/app/assets/javascripts/application.js +50 -0
- data/app/assets/javascripts/bootstrap.min.js +7 -0
- data/app/assets/javascripts/dataTables.bootstrap4.js +184 -0
- data/app/assets/javascripts/jquery.dataTables.js +15243 -0
- data/app/assets/javascripts/jquery.min.js +2 -0
- data/app/assets/javascripts/sb-admin-2.min.js +6 -0
- data/app/assets/stylesheets/bootstrap.min.css +6 -0
- data/app/assets/stylesheets/dataTables.bootstrap.css +314 -0
- data/app/assets/stylesheets/dataTables.responsive.css +106 -0
- data/app/assets/stylesheets/font-awesome.min.css +4 -0
- data/app/assets/stylesheets/sb-admin-2.min.css +5 -0
- data/app/views/scripts/restart_watcher.sh.erb +9 -0
- data/app/views/scripts/run_script.sh.erb +12 -0
- data/app/views/scripts/start_process.sh.erb +7 -0
- data/app/views/scripts/stop_process.sh.erb +7 -0
- data/app/views/scripts/update_process.sh.erb +24 -0
- data/app/views/scripts/update_watcher.sh.erb +3 -0
- data/app/views/web/dashboard.html.erb +90 -0
- data/app/views/web/job.html.erb +46 -0
- data/app/views/web/link.html.erb +12 -0
- data/app/views/web/loadstamp.html.erb +57 -0
- data/app/views/web/log.html.erb +49 -0
- data/app/views/web/partials/footer.html.erb +11 -0
- data/app/views/web/partials/header.html.erb +50 -0
- data/bin/install_adminix +40 -0
- data/bin/push +13 -0
- data/development.log +0 -0
- data/exe/adminix +91 -28
- data/lib/adminix.rb +42 -5
- data/lib/adminix/config.rb +170 -96
- data/lib/adminix/entities.rb +5 -0
- data/lib/adminix/entities/job.rb +54 -0
- data/lib/adminix/entities/log.rb +21 -0
- data/lib/adminix/entities/service.rb +211 -0
- data/lib/adminix/entities/sysload_stamp.rb +37 -0
- data/lib/adminix/entities/variable.rb +32 -0
- data/lib/adminix/helpers.rb +7 -2
- data/lib/adminix/helpers/command.rb +73 -0
- data/lib/adminix/helpers/files.rb +82 -0
- data/lib/adminix/helpers/log_reader.rb +16 -0
- data/lib/adminix/helpers/net_http.rb +63 -0
- data/lib/adminix/helpers/output.rb +28 -0
- data/lib/adminix/helpers/systemctl.rb +54 -0
- data/lib/adminix/services.rb +3 -0
- data/lib/adminix/services/app_service.rb +143 -0
- data/lib/adminix/services/logs_service.rb +13 -0
- data/lib/adminix/services/system_load_service.rb +16 -0
- data/lib/adminix/version.rb +1 -1
- data/lib/adminix/watcher.rb +76 -144
- data/lib/adminix/web.rb +4 -0
- data/lib/adminix/web/router.rb +98 -0
- data/lib/adminix/web/server.rb +60 -0
- data/lib/adminix/web/view_helper.rb +14 -0
- data/lib/event_machine.rb +2 -0
- data/lib/event_machine/http_server.rb +2 -0
- data/lib/event_machine/http_server/response.rb +314 -0
- data/lib/event_machine/http_server/server.rb +107 -0
- data/lib/event_machine/tail.rb +2 -0
- data/lib/event_machine/tail/filetail.rb +470 -0
- data/lib/event_machine/tail/globwatcher.rb +294 -0
- metadata +60 -45
- data/lib/adminix/errors.rb +0 -7
- data/lib/adminix/helpers/file.rb +0 -13
- data/lib/adminix/helpers/http.rb +0 -19
- data/lib/adminix/log_watch_handler.rb +0 -23
- data/lib/adminix/server_setup.rb +0 -76
- data/lib/adminix/service.rb +0 -179
- data/lib/adminix/setup.rb +0 -3
- data/lib/adminix/setup/routes.rb +0 -113
- data/lib/adminix/setup/services.rb +0 -139
- data/lib/adminix/setup/views.rb +0 -183
- data/lib/adminix/system.rb +0 -106
- data/views/daemon_scripts/upstart.conf.erb +0 -23
data/lib/adminix/web.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module Adminix
|
2
|
+
module Web
|
3
|
+
class Router < Server
|
4
|
+
include ViewHelper
|
5
|
+
def process_request
|
6
|
+
if @http_request_uri == '/ping'
|
7
|
+
render text: 'pong'
|
8
|
+
elsif @http_request_uri == '/'
|
9
|
+
dashboard_route
|
10
|
+
elsif @http_request_uri == '/link'
|
11
|
+
link_route
|
12
|
+
elsif @http_request_uri =="/log"
|
13
|
+
log_route
|
14
|
+
elsif @http_request_uri =="/job"
|
15
|
+
job_route
|
16
|
+
elsif @http_request_uri =="/loadstamp"
|
17
|
+
loadstamp_route
|
18
|
+
elsif @http_request_uri =="/start"
|
19
|
+
start_route
|
20
|
+
elsif @http_request_uri == "/stop"
|
21
|
+
stop_route
|
22
|
+
|
23
|
+
else
|
24
|
+
asset = Helpers::Files.read_asset(@http_request_uri)
|
25
|
+
if asset
|
26
|
+
render text: asset
|
27
|
+
else
|
28
|
+
not_found_route
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# initialize
|
35
|
+
def params
|
36
|
+
params = {}
|
37
|
+
ls = @http_query_string.to_s.split('&')
|
38
|
+
return params if ls == ''
|
39
|
+
|
40
|
+
ls.each do |p|
|
41
|
+
parts = p.split('=')
|
42
|
+
params[parts[0]] = parts[1]
|
43
|
+
end
|
44
|
+
return params
|
45
|
+
end
|
46
|
+
|
47
|
+
# Routes
|
48
|
+
|
49
|
+
def dashboard_route
|
50
|
+
content = view('dashboard.html')
|
51
|
+
render html: content, content_type: 'text/html'
|
52
|
+
end
|
53
|
+
|
54
|
+
def link_route
|
55
|
+
@page = !params['page'].nil? ? params['page'].to_i : 1
|
56
|
+
@per_page = 2
|
57
|
+
content = view('link.html')
|
58
|
+
render html: content, content_type: 'text/html'
|
59
|
+
end
|
60
|
+
|
61
|
+
def log_route
|
62
|
+
@page = !params['page'].nil? ? params['page'].to_i : 1
|
63
|
+
@per_page = 2
|
64
|
+
content = view('log.html')
|
65
|
+
render html: content, content_type: 'text/html'
|
66
|
+
end
|
67
|
+
|
68
|
+
def job_route
|
69
|
+
@page = !params['page'].nil? ? params['page'].to_i : 1
|
70
|
+
@per_page = 2
|
71
|
+
content = view('job.html')
|
72
|
+
render html: content, content_type: 'text/html'
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def loadstamp_route
|
77
|
+
# Adminix.logger.debug params
|
78
|
+
@page = !params['page'].nil? ? params['page'].to_i : 1
|
79
|
+
@per_page = 2
|
80
|
+
content = view('loadstamp.html')
|
81
|
+
render html: content, content_type: 'text/html'
|
82
|
+
end
|
83
|
+
|
84
|
+
def start_route
|
85
|
+
Adminix.watcher.app_service.start_process
|
86
|
+
content = {"status": "process started"}.to_json
|
87
|
+
render json: content
|
88
|
+
end
|
89
|
+
|
90
|
+
def stop_route
|
91
|
+
Adminix.watcher.app_service.stop_process
|
92
|
+
content = {"status": "process stopped"}.to_json
|
93
|
+
render json: content
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Adminix
|
4
|
+
module Web
|
5
|
+
class Server < EM::HttpServer::Server
|
6
|
+
def process_http_request
|
7
|
+
# puts @http_request_method
|
8
|
+
# puts @http_request_uri
|
9
|
+
# puts @http_query_string
|
10
|
+
# puts @http_protocol
|
11
|
+
# puts @http_content
|
12
|
+
# puts @http[:cookie]
|
13
|
+
# puts @http[:content_type]
|
14
|
+
# you have all the http headers in this hash
|
15
|
+
# puts @http.inspect
|
16
|
+
process_request
|
17
|
+
end
|
18
|
+
|
19
|
+
def http_request_errback(e)
|
20
|
+
# printing the whole exception
|
21
|
+
puts e.inspect
|
22
|
+
end
|
23
|
+
|
24
|
+
# Routes
|
25
|
+
|
26
|
+
def not_found_route
|
27
|
+
content = 'Page not found'
|
28
|
+
render text: content, status: 404
|
29
|
+
end
|
30
|
+
|
31
|
+
# Helpers
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def render(opts)
|
36
|
+
res = if opts[:html]
|
37
|
+
construct_response(opts[:html], content_type: 'text/html')
|
38
|
+
elsif opts[:text]
|
39
|
+
construct_response(opts[:text])
|
40
|
+
elsif opts[:json]
|
41
|
+
construct_response(opts[:json], content_type: 'application/json')
|
42
|
+
end
|
43
|
+
|
44
|
+
res.send_response
|
45
|
+
end
|
46
|
+
|
47
|
+
def construct_response(content, opts = {})
|
48
|
+
res = EM::DelegatedHttpResponse.new(self)
|
49
|
+
res.status = opts[:status] || 200
|
50
|
+
res.content_type opts[:content_type] || 'text/plain'
|
51
|
+
res.content = content
|
52
|
+
res
|
53
|
+
end
|
54
|
+
|
55
|
+
def view(path)
|
56
|
+
Helpers::Files.read_erb_tpl(path, binding)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,314 @@
|
|
1
|
+
# EventMachine HTTP Server
|
2
|
+
# HTTP Response-support class
|
3
|
+
#
|
4
|
+
# Author:: blackhedd (gmail address: garbagecat10).
|
5
|
+
#
|
6
|
+
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
7
|
+
#
|
8
|
+
# This program is made available under the terms of the GPL version 2.
|
9
|
+
#
|
10
|
+
#----------------------------------------------------------------------------
|
11
|
+
#
|
12
|
+
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
13
|
+
#
|
14
|
+
# Gmail: garbagecat10
|
15
|
+
#
|
16
|
+
# This program is free software; you can redistribute it and/or modify
|
17
|
+
# it under the terms of the GNU General Public License as published by
|
18
|
+
# the Free Software Foundation; either version 2 of the License, or
|
19
|
+
# (at your option) any later version.
|
20
|
+
#
|
21
|
+
# This program is distributed in the hope that it will be useful,
|
22
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
23
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
24
|
+
# GNU General Public License for more details.
|
25
|
+
#
|
26
|
+
# You should have received a copy of the GNU General Public License
|
27
|
+
# along with this program; if not, write to the Free Software
|
28
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
29
|
+
#
|
30
|
+
#---------------------------------------------------------------------------
|
31
|
+
#
|
32
|
+
|
33
|
+
module EventMachine
|
34
|
+
|
35
|
+
# This class provides a wide variety of features for generating and
|
36
|
+
# dispatching HTTP responses. It allows you to conveniently generate
|
37
|
+
# headers and content (including chunks and multiparts), and dispatch
|
38
|
+
# responses (including deferred or partially-complete responses).
|
39
|
+
#
|
40
|
+
# Although HttpResponse is coded as a class, it's not complete as it
|
41
|
+
# stands. It assumes that it has certain of the behaviors of
|
42
|
+
# EventMachine::Connection. You must add these behaviors, either by
|
43
|
+
# subclassing HttpResponse, or using the alternate version of this
|
44
|
+
# class, DelegatedHttpResponse. See the test cases for current information
|
45
|
+
# on which behaviors you have to add.
|
46
|
+
#
|
47
|
+
# TODO, someday it would be nice to provide a version of this functionality
|
48
|
+
# that is coded as a Module, so it can simply be mixed into an instance of
|
49
|
+
# EventMachine::Connection.
|
50
|
+
#
|
51
|
+
class HttpResponse
|
52
|
+
attr_accessor :status, :status_string, :content, :headers, :chunks, :multiparts
|
53
|
+
|
54
|
+
def initialize
|
55
|
+
@headers = {}
|
56
|
+
@keep_connection_open = false
|
57
|
+
end
|
58
|
+
|
59
|
+
def keep_connection_open arg=true
|
60
|
+
@keep_connection_open = arg
|
61
|
+
end
|
62
|
+
|
63
|
+
# sugarings for headers
|
64
|
+
def content_type *mime
|
65
|
+
if mime.length > 0
|
66
|
+
@headers["Content-type"] = mime.first.to_s
|
67
|
+
else
|
68
|
+
@headers["Content-type"]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Sugaring for Set-cookie headers. These are a pain because there can easily and
|
73
|
+
# legitimately be more than one. So we use an ugly verb to signify that.
|
74
|
+
# #add_set_cookies does NOT disturb the set-cookie headers which may have been
|
75
|
+
# added on a prior call. #set_cookie clears them out first.
|
76
|
+
def add_set_cookie *ck
|
77
|
+
if ck.length > 0
|
78
|
+
h = (@headers["Set-cookie"] ||= [])
|
79
|
+
ck.each {|c| h << c}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
def set_cookie *ck
|
83
|
+
h = (@headers["Set-cookie"] ||= [])
|
84
|
+
if ck.length > 0
|
85
|
+
h.clear
|
86
|
+
add_set_cookie *ck
|
87
|
+
else
|
88
|
+
h
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
# This is intended to send a complete HTTP response, including closing the connection
|
94
|
+
# if appropriate at the end of the transmission. Don't use this method to send partial
|
95
|
+
# or iterated responses. This method will send chunks and multiparts provided they
|
96
|
+
# are all available when we get here.
|
97
|
+
# Note that the default @status is 200 if the value doesn't exist.
|
98
|
+
def send_response
|
99
|
+
send_headers
|
100
|
+
send_body
|
101
|
+
send_trailer
|
102
|
+
close_connection_after_writing unless (@keep_connection_open and (@status || 200) < 500)
|
103
|
+
end
|
104
|
+
|
105
|
+
# Send the headers out in alpha-sorted order. This will degrade performance to some
|
106
|
+
# degree, and is intended only to simplify the construction of unit tests.
|
107
|
+
#
|
108
|
+
def send_headers
|
109
|
+
raise "sent headers already" if @sent_headers
|
110
|
+
@sent_headers = true
|
111
|
+
|
112
|
+
fixup_headers
|
113
|
+
|
114
|
+
ary = []
|
115
|
+
ary << "HTTP/1.1 #{@status || 200} #{@status_string || '...'}\r\n"
|
116
|
+
ary += generate_header_lines(@headers)
|
117
|
+
ary << "\r\n"
|
118
|
+
|
119
|
+
send_data ary.join
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def generate_header_lines in_hash
|
124
|
+
out_ary = []
|
125
|
+
in_hash.keys.sort.each {|k|
|
126
|
+
v = in_hash[k]
|
127
|
+
if v.is_a?(Array)
|
128
|
+
v.each {|v1| out_ary << "#{k}: #{v1}\r\n" }
|
129
|
+
else
|
130
|
+
out_ary << "#{k}: #{v}\r\n"
|
131
|
+
end
|
132
|
+
}
|
133
|
+
out_ary
|
134
|
+
end
|
135
|
+
private :generate_header_lines
|
136
|
+
|
137
|
+
|
138
|
+
# Examine the content type and data and other things, and perform a final
|
139
|
+
# fixup of the header array. We expect this to be called just before sending
|
140
|
+
# headers to the remote peer.
|
141
|
+
# In the case of multiparts, we ASSUME we will get called before any content
|
142
|
+
# gets sent out, because the multipart boundary is created here.
|
143
|
+
#
|
144
|
+
def fixup_headers
|
145
|
+
if @content
|
146
|
+
@headers["Content-length"] = content.to_s.bytesize
|
147
|
+
elsif @chunks
|
148
|
+
@headers["Transfer-encoding"] = "chunked"
|
149
|
+
# Might be nice to ENSURE there is no content-length header,
|
150
|
+
# but how to detect all the possible permutations of upper/lower case?
|
151
|
+
elsif @multiparts
|
152
|
+
@multipart_boundary = self.class.concoct_multipart_boundary
|
153
|
+
@headers["Content-type"] = "multipart/x-mixed-replace; boundary=\"#{@multipart_boundary}\""
|
154
|
+
else
|
155
|
+
@headers["Content-length"] = 0
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# we send either content, chunks, or multiparts. Content can only be sent once.
|
160
|
+
# Chunks and multiparts can be sent any number of times.
|
161
|
+
# DO NOT close the connection or send any goodbye kisses. This method can
|
162
|
+
# be called multiple times to send out chunks or multiparts.
|
163
|
+
def send_body
|
164
|
+
if @content
|
165
|
+
send_content
|
166
|
+
elsif @chunks
|
167
|
+
send_chunks
|
168
|
+
elsif @multiparts
|
169
|
+
send_multiparts
|
170
|
+
else
|
171
|
+
@content = ""
|
172
|
+
send_content
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# send a trailer which depends on the type of body we're dealing with.
|
177
|
+
# The assumption is that we're about to end the transmission of this particular
|
178
|
+
# HTTP response. (A connection-close may or may not follow.)
|
179
|
+
#
|
180
|
+
def send_trailer
|
181
|
+
send_headers unless @sent_headers
|
182
|
+
if @content
|
183
|
+
# no-op
|
184
|
+
elsif @chunks
|
185
|
+
unless @last_chunk_sent
|
186
|
+
chunk ""
|
187
|
+
send_chunks
|
188
|
+
end
|
189
|
+
elsif @multiparts
|
190
|
+
# in the lingo of RFC 2046/5.1.1, we're sending an "epilog"
|
191
|
+
# consisting of a blank line. I really don't know how that is
|
192
|
+
# supposed to interact with the case where we leave the connection
|
193
|
+
# open after transmitting the multipart response.
|
194
|
+
send_data "\r\n--#{@multipart_boundary}--\r\n\r\n"
|
195
|
+
else
|
196
|
+
# no-op
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def send_content
|
201
|
+
raise "sent content already" if @sent_content
|
202
|
+
@sent_content = true
|
203
|
+
send_data((@content || "").to_s)
|
204
|
+
end
|
205
|
+
|
206
|
+
# add a chunk to go to the output.
|
207
|
+
# Will cause the headers to pick up "content-transfer-encoding"
|
208
|
+
# Add the chunk to a list. Calling #send_chunks will send out the
|
209
|
+
# available chunks and clear the chunk list WITHOUT closing the connection,
|
210
|
+
# so it can be called any number of times.
|
211
|
+
# TODO!!! Per RFC2616, we may not send chunks to an HTTP/1.0 client.
|
212
|
+
# Raise an exception here if our user tries to do so.
|
213
|
+
# Chunked transfer coding is defined in RFC2616 pgh 3.6.1.
|
214
|
+
# The argument can be a string or a hash. The latter allows for
|
215
|
+
# sending chunks with extensions (someday).
|
216
|
+
#
|
217
|
+
def chunk text
|
218
|
+
@chunks ||= []
|
219
|
+
@chunks << text
|
220
|
+
end
|
221
|
+
|
222
|
+
# send the contents of the chunk list and clear it out.
|
223
|
+
# ASSUMES that headers have been sent.
|
224
|
+
# Does NOT close the connection.
|
225
|
+
# Can be called multiple times.
|
226
|
+
# According to RFC2616, phg 3.6.1, the last chunk will be zero length.
|
227
|
+
# But some caller could accidentally set a zero-length chunk in the middle
|
228
|
+
# of the stream. If that should happen, raise an exception.
|
229
|
+
# The reason for supporting chunks that are hashes instead of just strings
|
230
|
+
# is to enable someday supporting chunk-extension codes (cf the RFC).
|
231
|
+
# TODO!!! We're not supporting the final entity-header that may be
|
232
|
+
# transmitted after the last (zero-length) chunk.
|
233
|
+
#
|
234
|
+
def send_chunks
|
235
|
+
send_headers unless @sent_headers
|
236
|
+
while chunk = @chunks.shift
|
237
|
+
raise "last chunk already sent" if @last_chunk_sent
|
238
|
+
text = chunk.is_a?(Hash) ? chunk[:text] : chunk.to_s
|
239
|
+
send_data "#{format("%x", text.length).upcase}\r\n#{text}\r\n"
|
240
|
+
@last_chunk_sent = true if text.length == 0
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# To add a multipart to the outgoing response, specify the headers and the
|
245
|
+
# body. If only a string is given, it's treated as the body (in this case,
|
246
|
+
# the header is assumed to be empty).
|
247
|
+
#
|
248
|
+
def multipart arg
|
249
|
+
vals = if arg.is_a?(String)
|
250
|
+
{:body => arg, :headers => {}}
|
251
|
+
else
|
252
|
+
arg
|
253
|
+
end
|
254
|
+
|
255
|
+
@multiparts ||= []
|
256
|
+
@multiparts << vals
|
257
|
+
end
|
258
|
+
|
259
|
+
# Multipart syntax is defined in RFC 2046, pgh 5.1.1 et seq.
|
260
|
+
# The CRLF which introduces the boundary line of each part (content entity)
|
261
|
+
# is defined as being part of the boundary, not of the preceding part.
|
262
|
+
# So we don't need to mess with interpreting the last bytes of a part
|
263
|
+
# to ensure they are CRLF-terminated.
|
264
|
+
#
|
265
|
+
def send_multiparts
|
266
|
+
send_headers unless @sent_headers
|
267
|
+
while part = @multiparts.shift
|
268
|
+
send_data "\r\n--#{@multipart_boundary}\r\n"
|
269
|
+
send_data( generate_header_lines( part[:headers] || {} ).join)
|
270
|
+
send_data "\r\n"
|
271
|
+
send_data part[:body].to_s
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# TODO, this is going to be way too slow. Cache up the uuidgens.
|
276
|
+
#
|
277
|
+
def self.concoct_multipart_boundary
|
278
|
+
@multipart_index ||= 0
|
279
|
+
@multipart_index += 1
|
280
|
+
if @multipart_index >= 1000
|
281
|
+
@multipart_index = 0
|
282
|
+
@multipart_guid = nil
|
283
|
+
end
|
284
|
+
@multipart_guid ||= `uuidgen -r`.chomp.gsub(/\-/,"")
|
285
|
+
"#{@multipart_guid}#{@multipart_index}"
|
286
|
+
end
|
287
|
+
|
288
|
+
def send_redirect location
|
289
|
+
@status = 302 # TODO, make 301 available by parameter
|
290
|
+
@status_string = "Moved Temporarily"
|
291
|
+
@headers["Location"] = location
|
292
|
+
send_response
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
#----------------------------------------------------------------------------
|
298
|
+
|
299
|
+
require 'forwardable'
|
300
|
+
|
301
|
+
module EventMachine
|
302
|
+
class DelegatedHttpResponse < HttpResponse
|
303
|
+
extend Forwardable
|
304
|
+
def_delegators :@delegate,
|
305
|
+
:send_data,
|
306
|
+
:close_connection,
|
307
|
+
:close_connection_after_writing
|
308
|
+
|
309
|
+
def initialize dele
|
310
|
+
super()
|
311
|
+
@delegate = dele
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|