Source code: org/apache/ajp/tomcat33/Ajp14Interceptor.java
1 /*
2 * Copyright 1999-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.apache.ajp.tomcat33;
18
19 import java.io.IOException;
20 import java.net.InetAddress;
21 import java.net.Socket;
22
23 import org.apache.ajp.Ajp13;
24 import org.apache.ajp.NegociationHandler;
25 import org.apache.ajp.RequestHandler;
26 import org.apache.tomcat.core.Context;
27 import org.apache.tomcat.core.ContextManager;
28 import org.apache.tomcat.core.Request;
29 import org.apache.tomcat.core.Response;
30 import org.apache.tomcat.core.TomcatException;
31 import org.apache.tomcat.modules.server.PoolTcpConnector;
32 import org.apache.tomcat.util.buf.UDecoder;
33 import org.apache.tomcat.util.http.BaseRequest;
34 import org.apache.tomcat.util.http.Cookies;
35 import org.apache.tomcat.util.http.HttpMessages;
36 import org.apache.tomcat.util.net.TcpConnection;
37 import org.apache.tomcat.util.net.TcpConnectionHandler;
38
39 /** Note. PoolTcpConnector is a convenience base class used for
40 TCP-based connectors in tomcat33. It allows all those modules
41 to share the thread pool and listener code.
42
43 In future it's likely other optimizations will be implemented in
44 the PoolTcpConnector, so it's better to use it if you don't have
45 a good reason not to ( like a connector for J2ME, where you want
46 minimal footprint and don't care about high load )
47 */
48
49 /** Tomcat 33 module implementing the Ajp14 protocol.
50 *
51 * The actual protocol implementation is in Ajp14.java, this is just an
52 * adapter to plug it into tomcat.
53 */
54 public class Ajp14Interceptor extends PoolTcpConnector
55 implements TcpConnectionHandler
56 {
57 int ajp14_note=-1;
58 String password;
59 RequestHandler reqHandler=new RequestHandler();
60 NegociationHandler negHandler=new NegociationHandler();
61
62 public Ajp14Interceptor()
63 {
64 super();
65 super.setSoLinger( 100 );
66 super.setTcpNoDelay( true );
67 }
68
69 // initialization
70 public void engineInit(ContextManager cm) throws TomcatException {
71 log("engineInit");
72 }
73
74 public void engineStart(ContextManager cm) throws TomcatException {
75 super.engineInit( cm );
76 ajp14_note=cm.getNoteId( ContextManager.REQUEST_NOTE, "ajp14" );
77 super.engineStart(cm);
78 }
79
80
81 // -------------------- Ajp14 specific parameters --------------------
82
83 public void setPassword( String s ) {
84 this.password=s;
85 }
86
87 /**
88 * Set the original entropy seed
89 */
90 public void setSeed(String pseed)
91 {
92 negHandler.setSeed( pseed );
93 }
94
95 // -------------------- PoolTcpConnector --------------------
96
97 /** Called by PoolTcpConnector to allow childs to init.
98 */
99 protected void localInit() throws Exception {
100 ep.setConnectionHandler( this );
101 }
102
103 // -------------------- Handler implementation --------------------
104
105 /* The TcpConnectionHandler interface is used by the PoolTcpConnector to
106 * handle incoming connections.
107 */
108
109 /** Called by the thread pool when a new thread is added to the pool,
110 in order to create the (expensive) objects that will be stored
111 as thread data.
112 XXX we should use a single object, not array ( several reasons ),
113 XXX Ajp14 should be storead as a request note, to be available in
114 all modules
115 */
116 public Object[] init()
117 {
118 if( debug > 0 ) log("Init ");
119 Object thData[]=new Object[1];
120 thData[0]=initRequest( null );
121 return thData;
122 }
123
124 /** Construct the request object, with probably unnecesary
125 sanity tests ( should work without thread pool - but that is
126 not supported in PoolTcpConnector, maybe in future )
127 */
128 private Ajp14Request initRequest(Object thData[] ) {
129 if( ajp14_note < 0 ) throw new RuntimeException( "assert: ajp14_note>0" );
130 Ajp14Request req=null;
131 if( thData != null ) {
132 req=(Ajp14Request)thData[0];
133 }
134 if( req != null ) {
135 Response res=req.getResponse();
136 req.recycle();
137 res.recycle();
138 // make the note available to other modules
139 req.setNote( ajp14_note, req.ajp13);
140 return req;
141 }
142 // either thData==null or broken ( req==null)
143 Ajp13 ajp13=new Ajp13(reqHandler);
144 negHandler.init( ajp13 );
145
146 negHandler.setContainerSignature( ContextManager.TOMCAT_NAME +
147 " v" + ContextManager.TOMCAT_VERSION);
148 if( password!= null ) {
149 negHandler.setPassword( password );
150 ajp13.setBackward(false);
151 }
152
153 BaseRequest ajpreq=new BaseRequest();
154
155 req=new Ajp14Request(ajp13, ajpreq);
156 Ajp14Response res=new Ajp14Response(ajp13);
157 cm.initRequest(req, res);
158 return req;
159 }
160
161 /** Called whenever a new TCP connection is received. The connection
162 is reused.
163 */
164 public void processConnection(TcpConnection connection, Object thData[])
165 {
166 try {
167 if( debug>0)
168 log( "Received ajp14 connection ");
169 Socket socket = connection.getSocket();
170 // assert: socket!=null, connection!=null ( checked by PoolTcpEndpoint )
171
172 socket.setSoLinger( true, 100);
173
174 Ajp14Request req=initRequest( thData );
175 Ajp14Response res= (Ajp14Response)req.getResponse();
176 Ajp13 ajp13=req.ajp13;
177 BaseRequest ajpReq=req.ajpReq;
178
179 ajp13.setSocket(socket);
180
181 // first request should be the loginit.
182 int status=ajp13.receiveNextRequest( ajpReq );
183 if( status != 304 ) { // XXX use better codes
184 log( "Failure in logInit ");
185 return;
186 }
187
188 status=ajp13.receiveNextRequest( ajpReq );
189 if( status != 304 ) { // XXX use better codes
190 log( "Failure in login ");
191 return;
192 }
193
194 boolean moreRequests = true;
195 while(moreRequests) {
196 status=ajp13.receiveNextRequest( ajpReq );
197
198 if( status==-2) {
199 // special case - shutdown
200 // XXX need better communication, refactor it
201 if( !doShutdown(socket.getLocalAddress(),
202 socket.getInetAddress())) {
203 moreRequests = false;
204 continue;
205 }
206 }
207
208 // 999 low level requests are just ignored (ie cping/cpong)
209 if( status == 200)
210 cm.service(req, res);
211 else if (status == 500) {
212 log( "Invalid request received " + req );
213 break;
214 }
215
216 req.recycle();
217 res.recycle();
218 }
219 if( debug > 0 ) log("Closing ajp14 connection");
220 ajp13.close();
221 socket.close();
222 } catch (Exception e) {
223 log("Processing connection " + connection, e);
224 }
225 }
226
227 // We don't need to check isSameAddress if we authenticate !!!
228 protected boolean doShutdown(InetAddress serverAddr,
229 InetAddress clientAddr)
230 {
231 try {
232 // close the socket connection before handling any signal
233 // but get the addresses first so they are not corrupted
234 if(isSameAddress(serverAddr, clientAddr)) {
235 cm.stop();
236 // same behavior as in past, because it seems that
237 // stopping everything doesn't work - need to figure
238 // out what happens with the threads ( XXX )
239
240 // XXX It should work now - but will fail if servlets create
241 // threads
242 System.exit(0);
243 }
244 } catch(Exception ignored) {
245 log("Ignored " + ignored);
246 }
247 log("Shutdown command ignored");
248 return false;
249 }
250
251 // legacy, should be removed
252 public void setServer(Object contextM)
253 {
254 this.cm=(ContextManager)contextM;
255 }
256
257 public Object getInfo( Context ctx, Request request,
258 int id, String key ) {
259 if( ! ( request instanceof Ajp14Request ) ) {
260 return null;
261 }
262 Ajp14Request ajp14req = (Ajp14Request)request;
263 return ajp14req.ajpReq.getAttribute( key );
264 }
265 public int setInfo( Context ctx, Request request,
266 int id, String key, Object obj ) {
267 if( ! ( request instanceof Ajp14Request ) ) {
268 return DECLINED;
269 }
270 Ajp14Request ajp14req = (Ajp14Request)request;
271 ajp14req.ajpReq.setAttribute(key, obj);
272 return OK;
273 }
274
275
276
277 }
278
279
280 //-------------------- Glue code for request/response.
281 // Probably not needed ( or can be simplified ), but it's
282 // not that bad.
283
284 class Ajp14Request extends Request
285 {
286 Ajp13 ajp13;
287 BaseRequest ajpReq;
288
289 public Ajp14Request(Ajp13 ajp13, BaseRequest ajpReq)
290 {
291 headers = ajpReq.headers();
292 methodMB=ajpReq.method();
293 protoMB=ajpReq.protocol();
294 uriMB = ajpReq.requestURI();
295 queryMB = ajpReq.queryString();
296 remoteAddrMB = ajpReq.remoteAddr();
297 remoteHostMB = ajpReq.remoteHost();
298 serverNameMB = ajpReq.serverName();
299
300 // XXX sync cookies
301 scookies = new Cookies( headers );
302 urlDecoder=new UDecoder();
303
304 // XXX sync headers
305
306 params.setQuery( queryMB );
307 params.setURLDecoder( urlDecoder );
308 params.setHeaders( headers );
309 initRequest();
310
311 this.ajp13=ajp13;
312 this.ajpReq=ajpReq;
313 }
314
315 // -------------------- Wrappers for changed method names, and to use the buffers
316 // XXX Move BaseRequest into util !!! ( it's just a stuct with some MessageBytes )
317
318 public int getServerPort() {
319 return ajpReq.getServerPort();
320 }
321
322 public void setServerPort(int i ) {
323 ajpReq.setServerPort( i );
324 }
325
326 public void setRemoteUser( String s ) {
327 super.setRemoteUser(s);
328 ajpReq.remoteUser().setString(s);
329 }
330
331 public String getRemoteUser() {
332 String s=ajpReq.remoteUser().toString();
333 if( s == null )
334 s=super.getRemoteUser();
335 return s;
336 }
337
338 public String getAuthType() {
339 return ajpReq.authType().toString();
340 }
341
342 public void setAuthType(String s ) {
343 ajpReq.authType().setString(s);
344 }
345
346 public String getJvmRoute() {
347 return ajpReq.jvmRoute().toString();
348 }
349
350 public void setJvmRoute(String s ) {
351 ajpReq.jvmRoute().setString(s);
352 }
353
354 // XXX scheme
355
356 public boolean isSecure() {
357 return ajpReq.getSecure();
358 }
359
360 public int getContentLength() {
361 int i=ajpReq.getContentLength();
362 if( i >= 0 ) return i;
363 i= super.getContentLength();
364 return i;
365 }
366
367 public void setContentLength( int i ) {
368 super.setContentLength(i); // XXX sync
369 }
370
371 // XXX broken
372 // public Iterator getAttributeNames() {
373 // return attributes.keySet().iterator();
374 // }
375
376
377 // --------------------
378
379 public void recycle() {
380 super.recycle();
381 ajpReq.recycle();
382 if( ajp13!=null) ajp13.recycle();
383 }
384
385 public String dumpRequest() {
386 return ajpReq.toString();
387 }
388
389 // --------------------
390
391 // XXX This should go away if we introduce an InputBuffer.
392 // We almost have it as result of encoding fixes, but for now
393 // just keep this here, doesn't hurt too much.
394 public int doRead() throws IOException
395 {
396 if( available <= 0 )
397 return -1;
398 available--;
399 return ajp13.reqHandler.doRead(ajp13);
400 }
401
402 public int doRead(byte[] b, int off, int len) throws IOException
403 {
404 if( available <= 0 )
405 return -1;
406 int rd=ajp13.reqHandler.doRead( ajp13, b,off, len );
407 available -= rd;
408 return rd;
409 }
410
411 }
412
413 class Ajp14Response extends Response
414 {
415 Ajp13 ajp13;
416 boolean finished=false;
417
418 public Ajp14Response(Ajp13 ajp13)
419 {
420 super();
421 this.ajp13=ajp13;
422 }
423
424 public void recycle() {
425 super.recycle();
426 finished=false;
427 }
428
429 // XXX if more headers that MAX_SIZE, send 2 packets!
430 // XXX Can be implemented using module notification, no need to extend
431 public void endHeaders() throws IOException
432 {
433 super.endHeaders();
434
435 if (request.protocol().isNull()) {
436 return;
437 }
438
439 ajp13.reqHandler.sendHeaders(ajp13, ajp13.outBuf, getStatus(),
440 HttpMessages.getMessage(status),
441 getMimeHeaders());
442 }
443
444 // XXX Can be implemented using module notification, no need to extend
445 public void finish() throws IOException
446 {
447 if(!finished) {
448 super.finish();
449 finished = true; // Avoid END_OF_RESPONSE sent 2 times
450 ajp13.reqHandler.finish(ajp13, ajp13.outBuf);
451 }
452 }
453
454 // XXX Can be implemented using the buffers, no need to extend
455 public void doWrite( byte b[], int off, int len) throws IOException
456 {
457 ajp13.reqHandler.doWrite(ajp13, ajp13.outBuf, b, off, len );
458 }
459
460 }