001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.ajp;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.OutputStream;
022: import java.net.Socket;
023:
024: import org.apache.tomcat.util.http.BaseRequest;
025: import org.apache.tomcat.util.http.HttpMessages;
026: import org.apache.tomcat.util.http.MimeHeaders;
027:
028: /**
029: * Represents a single, persistent connection between the web server and
030: * the servlet container. Uses the Apache JServ Protocol version 1.3 for
031: * communication. Because this protocal does not multiplex requests, this
032: * connection can only be associated with a single request-handling cycle
033: * at a time.<P>
034: *
035: * This class contains knowledge about how an individual packet is laid out
036: * (via the <CODE>Ajp13Packet</CODE> class), and also about the
037: * stages of communicaton between the server and the servlet container. It
038: * translates from Tomcat's internal servlet support methods
039: * (e.g. doWrite) to the correct packets to send to the web server.
040: *
041: * @see Ajp13Interceptor
042: *
043: * @author Dan Milstein [danmil@shore.net]
044: * @author Keith Wannamaker [Keith@Wannamaker.org]
045: * @author Kevin Seguin [seguin@apache.org]
046: * @author Henri Gomez [hgomez@apache.org]
047: * @author Costin Manolache
048: */
049: public class Ajp13 {
050:
051: public static final int MAX_PACKET_SIZE = 8192;
052: public static final int H_SIZE = 4; // Size of basic packet header
053:
054: public static final int MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE
055: - 2;
056: public static final int MAX_SEND_SIZE = MAX_PACKET_SIZE - H_SIZE
057: - 4;
058:
059: // Error code for Ajp13
060: public static final int JK_AJP13_BAD_HEADER = -100;
061: public static final int JK_AJP13_NO_HEADER = -101;
062: public static final int JK_AJP13_COMM_CLOSED = -102;
063: public static final int JK_AJP13_COMM_BROKEN = -103;
064: public static final int JK_AJP13_BAD_BODY = -104;
065: public static final int JK_AJP13_INCOMPLETE_BODY = -105;
066:
067: // ============ Instance Properties ====================
068:
069: OutputStream out;
070: InputStream in;
071:
072: // Buffer used of output body and headers
073: public Ajp13Packet outBuf = new Ajp13Packet(MAX_PACKET_SIZE);
074: // Buffer used for input body
075: Ajp13Packet inBuf = new Ajp13Packet(MAX_PACKET_SIZE);
076: // Buffer used for request head ( and headers )
077: Ajp13Packet hBuf = new Ajp13Packet(MAX_PACKET_SIZE);
078:
079: // Holds incoming reads of request body data (*not* header data)
080: byte[] bodyBuff = new byte[MAX_READ_SIZE];
081:
082: int blen; // Length of current chunk of body data in buffer
083: int pos; // Current read position within that buffer
084:
085: boolean end_of_stream; // true if we've received an empty packet
086:
087: // Required handler - essential request processing
088: public RequestHandler reqHandler;
089: // AJP14 - detect protocol,set communication parameters, login
090: // If no password is set, use only Ajp13 messaging
091: boolean backwardCompat = true;
092: boolean logged = false;
093: String secret = null;
094:
095: public Ajp13() {
096: super ();
097: initBuf();
098: reqHandler = new RequestHandler();
099: reqHandler.init(this );
100: }
101:
102: public Ajp13(RequestHandler reqHandler) {
103: super ();
104: initBuf();
105: this .reqHandler = reqHandler;
106: reqHandler.init(this );
107: }
108:
109: /** Will be overriden
110: */
111: public void initBuf() {
112: outBuf = new Ajp13Packet(MAX_PACKET_SIZE);
113: inBuf = new Ajp13Packet(MAX_PACKET_SIZE);
114: hBuf = new Ajp13Packet(MAX_PACKET_SIZE);
115: }
116:
117: public void recycle() {
118: if (debug > 0) {
119: logger.log("recycle()");
120: }
121:
122: // This is a touch cargo-cultish, but I think wise.
123: blen = 0;
124: pos = 0;
125: end_of_stream = false;
126: logged = false;
127: }
128:
129: /**
130: * Associate an open socket with this instance.
131: */
132: public void setSocket(Socket socket) throws IOException {
133: if (debug > 0) {
134: logger.log("setSocket()");
135: }
136:
137: socket.setSoLinger(true, 100);
138: out = socket.getOutputStream();
139: in = socket.getInputStream();
140: pos = 0;
141: }
142:
143: /**
144: * Backward compat mode, no login needed
145: */
146: public void setBackward(boolean b) {
147: backwardCompat = b;
148: }
149:
150: public boolean isLogged() {
151: return logged;
152: }
153:
154: void setLogged(boolean b) {
155: logged = b;
156: }
157:
158: public void setSecret(String s) {
159: secret = s;
160: }
161:
162: public String getSecret() {
163: return secret;
164: }
165:
166: // -------------------- Handlers registry --------------------
167:
168: static final int MAX_HANDLERS = 32;
169: static final int RESERVED = 16; // reserved names, backward compat
170:
171: // Note that we don't make distinction between in and out
172: // messages ( i.e. one id is used only in one direction )
173: AjpHandler handlers[] = new AjpHandler[MAX_HANDLERS];
174: String handlerName[] = new String[MAX_HANDLERS];
175: int currentId = RESERVED;
176:
177: public int registerMessageType(int id, String name, AjpHandler h,
178: String sig[]) {
179: if (id < 0) {
180: // try to find it by name
181: for (int i = 0; i < handlerName.length; i++)
182: if (name.equals(handlerName[i]))
183: return i;
184: handlerName[currentId] = name;
185: handlers[currentId] = h;
186: currentId++;
187: return currentId;
188: }
189: // fixed id
190: handlerName[id] = name;
191: handlers[id] = h;
192: return id;
193: }
194:
195: // -------------------- Main dispatch --------------------
196:
197: /**
198: * Read a new packet from the web server and decode it. If it's a
199: * forwarded request, store its properties in the passed-in AjpRequest
200: * object.
201: *
202: * @param req An empty (newly-recycled) request object.
203: *
204: * @return 200 in case of a successful read of a forwarded request, 500
205: * if there were errors in the reading of the request, and -2 if the
206: * server is asking the container to shut itself down.
207: */
208: public int receiveNextRequest(BaseRequest req) throws IOException {
209: if (debug > 0) {
210: logger.log("receiveNextRequest()");
211: }
212:
213: // XXX The return values are awful.
214:
215: int err = 0;
216:
217: // if we receive an IOException here, it must be because
218: // the remote just closed the ajp13 connection, and it's not
219: // an error, we just need to close the AJP13 connection
220: try {
221: err = receive(hBuf);
222: } catch (IOException ioe) {
223: if (debug > 0)
224: logger.log("IOException receiving message ");
225: return -1; // Indicate it's a disconnection from the remote end
226: }
227:
228: if (err < 0) {
229: if (debug > 0)
230: logger.log("Error receiving message ");
231: return 500;
232: }
233:
234: int type = (int) hBuf.getByte();
235: // System.out.println("XXX " + this );
236: return handleMessage(type, hBuf, req);
237: }
238:
239: /** Override for ajp14, temporary
240: */
241: public int handleMessage(int type, Ajp13Packet hBuf, BaseRequest req)
242: throws IOException {
243: if (type > handlers.length) {
244: logger.log("Invalid handler " + type);
245: return 500;
246: }
247:
248: if (debug > 0)
249: logger.log("Received " + type + " " + handlerName[type]);
250:
251: // Ajp14, unlogged
252: if (!backwardCompat && !isLogged()) {
253: if (type != NegociationHandler.JK_AJP14_LOGINIT_CMD
254: && type != NegociationHandler.JK_AJP14_LOGCOMP_CMD) {
255:
256: logger.log("Ajp14 error: not logged " + type + " "
257: + handlerName[type]);
258:
259: return 300;
260: }
261: // else continue
262: }
263:
264: // Ajp13 messages
265: switch (type) {
266: case RequestHandler.JK_AJP13_FORWARD_REQUEST:
267: return reqHandler.decodeRequest(this , hBuf, req);
268:
269: case RequestHandler.JK_AJP13_CPING_REQUEST:
270: return reqHandler.sendCPong(this , outBuf);
271:
272: case RequestHandler.JK_AJP13_SHUTDOWN:
273: return -2;
274: }
275:
276: // logged || loging message
277: AjpHandler handler = handlers[type];
278: if (handler == null) {
279: logger.log("Unknown message " + type + handlerName[type]);
280: return 200;
281: }
282:
283: if (debug > 0)
284: logger.log("Ajp14 handler " + handler);
285: return handler.handleAjpMessage(type, this , hBuf, req);
286: }
287:
288: // ==================== Servlet Input Support =================
289:
290: /** @deprecated -- Will use reqHandler, make sure nobody else
291: calls this */
292:
293: public int available() throws IOException {
294: return reqHandler.available(this );
295: }
296:
297: public int doRead() throws IOException {
298: return reqHandler.doRead(this );
299: }
300:
301: public int doRead(byte[] b, int off, int len) throws IOException {
302: return reqHandler.doRead(this , b, off, len);
303: }
304:
305: private boolean refillReadBuffer() throws IOException {
306: return reqHandler.refillReadBuffer(this );
307: }
308:
309: public void beginSendHeaders(int status, String statusMessage,
310: int numHeaders) throws IOException {
311: reqHandler.beginSendHeaders(this , outBuf, status,
312: statusMessage, numHeaders);
313: }
314:
315: public void sendHeader(String name, String value)
316: throws IOException {
317: reqHandler.sendHeader(outBuf, name, value);
318: }
319:
320: public void endSendHeaders() throws IOException {
321: reqHandler.endSendHeaders(this , outBuf);
322: }
323:
324: public void sendHeaders(int status, MimeHeaders headers)
325: throws IOException {
326: reqHandler.sendHeaders(this , outBuf, status, HttpMessages
327: .getMessage(status), headers);
328: }
329:
330: public void sendHeaders(int status, String statusMessage,
331: MimeHeaders headers) throws IOException {
332: reqHandler.sendHeaders(this , outBuf, status, statusMessage,
333: headers);
334: }
335:
336: public void finish() throws IOException {
337: reqHandler.finish(this , outBuf);
338: }
339:
340: public void doWrite(byte b[], int off, int len) throws IOException {
341: reqHandler.doWrite(this , outBuf, b, off, len);
342: }
343:
344: // ========= Internal Packet-Handling Methods =================
345:
346: /**
347: * Read N bytes from the InputStream, and ensure we got them all
348: * Under heavy load we could experience many fragmented packets
349: * just read Unix Network Programming to recall that a call to
350: * read didn't ensure you got all the data you want
351: *
352: * from read() Linux manual
353: *
354: * On success, the number of bytes read is returned (zero indicates end of file),
355: * and the file position is advanced by this number.
356: * It is not an error if this number is smaller than the number of bytes requested;
357: * this may happen for example because fewer bytes
358: * are actually available right now (maybe because we were close to end-of-file,
359: * or because we are reading from a pipe, or from a
360: * terminal), or because read() was interrupted by a signal.
361: * On error, -1 is returned, and errno is set appropriately. In this
362: * case it is left unspecified whether the file position (if any) changes.
363: *
364: **/
365: private int readN(InputStream in, byte[] b, int offset, int len)
366: throws IOException {
367: int pos = 0;
368: int got;
369:
370: while (pos < len) {
371: got = in.read(b, pos + offset, len - pos);
372:
373: if (debug > 10) {
374: logger.log("read got # " + got);
375: }
376:
377: // connection just closed by remote.
378: if (got <= 0) {
379: // This happens periodically, as apache restarts
380: // periodically.
381: // It should be more gracefull ! - another feature for Ajp14
382: return JK_AJP13_COMM_BROKEN;
383: }
384:
385: pos += got;
386: }
387: return pos;
388: }
389:
390: /**
391: * Read in a packet from the web server and store it in the passed-in
392: * <CODE>Ajp13Packet</CODE> object.
393: *
394: * @param msg The object into which to store the incoming packet -- any
395: * current contents will be overwritten.
396: *
397: * @return The number of bytes read on a successful read or -1 if there
398: * was an error.
399: **/
400: public int receive(Ajp13Packet msg) throws IOException {
401: if (debug > 0) {
402: logger.log("receive()");
403: }
404:
405: // XXX If the length in the packet header doesn't agree with the
406: // actual number of bytes read, it should probably return an error
407: // value. Also, callers of this method never use the length
408: // returned -- should probably return true/false instead.
409: byte b[] = msg.getBuff();
410:
411: int rd = readN(in, b, 0, H_SIZE);
412:
413: // XXX - connection closed (JK_AJP13_COMM_CLOSED)
414: // - connection broken (JK_AJP13_COMM_BROKEN)
415: //
416: if (rd < 0) {
417: // Most likely normal apache restart.
418: return rd;
419: }
420:
421: int len = msg.checkIn();
422: if (debug > 5)
423: logger.log("Received " + rd + " " + len + " " + b[0]);
424:
425: // XXX check if enough space - it's assert()-ed !!!
426:
427: int total_read = 0;
428:
429: total_read = readN(in, b, H_SIZE, len);
430:
431: // it's ok to have read 0 bytes when len=0 -- this means
432: // the end of the stream has been reached.
433: if (total_read < 0) {
434: logger.log("can't read body, waited #" + len);
435: return JK_AJP13_BAD_BODY;
436: }
437:
438: if (total_read != len) {
439: logger.log("incomplete read, waited #" + len + " got only "
440: + total_read);
441: return JK_AJP13_INCOMPLETE_BODY;
442: }
443:
444: if (debug > 0)
445: logger.log("receive: total read = " + total_read);
446: return total_read;
447: }
448:
449: /**
450: * Send a packet to the web server. Works for any type of message.
451: *
452: * @param msg A packet with accumulated data to send to the server --
453: * this method will write out the length in the header.
454: */
455: public void send(Ajp13Packet msg) throws IOException {
456: if (debug > 0) {
457: logger.log("send()");
458: }
459:
460: msg.end(); // Write the packet header
461: byte b[] = msg.getBuff();
462: int len = msg.getLen();
463:
464: if (debug > 5)
465: logger.log("send() " + len + " " + b[0]);
466:
467: out.write(b, 0, len);
468: }
469:
470: /**
471: * Close the socket connection to the web server. In general, sockets
472: * are maintained across many requests, so this will not be called
473: * after finish().
474: *
475: * @see Ajp13Interceptor#processConnection
476: */
477: public void close() throws IOException {
478: if (debug > 0) {
479: logger.log("close()");
480: }
481:
482: if (null != out) {
483: out.close();
484: }
485: if (null != in) {
486: in.close();
487: }
488: setLogged(false); // no more logged now
489: }
490:
491: // -------------------- Debug --------------------
492: protected int debug = 0;
493:
494: public void setDebug(int debug) {
495: this .debug = debug;
496: this .reqHandler.setDebug(debug);
497: }
498:
499: public void setLogger(Logger l) {
500: this .logger = l;
501: this .reqHandler.setLogger(l);
502: }
503:
504: /**
505: * XXX place holder...
506: */
507: Logger logger = new Logger();
508: }
|