bayserver-docker-ajp 2.2.0

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.
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
+