bayserver-docker-ajp 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +7 -0
  2. data/lib/baykit/bayserver/docker/ajp/ajp_command.rb +55 -0
  3. data/lib/baykit/bayserver/docker/ajp/ajp_command_handler.rb +26 -0
  4. data/lib/baykit/bayserver/docker/ajp/ajp_command_unpacker.rb +66 -0
  5. data/lib/baykit/bayserver/docker/ajp/ajp_docker.rb +15 -0
  6. data/lib/baykit/bayserver/docker/ajp/ajp_inbound_handler.rb +292 -0
  7. data/lib/baykit/bayserver/docker/ajp/ajp_packet.rb +112 -0
  8. data/lib/baykit/bayserver/docker/ajp/ajp_packet_factory.rb +18 -0
  9. data/lib/baykit/bayserver/docker/ajp/ajp_packet_unpacker.rb +150 -0
  10. data/lib/baykit/bayserver/docker/ajp/ajp_port_docker.rb +52 -0
  11. data/lib/baykit/bayserver/docker/ajp/ajp_protocol_handler.rb +55 -0
  12. data/lib/baykit/bayserver/docker/ajp/ajp_type.rb +22 -0
  13. data/lib/baykit/bayserver/docker/ajp/ajp_warp_docker.rb +54 -0
  14. data/lib/baykit/bayserver/docker/ajp/ajp_warp_handler.rb +258 -0
  15. data/lib/baykit/bayserver/docker/ajp/command/cmd_data.rb +59 -0
  16. data/lib/baykit/bayserver/docker/ajp/command/cmd_end_response.rb +48 -0
  17. data/lib/baykit/bayserver/docker/ajp/command/cmd_forward_request.rb +288 -0
  18. data/lib/baykit/bayserver/docker/ajp/command/cmd_get_body_chunk.rb +44 -0
  19. data/lib/baykit/bayserver/docker/ajp/command/cmd_send_body_chunk.rb +71 -0
  20. data/lib/baykit/bayserver/docker/ajp/command/cmd_send_headers.rb +153 -0
  21. data/lib/baykit/bayserver/docker/ajp/command/cmd_shutdown.rb +37 -0
  22. data/lib/baykit/bayserver/docker/ajp/command/package.rb +7 -0
  23. data/lib/baykit/bayserver/docker/ajp/package.rb +12 -0
  24. metadata +78 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9f0897b0f990499668e38deec50e8c9426cb14c4a41e403bb08c7821a294f692
