Source code: org/apache/ajp/tomcat4/Ajp13Processor.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.tomcat4;
18
19
20 import java.io.IOException;
21 import java.net.Socket;
22
23 import javax.servlet.ServletException;
24 import javax.servlet.http.HttpServletResponse;
25
26 import org.apache.ajp.Ajp13;
27 import org.apache.catalina.Lifecycle;
28 import org.apache.catalina.LifecycleException;
29 import org.apache.catalina.LifecycleListener;
30 import org.apache.catalina.util.LifecycleSupport;
31 import org.apache.catalina.util.StringManager;
32 import org.apache.tomcat.util.http.BaseRequest;
33
34 /**
35 * @author Kevin Seguin
36 * @version $Revision: 299218 $ $Date: 2004-02-24 03:48:44 -0500 (Tue, 24 Feb 2004) $
37 */
38
39 final class Ajp13Processor
40 implements Lifecycle, Runnable {
41
42 /**
43 * A simple class to provide synchronized access
44 * to a boolean.
45 */
46 private class Bool {
47
48 private boolean b = false;
49
50 Bool() {
51 }
52
53 Bool(boolean b) {
54 this.b = b;
55 }
56
57 synchronized boolean value() {
58 return b;
59 }
60
61 synchronized void set(boolean b) {
62 this.b = b;
63 }
64 }
65
66 // ----------------------------------------------------------- Constructors
67
68
69 /**
70 * Construct a new Ajp13Processor associated with the specified connector.
71 *
72 * @param connector Ajp13Connector that owns this processor
73 * @param id Identifier of this Ajp13Processor (unique per connector)
74 * @param threadGroup The thread group any threads created by the processor
75 * should be in.
76 */
77 public Ajp13Processor(Ajp13Connector connector,
78 int id,
79 ThreadGroup threadGroup) {
80
81 super();
82 this.connector = connector;
83 this.debug = connector.getDebug();
84 this.id = id;
85 this.request = (Ajp13Request) connector.createRequest();
86 this.request.setConnector(connector);
87 this.request.setConnector(connector);
88 this.response = (Ajp13Response) connector.createResponse();
89 this.response.setConnector(connector);
90 this.threadName =
91 "Ajp13Processor[" + connector.getPort() + "][" + id + "]";
92 this.threadGroup = threadGroup;
93
94 this.logger.setConnector(connector);
95 this.logger.setName(this.threadName);
96 }
97
98
99 // ----------------------------------------------------- Instance Variables
100
101 private Ajp13Logger logger = new Ajp13Logger();
102 private BaseRequest ajpRequest = new BaseRequest();
103
104 /**
105 * Is there a new socket available?
106 */
107 private boolean available = false;
108
109
110 /**
111 * The Ajp13Connector with which this processor is associated.
112 */
113 private Ajp13Connector connector = null;
114
115
116 /**
117 * The debugging detail level for this component.
118 */
119 private int debug = 0;
120
121
122 /**
123 * The identifier of this processor, unique per connector.
124 */
125 private int id = 0;
126
127
128 /**
129 * The lifecycle event support for this component.
130 */
131 private LifecycleSupport lifecycle = new LifecycleSupport(this);
132
133
134 /**
135 * The AJP13 request object we will pass to our associated container.
136 */
137 private Ajp13Request request = null;
138
139
140 /**
141 * The AJP13 response object we will pass to our associated container.
142 */
143 private Ajp13Response response = null;
144
145
146 /**
147 * The string manager for this package.
148 */
149 protected StringManager sm =
150 StringManager.getManager(Constants.PACKAGE);
151
152
153 /**
154 * The socket we are currently processing a request for. This object
155 * is used for inter-thread communication only.
156 */
157 private Socket socket = null;
158
159
160 /**
161 * Has this component been started yet?
162 */
163 private boolean started = false;
164
165
166 /**
167 * The shutdown signal to our background thread
168 */
169 private Bool stopped = new Bool(true);
170
171 /**
172 * Are we currently handling a request?
173 */
174 private Bool handlingRequest = new Bool(false);
175
176
177 /**
178 * The background thread.
179 */
180 private Thread thread = null;
181
182
183 /**
184 * The name to register for the background thread.
185 */
186 private String threadName = null;
187
188
189 /**
190 * This processor's thread group.
191 */
192 private ThreadGroup threadGroup = null;
193
194
195 /**
196 * The thread synchronization object.
197 */
198 private Object threadSync = new Object();
199
200
201
202 // -------------------------------------------------------- Package Methods
203
204
205 /**
206 * Process an incoming TCP/IP connection on the specified socket. Any
207 * exception that occurs during processing must be logged and swallowed.
208 * <b>NOTE</b>: This method is called from our Connector's thread. We
209 * must assign it to our own thread so that multiple simultaneous
210 * requests can be handled.
211 *
212 * @param socket TCP socket to process
213 */
214 synchronized void assign(Socket socket) {
215
216 // Wait for the Processor to get the previous Socket
217 while (available) {
218 try {
219 wait();
220 } catch (InterruptedException e) {
221 }
222 }
223
224 // Store the newly available Socket and notify our thread
225 this.socket = socket;
226 available = true;
227 notifyAll();
228
229 if ((debug > 0) && (socket != null))
230 logger.log(" An incoming request is being assigned");
231
232 }
233
234
235 // -------------------------------------------------------- Private Methods
236
237
238 /**
239 * Await a newly assigned Socket from our Connector, or <code>null</code>
240 * if we are supposed to shut down.
241 */
242 private synchronized Socket await() {
243
244 // Wait for the Connector to provide a new Socket
245 while (!available) {
246 try {
247 wait();
248 } catch (InterruptedException e) {
249 }
250 }
251
252 // Notify the Connector that we have received this Socket
253 Socket socket = this.socket;
254 available = false;
255 notifyAll();
256
257 if ((debug > 0) && (socket != null))
258 logger.log(" The incoming request has been awaited");
259
260 return (socket);
261
262 }
263
264 /**
265 * Parse and record the connection parameters related to this request.
266 *
267 * @param socket The socket on which we are connected
268 *
269 * @exception IOException if an input/output error occurs
270 * @exception ServletException if a parsing error occurs
271 */
272 private void parseConnection(Socket socket)
273 throws IOException, ServletException {
274
275 if (debug > 1)
276 logger.log(" parseConnection: address=" + socket.getInetAddress() +
277 ", port=" + connector.getPort());
278 request.setServerPort(connector.getPort());
279 request.setSocket(socket);
280
281 }
282
283 /**
284 * Process an incoming AJP13 request on the Socket that has been assigned
285 * to this Processor. Any exceptions that occur during processing must be
286 * swallowed and dealt with.
287 *
288 * @param socket The socket on which we are connected to the client
289 */
290 private void process(Socket socket) {
291
292 Ajp13 ajp13 = new Ajp13();
293 ajp13.setDebug(debug);
294 ajp13.setLogger(new org.apache.ajp.Logger() {
295 public void log(String msg) {
296 logger.log("[Ajp13] " + msg);
297 }
298
299 public void log(String msg, Throwable t) {
300 logger.log("[Ajp13] " + msg, t);
301 }
302 });
303
304 Ajp13InputStream input = new Ajp13InputStream(ajp13);
305 Ajp13OutputStream output = new Ajp13OutputStream(ajp13);
306 response.setAjp13(ajp13);
307
308 try {
309 ajp13.setSocket(socket);
310 } catch (IOException e) {
311 logger.log("process: ajp13.setSocket", e);
312 }
313
314 boolean moreRequests = true;
315 String expectedSecret=connector.getSecret();
316
317 boolean needAuth= ( expectedSecret != null );
318
319 while (moreRequests && !stopped.value()) {
320
321 int status = 0;
322 try {
323 if (debug > 0) {
324 logger.log("waiting on next request...");
325 }
326
327 status = ajp13.receiveNextRequest(ajpRequest);
328
329 if (debug > 0) {
330 logger.log("received next request, status=" + status);
331 }
332 } catch (IOException e) {
333 logger.log("process: ajp13.receiveNextRequest", e);
334 }
335
336 if( needAuth ) {
337 String connSecret=ajp13.getSecret();
338 if( connSecret == null ) {
339 logger.log( "Connection without password, " +
340 "tomcat is configured to require one" );
341 break;
342 }
343 if( ! connSecret.equals(expectedSecret) ) {
344 logger.log( "Connection with wrong password" );
345 break;
346 }
347
348 needAuth=false;
349 }
350
351 if (stopped.value()) {
352 if (debug > 0) {
353 logger.log("process: received request, but we're stopped");
354 }
355 break;
356 }
357
358 if( status==-2) {
359 // special case - shutdown
360 // XXX need better communication, refactor it
361 // if( !doShutdown(socket.getLocalAddress(),
362 // socket.getInetAddress())) {
363 // moreRequests = false;
364 // continue;
365 // }
366 break;
367 }
368
369 // Allready handled by low level proto, don't go farther
370 if( status == 999 )
371 {
372 ajpRequest.recycle();
373 request.recycle();
374
375 // recycle ajp13 object
376 ajp13.recycle();
377
378 continue;
379 }
380
381 if( status != 200 )
382 break;
383
384 try {
385 // set flag
386 handlingRequest.set(true);
387
388 boolean bad_request = false;
389
390 // set up request
391 try {
392 request.setAjpRequest(ajpRequest);
393 } catch (IllegalArgumentException e) {
394 bad_request = true;
395 }
396 request.setResponse(response);
397 request.setStream(input);
398
399 // setup response
400 response.setRequest(request);
401 response.setStream(output);
402
403 if (debug > 0) {
404 logger.log("invoking...");
405 }
406
407 if (!bad_request) {
408 try {
409 connector.getContainer().invoke(request, response);
410 } catch (IOException ioe) {
411 // Pass the IOException through
412 throw ioe;
413 } catch (Throwable e) {
414 // A throwable here could be caused by a Valve,
415 // Filter, or other component in the chain.
416 // Processing of the request failed, return an
417 // Internal Server Error
418 logger.log("process: invoke", e);
419 response.sendError
420 (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
421 }
422 } else {
423 response.sendError
424 (HttpServletResponse.SC_BAD_REQUEST);
425 }
426
427 if (debug > 0) {
428 logger.log("done invoking, finishing request/response....");
429 }
430
431 response.finishResponse();
432 request.finishRequest();
433
434 if (debug > 0) {
435 logger.log("finished handling request.");
436 }
437
438 } catch (IOException ioe) {
439 // Normally this catches a socket Broken Pipe caused by the
440 // remote client aborting the request. Don't print the stack
441 // trace in this case. Then let the Processor recycle.
442 logger.log("process: IOException " + ioe.getMessage());
443 moreRequests = false;
444 } catch (Throwable e) {
445 // Processing the request and sending the response failed.
446 // We don't know what the state of the Ajp Connector socket
447 // is in. Bail out and recycle the Processor.
448 logger.log("process: finish", e);
449 moreRequests = false;
450 }
451
452 // Recycling the request and the response objects
453 if (debug > 0) {
454 logger.log("recyling objects ...");
455 }
456
457 ajpRequest.recycle();
458 request.recycle();
459 response.recycle();
460
461 // recycle ajp13 object
462 ajp13.recycle();
463
464 // reset flag
465 handlingRequest.set(false);
466 }
467
468 try {
469 if (debug > 0) {
470 logger.log("closing ajp13 object...");
471 }
472
473 ajp13.close();
474
475 if (debug > 0) {
476 logger.log("ajp13 object closed.");
477 }
478 } catch (IOException e) {
479 logger.log("process: ajp13.close", e);
480 }
481
482 try {
483 if (debug > 0) {
484 logger.log("closing socket...");
485 }
486
487 socket.close();
488
489 if (debug > 0) {
490 logger.log("socket closed.");
491 }
492 } catch (IOException e) {
493 logger.log("process: socket.close", e);
494 }
495 socket = null;
496
497 if (debug > 0) {
498 logger.log("process: done");
499 }
500 }
501
502
503 // ---------------------------------------------- Background Thread Methods
504
505
506 /**
507 * The background thread that listens for incoming TCP/IP connections and
508 * hands them off to an appropriate processor.
509 */
510 public void run() {
511
512 // Process requests until we receive a shutdown signal
513 while (!stopped.value()) {
514
515 // Wait for the next socket to be assigned
516 if (debug > 0) {
517 logger.log("waiting for next socket to be assigned...");
518 }
519 Socket socket = await();
520 if (socket == null)
521 continue;
522
523 if (debug > 0) {
524 logger.log("socket assigned.");
525 }
526
527 // Process the request from this socket
528 process(socket);
529
530 // Finish up this request
531 if (debug > 0) {
532 logger.log("recycling myself ...");
533 }
534 connector.recycle(this);
535 }
536
537 // Tell threadStop() we have shut ourselves down successfully
538 synchronized (threadSync) {
539 threadSync.notifyAll();
540 }
541
542 }
543
544
545 /**
546 * Start the background processing thread.
547 */
548 private void threadStart() {
549
550 logger.log(sm.getString("ajp13Processor.starting"));
551
552 stopped.set(false);
553 thread = new Thread(threadGroup, this, threadName);
554 thread.setDaemon(true);
555 thread.start();
556
557 if (debug > 0)
558 logger.log(" Background thread has been started");
559
560 }
561
562
563 /**
564 * Stop the background processing thread.
565 */
566 private void threadStop() {
567
568 logger.log(sm.getString("ajp13Processor.stopping"));
569
570 stopped.set(true);
571 assign(null);
572 synchronized (threadSync) {
573 try {
574 if (handlingRequest.value()) {
575 if (debug > 0) {
576 logger.log
577 ("currentling handling a request, so waiting....");
578 }
579 threadSync.wait(5000);
580 } else {
581 if (debug > 0) {
582 logger.log
583 ("not currently handling a request, not waiting.");
584 }
585 }
586 } catch (InterruptedException e) {
587 ;
588 }
589 }
590 thread = null;
591
592 }
593
594
595 // ------------------------------------------------------ Lifecycle Methods
596
597
598 /**
599 * Add a lifecycle event listener to this component.
600 *
601 * @param listener The listener to add
602 */
603 public void addLifecycleListener(LifecycleListener listener) {
604
605 lifecycle.addLifecycleListener(listener);
606
607 }
608
609 /**
610 * Get the lifecycle listeners associated with this lifecycle. If this
611 * Lifecycle has no listeners registered, a zero-length array is returned.
612 */
613 public LifecycleListener[] findLifecycleListeners() {
614 return null; // FIXME: lifecycle.findLifecycleListeners();
615 }
616
617
618 /**
619 * Remove a lifecycle event listener from this component.
620 *
621 * @param listener The listener to add
622 */
623 public void removeLifecycleListener(LifecycleListener listener) {
624
625 lifecycle.removeLifecycleListener(listener);
626
627 }
628
629
630 /**
631 * Start the background thread we will use for request processing.
632 *
633 * @exception LifecycleException if a fatal startup error occurs
634 */
635 public void start() throws LifecycleException {
636
637 if (started)
638 throw new LifecycleException
639 (sm.getString("ajp13Processor.alreadyStarted"));
640 lifecycle.fireLifecycleEvent(START_EVENT, null);
641 started = true;
642
643 threadStart();
644
645 }
646
647
648 /**
649 * Stop the background thread we will use for request processing.
650 *
651 * @exception LifecycleException if a fatal shutdown error occurs
652 */
653 public void stop() throws LifecycleException {
654
655 if (!started)
656 throw new LifecycleException
657 (sm.getString("ajp13Processor.notStarted"));
658 lifecycle.fireLifecycleEvent(STOP_EVENT, null);
659 started = false;
660
661 threadStop();
662
663 }
664
665
666 }