Source code: org/apache/ajp/Ajp13.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;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.net.Socket;
23
24 import org.apache.tomcat.util.http.BaseRequest;
25 import org.apache.tomcat.util.http.HttpMessages;
26 import org.apache.tomcat.util.http.MimeHeaders;
27
28 /**
29 * Represents a single, persistent connection between the web server and
30 * the servlet container. Uses the Apache JServ Protocol version 1.3 for
31 * communication. Because this protocal does not multiplex requests, this
32 * connection can only be associated with a single request-handling cycle
33 * at a time.<P>
34 *
35 * This class contains knowledge about how an individual packet is laid out
36 * (via the <CODE>Ajp13Packet</CODE> class), and also about the
37 * stages of communicaton between the server and the servlet container. It
38 * translates from Tomcat's internal servlet support methods
39 * (e.g. doWrite) to the correct packets to send to the web server.
40 *
41 * @author Dan Milstein [danmil@shore.net]
42 * @author Keith Wannamaker [Keith@Wannamaker.org]
43 * @author Kevin Seguin [seguin@apache.org]
44 * @author Henri Gomez [hgomez@apache.org]
45 * @author Costin Manolache
46 */
47 public class Ajp13 {
48
49 public static final int MAX_PACKET_SIZE=8192;
50 public static final int H_SIZE=4; // Size of basic packet header
51
52 public static final int MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
53 public static final int MAX_SEND_SIZE = MAX_PACKET_SIZE - H_SIZE - 4;
54
55 // Error code for Ajp13
56 public static final int JK_AJP13_BAD_HEADER = -100;
57 public static final int JK_AJP13_NO_HEADER = -101;
58 public static final int JK_AJP13_COMM_CLOSED = -102;
59 public static final int JK_AJP13_COMM_BROKEN = -103;
60 public static final int JK_AJP13_BAD_BODY = -104;
61 public static final int JK_AJP13_INCOMPLETE_BODY = -105;
62
63 // ============ Instance Properties ====================
64
65 OutputStream out;
66 InputStream in;
67
68 // Buffer used of output body and headers
69 public Ajp13Packet outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
70 // Buffer used for input body
71 Ajp13Packet inBuf = new Ajp13Packet( MAX_PACKET_SIZE );
72 // Buffer used for request head ( and headers )
73 Ajp13Packet hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
74
75 // Holds incoming reads of request body data (*not* header data)
76 byte []bodyBuff = new byte[MAX_READ_SIZE];
77
78 int blen; // Length of current chunk of body data in buffer
79 int pos; // Current read position within that buffer
80
81 boolean end_of_stream; // true if we've received an empty packet
82
83 // Required handler - essential request processing
84 public RequestHandler reqHandler;
85 // AJP14 - detect protocol,set communication parameters, login
86 // If no password is set, use only Ajp13 messaging
87 boolean backwardCompat=true;
88 boolean logged=false;
89 String secret=null;
90
91 public Ajp13() {
92 super();
93 initBuf();
94 reqHandler=new RequestHandler();
95 reqHandler.init( this );
96 }
97
98 public Ajp13(RequestHandler reqHandler ) {
99 super();
100 initBuf();
101 this.reqHandler=reqHandler;
102 reqHandler.init( this );
103 }
104
105 /** Will be overriden
106 */
107 public void initBuf() {
108 outBuf = new Ajp13Packet( MAX_PACKET_SIZE );
109 inBuf = new Ajp13Packet( MAX_PACKET_SIZE );
110 hBuf=new Ajp13Packet( MAX_PACKET_SIZE );
111 }
112
113 public void recycle() {
114 if (debug > 0) {
115 logger.log("recycle()");
116 }
117
118 // This is a touch cargo-cultish, but I think wise.
119 blen = 0;
120 pos = 0;
121 end_of_stream = false;
122 logged=false;
123 }
124
125 /**
126 * Associate an open socket with this instance.
127 */
128 public void setSocket( Socket socket ) throws IOException {
129 if (debug > 0) {
130 logger.log("setSocket()");
131 }
132
133 socket.setSoLinger( true, 100);
134 out = socket.getOutputStream();
135 in = socket.getInputStream();
136 pos = 0;
137 }
138
139 /**
140 * Backward compat mode, no login needed
141 */
142 public void setBackward(boolean b)
143 {
144 backwardCompat=b;
145 }
146
147 public boolean isLogged() {
148 return logged;
149 }
150
151 void setLogged( boolean b ) {
152 logged=b;
153 }
154
155 public void setSecret( String s ) {
156 secret=s;
157 }
158
159 public String getSecret() {
160 return secret;
161 }
162
163 // -------------------- Handlers registry --------------------
164
165 static final int MAX_HANDLERS=32;
166 static final int RESERVED=16; // reserved names, backward compat
167
168 // Note that we don't make distinction between in and out
169 // messages ( i.e. one id is used only in one direction )
170 AjpHandler handlers[]=new AjpHandler[MAX_HANDLERS];
171 String handlerName[]=new String[MAX_HANDLERS];
172 int currentId=RESERVED;
173
174 public int registerMessageType( int id, String name, AjpHandler h,
175 String sig[] )
176 {
177 if( id < 0 ) {
178 // try to find it by name
179 for( int i=0; i< handlerName.length; i++ )
180 if( name.equals( handlerName[i] ) ) return i;
181 handlerName[currentId]=name;
182 handlers[currentId]=h;
183 currentId++;
184 return currentId;
185 }
186 // fixed id
187 handlerName[id]=name;
188 handlers[id]=h;
189 return id;
190 }
191
192 // -------------------- Main dispatch --------------------
193
194 /**
195 * Read a new packet from the web server and decode it. If it's a
196 * forwarded request, store its properties in the passed-in AjpRequest
197 * object.
198 *
199 * @param req An empty (newly-recycled) request object.
200 *
201 * @return 200 in case of a successful read of a forwarded request, 500
202 * if there were errors in the reading of the request, and -2 if the
203 * server is asking the container to shut itself down.
204 */
205 public int receiveNextRequest(BaseRequest req) throws IOException {
206 if (debug > 0) {
207 logger.log("receiveNextRequest()");
208 }
209
210 // XXX The return values are awful.
211
212 int err = 0;
213
214 // if we receive an IOException here, it must be because
215 // the remote just closed the ajp13 connection, and it's not
216 // an error, we just need to close the AJP13 connection
217 try {
218 err = receive(hBuf);
219 } catch (IOException ioe) {
220 if(debug >0 ) logger.log( "IOException receiving message ");
221 return -1; // Indicate it's a disconnection from the remote end
222 }
223
224 if(err < 0) {
225 if(debug >0 ) logger.log( "Error receiving message ");
226 return 500;
227 }
228
229 int type = (int)hBuf.getByte();
230 // System.out.println("XXX " + this );
231 return handleMessage( type, hBuf, req );
232 }
233
234 /** Override for ajp14, temporary
235 */
236 public int handleMessage( int type, Ajp13Packet hBuf, BaseRequest req )
237 throws IOException
238 {
239 if( type > handlers.length ) {
240 logger.log( "Invalid handler " + type );
241 return 500;
242 }
243
244 if( debug > 0 )
245 logger.log( "Received " + type + " " + handlerName[type]);
246
247 // Ajp14, unlogged
248 if( ! backwardCompat && ! isLogged() ) {
249 if( type != NegociationHandler.JK_AJP14_LOGINIT_CMD &&
250 type != NegociationHandler.JK_AJP14_LOGCOMP_CMD ) {
251
252 logger.log( "Ajp14 error: not logged " +
253 type + " " + handlerName[type]);
254
255 return 300;
256 }
257 // else continue
258 }
259
260 // Ajp13 messages
261 switch(type) {
262 case RequestHandler.JK_AJP13_FORWARD_REQUEST:
263 return reqHandler.decodeRequest(this, hBuf, req);
264
265 case RequestHandler.JK_AJP13_CPING_REQUEST:
266 return reqHandler.sendCPong(this, outBuf);
267
268 case RequestHandler.JK_AJP13_SHUTDOWN:
269 return -2;
270 }
271
272 // logged || loging message
273 AjpHandler handler=handlers[type];
274 if( handler==null ) {
275 logger.log( "Unknown message " + type + handlerName[type] );
276 return 200;
277 }
278
279 if( debug > 0 )
280 logger.log( "Ajp14 handler " + handler );
281 return handler.handleAjpMessage( type, this, hBuf, req );
282 }
283
284 // ==================== Servlet Input Support =================
285
286 /** @deprecated -- Will use reqHandler, make sure nobody else
287 calls this */
288
289
290 public int available() throws IOException {
291 return reqHandler.available(this);
292 }
293
294 public int doRead() throws IOException
295 {
296 return reqHandler.doRead( this );
297 }
298
299 public int doRead(byte[] b, int off, int len) throws IOException
300 {
301 return reqHandler.doRead( this, b, off, len );
302 }
303
304 private boolean refillReadBuffer() throws IOException
305 {
306 return reqHandler.refillReadBuffer(this);
307 }
308
309 public void beginSendHeaders(int status,
310 String statusMessage,
311 int numHeaders) throws IOException {
312 reqHandler.beginSendHeaders( this, outBuf,
313 status, statusMessage,
314 numHeaders);
315 }
316
317 public void sendHeader(String name, String value) throws IOException {
318 reqHandler.sendHeader( outBuf, name, value );
319 }
320
321
322 public void endSendHeaders() throws IOException {
323 reqHandler.endSendHeaders(this, outBuf);
324 }
325
326 public void sendHeaders(int status, MimeHeaders headers)
327 throws IOException
328 {
329 reqHandler.sendHeaders(this, outBuf, status,
330 HttpMessages.getMessage(status),
331 headers);
332 }
333
334 public void sendHeaders(int status, String statusMessage,
335 MimeHeaders headers)
336 throws IOException
337 {
338 reqHandler.sendHeaders( this, outBuf, status,
339 statusMessage, headers );
340 }
341
342 public void finish() throws IOException {
343 reqHandler.finish(this, outBuf );
344 }
345
346 public void doWrite(byte b[], int off, int len) throws IOException {
347 reqHandler.doWrite( this, outBuf, b, off, len );
348 }
349
350
351 // ========= Internal Packet-Handling Methods =================
352
353 /**
354 * Read N bytes from the InputStream, and ensure we got them all
355 * Under heavy load we could experience many fragmented packets
356 * just read Unix Network Programming to recall that a call to
357 * read didn't ensure you got all the data you want
358 *
359 * from read() Linux manual
360 *
361 * On success, the number of bytes read is returned (zero indicates end of file),
362 * and the file position is advanced by this number.
363 * It is not an error if this number is smaller than the number of bytes requested;
364 * this may happen for example because fewer bytes
365 * are actually available right now (maybe because we were close to end-of-file,
366 * or because we are reading from a pipe, or from a
367 * terminal), or because read() was interrupted by a signal.
368 * On error, -1 is returned, and errno is set appropriately. In this
369 * case it is left unspecified whether the file position (if any) changes.
370 *
371 **/
372 private int readN(InputStream in, byte[] b, int offset, int len) throws IOException {
373 int pos = 0;
374 int got;
375
376 while(pos < len) {
377 got = in.read(b, pos + offset, len - pos);
378
379 if (debug > 10) {
380 logger.log("read got # " + got);
381 }
382
383 // connection just closed by remote.
384 if (got <= 0) {
385 // This happens periodically, as apache restarts
386 // periodically.
387 // It should be more gracefull ! - another feature for Ajp14
388 return JK_AJP13_COMM_BROKEN;
389 }
390
391 pos += got;
392 }
393 return pos;
394 }
395
396 /**
397 * Read in a packet from the web server and store it in the passed-in
398 * <CODE>Ajp13Packet</CODE> object.
399 *
400 * @param msg The object into which to store the incoming packet -- any
401 * current contents will be overwritten.
402 *
403 * @return The number of bytes read on a successful read or -1 if there
404 * was an error.
405 **/
406 public int receive(Ajp13Packet msg) throws IOException {
407 if (debug > 0) {
408 logger.log("receive()");
409 }
410
411 // XXX If the length in the packet header doesn't agree with the
412 // actual number of bytes read, it should probably return an error
413 // value. Also, callers of this method never use the length
414 // returned -- should probably return true/false instead.
415 byte b[] = msg.getBuff();
416
417 int rd = readN(in, b, 0, H_SIZE );
418
419 // XXX - connection closed (JK_AJP13_COMM_CLOSED)
420 // - connection broken (JK_AJP13_COMM_BROKEN)
421 //
422 if(rd < 0) {
423 // Most likely normal apache restart.
424 return rd;
425 }
426
427 int len = msg.checkIn();
428 if( debug > 5 )
429 logger.log( "Received " + rd + " " + len + " " + b[0] );
430
431 // XXX check if enough space - it's assert()-ed !!!
432
433 int total_read = 0;
434
435 total_read = readN(in, b, H_SIZE, len);
436
437 // it's ok to have read 0 bytes when len=0 -- this means
438 // the end of the stream has been reached.
439 if (total_read < 0) {
440 logger.log("can't read body, waited #" + len);
441 return JK_AJP13_BAD_BODY;
442 }
443
444 if (total_read != len) {
445 logger.log( "incomplete read, waited #" + len +
446 " got only " + total_read);
447 return JK_AJP13_INCOMPLETE_BODY;
448 }
449
450 if (debug > 0)
451 logger.log("receive: total read = " + total_read);
452 return total_read;
453 }
454
455 /**
456 * Send a packet to the web server. Works for any type of message.
457 *
458 * @param msg A packet with accumulated data to send to the server --
459 * this method will write out the length in the header.
460 */
461 public void send( Ajp13Packet msg ) throws IOException {
462 if (debug > 0) {
463 logger.log("send()");
464 }
465
466 msg.end(); // Write the packet header
467 byte b[] = msg.getBuff();
468 int len = msg.getLen();
469
470 if (debug > 5 )
471 logger.log("send() " + len + " " + b[0] );
472
473 out.write( b, 0, len );
474 }
475
476 /**
477 * Close the socket connection to the web server. In general, sockets
478 * are maintained across many requests, so this will not be called
479 * after finish().
480 */
481 public void close() throws IOException {
482 if (debug > 0) {
483 logger.log("close()");
484 }
485
486 if(null != out) {
487 out.close();
488 }
489 if(null !=in) {
490 in.close();
491 }
492 setLogged( false ); // no more logged now
493 }
494
495 // -------------------- Debug --------------------
496 protected int debug = 0;
497
498 public void setDebug(int debug) {
499 this.debug = debug;
500 this.reqHandler.setDebug(debug);
501 }
502
503 public void setLogger(Logger l) {
504 this.logger = l;
505 this.reqHandler.setLogger(l);
506 }
507
508 /**
509 * XXX place holder...
510 */
511 Logger logger = new Logger();
512 }