4
+ data.tar.gz: 527c1a81a6063b4079781c50e8efb8d2a0324418f6fbf55a4fad2a1fd3f1be73
5
+ SHA512:
6
+ metadata.gz: f2c5f4a2373e576628a41326b018a301a0e51312068db3e8c16e849ede6e7bdd5c2ece680151c761c3d31ad8ebfd895e90ae14d77201420cd3858633a4c00d87
7
+ data.tar.gz: 13dab20d83962f56f704f312a4a49817be9689dfd5416646a253520f23876b61abbfa8a62bce73406dee3c07242ae2fd53d612ac21a11cf23399a7e6997755c0
@@ -0,0 +1,55 @@
1
+ #
2
+ # AJP Protocol
3
+ # https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
4
+ #
5
+ module Baykit
6
+ module BayServer
7
+ module Docker
8
+ module Ajp
9
+ class AjpCommand < Baykit::BayServer::Protocol::Command
10
+
11
+ attr_accessor :to_server
12
+
13
+ def initialize(type, to_server)
14
+ super type
15
+ @to_server = to_server
16
+ end
17
+
18
+ def unpack(pkt)
19
+ if pkt.type() != @type
20
+ raise RuntimeError.new("Illegal State")
21
+ end
22
+ @to_server = pkt.to_server
23
+ end
24
+
25
+ #
26
+ # Super class method must be called from last line of override method
27
+ # since header cannot be packed before data is constructed.
28
+ #
29
+ def pack(pkt)
30
+ if pkt.type() != @type
31
+ raise RuntimeError.new "Illegal State"
32
+ end
33
+ pkt.to_server = @to_server
34
+ pack_header(pkt)
35
+ end
36
+
37
+ def pack_header(pkt)
38
+ acc = pkt.new_ajp_header_accessor
39
+ if pkt.to_server
40
+ acc.put_byte(0x12)
41
+ acc.put_byte(0x34)
42
+ else
43
+ acc.put_byte('A')
44
+ acc.put_byte('B')
45
+ end
46
+
47
+ acc.put_byte((pkt.data_len >> 8) & 0xff)
48
+ acc.put_byte(pkt.data_len & 0xff)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,26 @@
1
+ require 'baykit/bayserver/protocol/command_handler'
2
+
3
+ module Baykit
4
+ module BayServer
5
+ module Docker
6
+ module Ajp
7
+ module AjpCommandHandler
8
+ include Baykit::BayServer::Protocol::CommandHandler # implements
9
+
10
+ # abstract method
11
+ #
12
+ # handle_data(cmd)
13
+ # handle_end_response(cmd)
14
+ # handle_forward_request(cmd)
15
+ # handle_send_body_chunk(cmd)
16
+ # handle_send_headers(cmd)
17
+ # handle_shutdown(cmd)
18
+ # handle_get_body_chunk(cmd)
19
+ # need_data()
20
+ #
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,66 @@
1
+ require 'baykit/bayserver/protocol/command_unpacker'
2
+ require 'baykit/bayserver/docker/ajp/ajp_type'
3
+ require 'baykit/bayserver/docker/ajp/command/package'
4
+
5
+ module Baykit
6
+ module BayServer
7
+ module Docker
8
+ module Ajp
9
+ class AjpCommandUnPacker <Baykit::BayServer::Protocol::CommandUnPacker
10
+
11
+ include Baykit::BayServer::Docker::Ajp::Command
12
+
13
+ attr :cmd_handler
14
+
15
+ def initialize(handler)
16
+ @cmd_handler = handler
17
+ reset
18
+ end
19
+
20
+ def reset()
21
+ end
22
+
23
+ def packet_received(pkt)
24
+
25
+ BayLog.debug("ajp: packet received: type=%d datalen=%d", pkt.type, pkt.data_len)
26
+
27
+ case pkt.type
28
+ when AjpType::DATA
29
+ cmd = CmdData.new
30
+
31
+ when AjpType::FORWARD_REQUEST
32
+ cmd = CmdForwardRequest.new
33
+
34
+ when AjpType::SEND_BODY_CHUNK
35
+ cmd = CmdSendBodyChunk.new(pkt.buf, pkt.header_len, pkt.data_len)
36
+
37
+ when AjpType::SEND_HEADERS
38
+ cmd = CmdSendHeaders.new
39
+
40
+ when AjpType::END_RESPONSE
41
+ cmd = CmdEndResponse.new
42
+
43
+ when AjpType::SHUTDOWN
44
+ cmd = CmdShutdown.new
45
+
46
+ when AjpType::GET_BODY_CHUNK
47
+ cmd = CmdGetBodyChunk.new
48
+
49
+ else
50
+ raise Sink.new()
51
+ end
52
+
53
+ cmd.unpack(pkt)
54
+ return cmd.handle(@cmd_handler) # visit
55
+ end
56
+
57
+ def need_data()
58
+ return @cmd_handler.need_data()
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+
@@ -0,0 +1,15 @@
1
+ module Baykit
2
+ module BayServer
3
+ module Docker
4
+ module Ajp
5
+ module AjpDocker
6
+ include Baykit::BayServer::Docker::Docker # implements
7
+
8
+ PROTO_NAME = "ajp"
9
+
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,292 @@
1
+ require 'baykit/bayserver/docker/base/inbound_handler'
2
+ require 'baykit/bayserver/tours/req_content_handler'
3
+
4
+ require 'baykit/bayserver/util/string_util'
5
+ require 'baykit/bayserver/util/http_util'
6
+ require 'baykit/bayserver/docker/ajp/ajp_protocol_handler'
7
+ require 'baykit/bayserver/docker/ajp/command/package'
8
+
9
+ module Baykit
10
+ module BayServer
11
+ module Docker
12
+ module Ajp
13
+ class AjpInboundHandler < Baykit::BayServer::Docker::Ajp::AjpProtocolHandler
14
+
15
+ class InboundProtocolHandlerFactory
16
+ include Baykit::BayServer::Protocol::ProtocolHandlerFactory # implements
17
+
18
+ def create_protocol_handler(pkt_store)
19
+ return AjpInboundHandler.new(pkt_store)
20
+ end
21
+ end
22
+
23
+ include Baykit::BayServer::Docker::Base::InboundHandler # implements
24
+ include Baykit::BayServer::Util
25
+ include Baykit::BayServer::Agent
26
+ include Baykit::BayServer::Protocol
27
+ include Baykit::BayServer::Tours
28
+ include Baykit::BayServer::Docker::Ajp::Command
29
+
30
+ STATE_READ_FORWARD_REQUEST = :FORWARD_REQUEST
31
+ STATE_READ_DATA = :READ_DATA
32
+
33
+ DUMMY_KEY = 1
34
+ attr :cur_tour_id
35
+ attr :req_command
36
+
37
+ attr :state
38
+ attr :keeping
39
+
40
+ def initialize(pkt_store)
41
+ super(pkt_store, true)
42
+ reset_state()
43
+ end
44
+
45
+ ######################################################
46
+ # implements Reusable
47
+ ######################################################
48
+ def reset()
49
+ super
50
+ reset_state()
51
+ @req_command = nil
52
+ @keeping = false
53
+ @cur_tour_id = 0
54
+ end
55
+
56
+ ######################################################
57
+ # implements InboundHandler
58
+ ######################################################
59
+ def send_res_headers(tur)
60
+ chunked = false
61
+ cmd = CmdSendHeaders.new()
62
+ tur.res.headers.names.each do |name|
63
+ tur.res.headers.values(name).each do |value|
64
+ cmd.add_header(name, value)
65
+ end
66
+ end
67
+ cmd.status = tur.res.headers.status
68
+ command_packer.post(@ship, cmd)
69
+
70
+ BayLog.debug("%s send header: content-length=%d", self, tur.res.headers.content_length())
71
+ end
72
+
73
+ def send_res_content(tur, bytes, ofs, len, &lis)
74
+ cmd = CmdSendBodyChunk.new(bytes, ofs, len);
75
+ @command_packer.post(ship, cmd, &lis);
76
+ end
77
+
78
+ def send_end_tour(tur, keep_alive, &callback)
79
+ BayLog.debug("%s endTour: tur=%s keep=%s", @ship, tur, keep_alive)
80
+ cmd = CmdEndResponse.new()
81
+ cmd.reuse = keep_alive
82
+
83
+ ensure_func = lambda do
84
+ if !keep_alive
85
+ command_packer.end(@ship)
86
+ end
87
+ end
88
+
89
+ begin
90
+ command_packer.post(@ship, cmd) do
91
+ BayLog.debug("%s call back in sendEndTour: tur=%s keep=%s", self, tur, keep_alive)
92
+ ensure_func.call()
93
+ callback.call()
94
+ end
95
+ rescue IOError => e
96
+ BayLog.debug("%s post failed in sendEndTour: tur=%s keep=%s", self, tur, keep_alive)
97
+ ensure_func.call()
98
+ raise e
99
+ end
100
+ end
101
+
102
+ def send_req_protocol_error(e)
103
+ tur = @ship.get_error_tour()
104
+ tur.res.send_error(Tour::TOUR_ID_NOCHECK, HttpStatus::BAD_REQUEST, e.message, e)
105
+ return true
106
+ end
107
+
108
+
109
+ ######################################################
110
+ # implements AjpCommandHandler
111
+ ######################################################
112
+ def handle_forward_request(cmd)
113
+ BayLog.debug("%s handleForwardRequest method=%s uri=%s", @ship, cmd.method, cmd.req_uri)
114
+ if @state != STATE_READ_FORWARD_REQUEST
115
+ raise ProtocolException.new("Invalid AJP command: #{cmd.type}")
116
+ end
117
+
118
+ @keeping = false
119
+ @req_command = cmd
120
+ tur = @ship.get_tour(DUMMY_KEY)
121
+ if tur == nil
122
+ BayLog.error(BayMessage.get(:INT_NO_MORE_TOURS))
123
+ tur = @ship.get_tour(AjpInboundHandler::DUMMY_KEY, true)
124
+ tur.res.send_error(Tour::TOUR_ID_NOCHECK, HttpStatus::SERVICE_UNAVAILABLE, "No available tours")
125
+ tur.res.end_content(Tour::TOUR_ID_NOCHECK)
126
+ @ship.agent.shutdown(false)
127
+ return NextSocketAction::CONTINUE
128
+ end
129
+
130
+ @cur_tour_id = tur.id
131
+ tur.req.uri = cmd.req_uri
132
+ tur.req.protocol = cmd.protocol
133
+ tur.req.method = cmd.method
134
+ cmd.headers.copy_to(tur.req.headers)
135
+ query_string = cmd.attributes["?query_string"]
136
+
137
+ if StringUtil.set?(query_string)
138
+ tur.req.uri += "?" + query_string
139
+ end
140
+
141
+ BayLog.debug("%s read header method=%s protocol=%s uri=%s contlen=%d",
142
+ tur, tur.req.method, tur.req.protocol, tur.req.uri, tur.req.headers.content_length)
143
+
144
+ if BayServer.harbor.trace_header?
145
+ cmd.headers.names.each do |name|
146
+ cmd.headers.values(name).each do |value|
147
+ BayLog.info("%s header: %s=%s", tur, name, value)
148
+ end
149
+ end
150
+ end
151
+
152
+ req_cont_len = cmd.headers.content_length
153
+ if req_cont_len > 0
154
+ sid = @ship.ship_id
155
+ tur.req.set_consume_listener(req_cont_len) do |len, resume|
156
+ if resume
157
+ @ship.resume(sid)
158
+ end
159
+ end
160
+ end
161
+
162
+ begin
163
+ start_tour(tur)
164
+
165
+ if req_cont_len <= 0
166
+ end_req_content(tur)
167
+ else
168
+ change_state(STATE_READ_DATA)
169
+ end
170
+
171
+ return NextSocketAction::CONTINUE
172
+ rescue HttpException => e
173
+ if req_cont_len <= 0
174
+ tur.res.send_http_exception(Tour::TOUR_ID_NOCHECK, e)
175
+ reset_state()
176
+ return NextSocketAction::WRITE
177
+ else
178
+ # Delay send
179
+ change_state(STATE_READ_DATA)
180
+ tur.error = e
181
+ tur.req.set_content_handler(ReqContentHandler::DEV_NULL)
182
+ return NextSocketAction::CONTINUE
183
+ end
184
+ end
185
+ end
186
+
187
+ def handle_data(cmd)
188
+ BayLog.debug("%s handleData len=%s", @ship, cmd.length)
189
+
190
+ if @state != STATE_READ_DATA
191
+ raise RuntimeError.new("Invalid AJP command: #{cmd.type} state=#{@state}")
192
+ end
193
+
194
+ tur = @ship.get_tour(DUMMY_KEY)
195
+ success = tur.req.post_content(Tour::TOUR_ID_NOCHECK, cmd.data, cmd.start, cmd.length)
196
+
197
+ if tur.req.bytes_posted == tur.req.bytes_limit
198
+ # request content completed
199
+
200
+ if tur.error != nil
201
+ tur.res.send_http_exception(Tour::TOUR_ID_NOCHECK, tur.error)
202
+ reset_state()
203
+ return NextSocketAction::WRITE
204
+ else
205
+ begin
206
+ end_req_content(tur)
207
+ return NextSocketAction::CONTINUE
208
+ rescue HttpException => e
209
+ tur.res.send_http_exception(Tour::TOUR_ID_NOCHECK, e)
210
+ reset_state()
211
+ return NextSocketAction::WRITE
212
+ end
213
+ end
214
+ else
215
+ bch = CmdGetBodyChunk.new()
216
+ bch.req_len = tur.req.bytes_limit - tur.req.bytes_posted
217
+ if bch.req_len > AjpPacket::MAX_DATA_LEN
218
+ bch.req_len = AjpPacket::MAX_DATA_LEN
219
+ end
220
+ command_packer.post(@ship, bch)
221
+
222
+ if !success
223
+ return NextSocketAction::SUSPEND
224
+ else
225
+ return NextSocketAction::CONTINUE
226
+ end
227
+ end
228
+ end
229
+
230
+ def handle_send_body_chunk(cmd)
231
+ raise RuntimeError.new "Invalid AJP command: #{cmd.type}"
232
+ end
233
+
234
+ def handle_send_headers(cmd)
235
+ raise RuntimeError.new "Invalid AJP command: #{cmd.type}"
236
+ end
237
+
238
+ def handle_shutdown(cmd)
239
+ BayLog.info("%s handle_shutdown", @ship)
240
+ BayServer.shutdown
241
+ NextSocketAction::CLOSE
242
+ end
243
+
244
+ def handle_end_response(cmd)
245
+ raise RuntimeError.new "Invalid AJP command: #{cmd.type}"
246
+ end
247
+
248
+ def handle_get_body_chunk(cmd)
249
+ raise RuntimeError.new "Invalid AJP command: #{cmd.type}"
250
+ end
251
+
252
+ def need_data()
253
+ return @state == STATE_READ_DATA
254
+ end
255
+
256
+ private
257
+ def reset_state
258
+ change_state(STATE_READ_FORWARD_REQUEST)
259
+ end
260
+
261
+ def change_state(new_state)
262
+ @state = new_state
263
+ end
264
+
265
+ def end_req_content(tur)
266
+ tur.req.end_content(Tour::TOUR_ID_NOCHECK)
267
+ reset_state()
268
+ end
269
+
270
+ def start_tour(tur)
271
+ HttpUtil.parse_host_port(tur, @req_command.is_ssl ? 443 : 80)
272
+ HttpUtil.parse_authorization(tur)
273
+
274
+ skt = @ship.socket
275
+ tur.req.remote_port = nil
276
+ tur.req.remote_address = @req_command.remote_addr
277
+ tur.req.remote_host_func = lambda { @req_command.remote_host }
278
+
279
+ tur.req.server_address = skt.local_address.ip_address
280
+ tur.req.server_port = @req_command.server_port
281
+ tur.req.server_name = @req_command.server_name
282
+ tur.is_secure = @req_command.is_ssl
283
+
284
+ tur.go()
285
+ end
286
+
287
+
288
+ end
289
+ end
290
+ end
291
+ end
292
+ end
@@ -0,0 +1,112 @@
1
+ require 'baykit/bayserver/protocol/packet_part_accessor'
2
+ require 'baykit/bayserver/util/string_util'
3
+ require 'baykit/bayserver/protocol/packet'
4
+
5
+ #
6
+ # AJP Protocol
7
+ # https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
8
+ #
9
+ # AJP packet spec
10
+ #
11
+ # packet: preamble, length, body
12
+ # preamble:
13
+ # 0x12, 0x34 (client->server)
14
+ # | 'A', 'B' (server->client)
15
+ # length:
16
+ # 2 byte
17
+ # body:
18
+ # $length byte
19
+ #
20
+ #
21
+ # Body format
22
+ # client->server
23
+ # Code Type of Packet Meaning
24
+ # 2 Forward Request Begin the request-processing cycle with the following data
25
+ # 7 Shutdown The web server asks the container to shut itself down.
26
+ # 8 Ping The web server asks the container to take control (secure login phase).
27
+ # 10 CPing The web server asks the container to respond quickly with a CPong.
28
+ # none Data Size (2 bytes) and corresponding body data.
29
+ #
30
+ # server->client
31
+ # Code Type of Packet Meaning
32
+ # 3 Send Body Chunk Send a chunk of the body from the servlet container to the web server (and presumably, onto the browser).
33
+ # 4 Send Headers Send the response headers from the servlet container to the web server (and presumably, onto the browser).
34
+ # 5 End Response Marks the end of the response (and thus the request-handling cycle).
35
+ # 6 Get Body Chunk Get further data from the request if it hasn't all been transferred yet.
36
+ # 9 CPong Reply The reply to a CPing request
37
+ #
38
+ #
39
+
40
+ module Baykit
41
+ module BayServer
42
+ module Docker
43
+ module Ajp
44
+ class AjpPacket < Baykit::BayServer::Protocol::Packet
45
+ class AjpAccessor < Baykit::BayServer::Protocol::PacketPartAccessor
46
+ include Baykit::BayServer::Util
47
+
48
+ def initialize(type, start, max_len)
49
+ super
50
+ end
51
+
52
+ def put_string(str)
53
+ if StringUtil.empty?(str)
54
+ put_short(0xffff)
55
+ else
56
+ put_short(str.length)
57
+ super str
58
+ put_byte(0) # null terminator
59
+ end
60
+ end
61
+
62
+ def get_string
63
+ get_string_by_len(get_short())
64
+ end
65
+
66
+ def get_string_by_len(len)
67
+
68
+ if len == 0xffff
69
+ return ""
70
+ end
71
+
72
+ buf = StringUtil.alloc(len)
73
+ get_bytes(buf, 0, len)
74
+ get_byte() # null terminator
75
+
76
+ buf
77
+ end
78
+ end
79
+
80
+ PREAMBLE_SIZE = 4
81
+ MAX_DATA_LEN = 8192 - PREAMBLE_SIZE
82
+ MIN_BUF_SIZE = 1024
83
+
84
+ attr_accessor :to_server
85
+
86
+ def initialize(type)
87
+ super(type, PREAMBLE_SIZE, MAX_DATA_LEN)
88
+ end
89
+
90
+ def reset()
91
+ @to_server = false
92
+ super
93
+ end
94
+
95
+ def new_ajp_header_accessor
96
+ AjpAccessor.new(self, 0, PREAMBLE_SIZE)
97
+ end
98
+
99
+ def new_ajp_data_accessor
100
+ AjpAccessor.new(self, PREAMBLE_SIZE, -1)
101
+ end
102
+
103
+ def to_s
104
+ "AjpPacket(#{@type})"
105
+ end
106
+
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+
@@ -0,0 +1,18 @@
1
+ require 'baykit/bayserver/protocol/packet_factory'
2
+
3
+ module Baykit
4
+ module BayServer
5
+ module Docker
6
+ module Ajp
7
+ class AjpPacketFactory < Baykit::BayServer::Protocol::PacketFactory
8
+
9
+ def create_packet(type)
10
+ AjpPacket.new(type)
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+