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.tomcat33;
018:
019: import java.io.IOException;
020: import java.net.InetAddress;
021: import java.net.Socket;
022:
023: import org.apache.ajp.Ajp13;
024: import org.apache.ajp.NegociationHandler;
025: import org.apache.ajp.RequestHandler;
026: import org.apache.tomcat.core.Context;
027: import org.apache.tomcat.core.ContextManager;
028: import org.apache.tomcat.core.Request;
029: import org.apache.tomcat.core.Response;
030: import org.apache.tomcat.core.TomcatException;
031: import org.apache.tomcat.modules.server.PoolTcpConnector;
032: import org.apache.tomcat.util.buf.UDecoder;
033: import org.apache.tomcat.util.http.BaseRequest;
034: import org.apache.tomcat.util.http.Cookies;
035: import org.apache.tomcat.util.http.HttpMessages;
036: import org.apache.tomcat.util.net.TcpConnection;
037: import org.apache.tomcat.util.net.TcpConnectionHandler;
038:
039: /** Note. PoolTcpConnector is a convenience base class used for
040: TCP-based connectors in tomcat33. It allows all those modules
041: to share the thread pool and listener code.
042:
043: In future it's likely other optimizations will be implemented in
044: the PoolTcpConnector, so it's better to use it if you don't have
045: a good reason not to ( like a connector for J2ME, where you want
046: minimal footprint and don't care about high load )
047: */
048:
049: /** Tomcat 33 module implementing the Ajp14 protocol.
050: *
051: * The actual protocol implementation is in Ajp14.java, this is just an
052: * adapter to plug it into tomcat.
053: */
054: public class Ajp14Interceptor extends PoolTcpConnector implements
055: TcpConnectionHandler {
056: int ajp14_note = -1;
057: String password;
058: RequestHandler reqHandler = new RequestHandler();
059: NegociationHandler negHandler = new NegociationHandler();
060:
061: public Ajp14Interceptor() {
062: super ();
063: super .setSoLinger(100);
064: super .setTcpNoDelay(true);
065: }
066:
067: // initialization
068: public void engineInit(ContextManager cm) throws TomcatException {
069: log("engineInit");
070: }
071:
072: public void engineStart(ContextManager cm) throws TomcatException {
073: super .engineInit(cm);
074: ajp14_note = cm.getNoteId(ContextManager.REQUEST_NOTE, "ajp14");
075: super .engineStart(cm);
076: }
077:
078: // -------------------- Ajp14 specific parameters --------------------
079:
080: public void setPassword(String s) {
081: this .password = s;
082: }
083:
084: /**
085: * Set the original entropy seed
086: */
087: public void setSeed(String pseed) {
088: negHandler.setSeed(pseed);
089: }
090:
091: // -------------------- PoolTcpConnector --------------------
092:
093: /** Called by PoolTcpConnector to allow childs to init.
094: */
095: protected void localInit() throws Exception {
096: ep.setConnectionHandler(this );
097: }
098:
099: // -------------------- Handler implementation --------------------
100:
101: /* The TcpConnectionHandler interface is used by the PoolTcpConnector to
102: * handle incoming connections.
103: */
104:
105: /** Called by the thread pool when a new thread is added to the pool,
106: in order to create the (expensive) objects that will be stored
107: as thread data.
108: XXX we should use a single object, not array ( several reasons ),
109: XXX Ajp14 should be storead as a request note, to be available in
110: all modules
111: */
112: public Object[] init() {
113: if (debug > 0)
114: log("Init ");
115: Object thData[] = new Object[1];
116: thData[0] = initRequest(null);
117: return thData;
118: }
119:
120: /** Construct the request object, with probably unnecesary
121: sanity tests ( should work without thread pool - but that is
122: not supported in PoolTcpConnector, maybe in future )
123: */
124: private Ajp14Request initRequest(Object thData[]) {
125: if (ajp14_note < 0)
126: throw new RuntimeException("assert: ajp14_note>0");
127: Ajp14Request req = null;
128: if (thData != null) {
129: req = (Ajp14Request) thData[0];
130: }
131: if (req != null) {
132: Response res = req.getResponse();
133: req.recycle();
134: res.recycle();
135: // make the note available to other modules
136: req.setNote(ajp14_note, req.ajp13);
137: return req;
138: }
139: // either thData==null or broken ( req==null)
140: Ajp13 ajp13 = new Ajp13(reqHandler);
141: negHandler.init(ajp13);
142:
143: negHandler.setContainerSignature(ContextManager.TOMCAT_NAME
144: + " v" + ContextManager.TOMCAT_VERSION);
145: if (password != null) {
146: negHandler.setPassword(password);
147: ajp13.setBackward(false);
148: }
149:
150: BaseRequest ajpreq = new BaseRequest();
151:
152: req = new Ajp14Request(ajp13, ajpreq);
153: Ajp14Response res = new Ajp14Response(ajp13);
154: cm.initRequest(req, res);
155: return req;
156: }
157:
158: /** Called whenever a new TCP connection is received. The connection
159: is reused.
160: */
161: public void processConnection(TcpConnection connection,
162: Object thData[]) {
163: try {
164: if (debug > 0)
165: log("Received ajp14 connection ");
166: Socket socket = connection.getSocket();
167: // assert: socket!=null, connection!=null ( checked by PoolTcpEndpoint )
168:
169: socket.setSoLinger(true, 100);
170:
171: Ajp14Request req = initRequest(thData);
172: Ajp14Response res = (Ajp14Response) req.getResponse();
173: Ajp13 ajp13 = req.ajp13;
174: BaseRequest ajpReq = req.ajpReq;
175:
176: ajp13.setSocket(socket);
177:
178: // first request should be the loginit.
179: int status = ajp13.receiveNextRequest(ajpReq);
180: if (status != 304) { // XXX use better codes
181: log("Failure in logInit ");
182: return;
183: }
184:
185: status = ajp13.receiveNextRequest(ajpReq);
186: if (status != 304) { // XXX use better codes
187: log("Failure in login ");
188: return;
189: }
190:
191: boolean moreRequests = true;
192: while (moreRequests) {
193: status = ajp13.receiveNextRequest(ajpReq);
194:
195: if (status == -2) {
196: // special case - shutdown
197: // XXX need better communication, refactor it
198: if (!doShutdown(socket.getLocalAddress(), socket
199: .getInetAddress())) {
200: moreRequests = false;
201: continue;
202: }
203: }
204:
205: // 999 low level requests are just ignored (ie cping/cpong)
206: if (status == 200)
207: cm.service(req, res);
208: else if (status == 500) {
209: log("Invalid request received " + req);
210: break;
211: }
212:
213: req.recycle();
214: res.recycle();
215: }
216: if (debug > 0)
217: log("Closing ajp14 connection");
218: ajp13.close();
219: socket.close();
220: } catch (Exception e) {
221: log("Processing connection " + connection, e);
222: }
223: }
224:
225: // We don't need to check isSameAddress if we authenticate !!!
226: protected boolean doShutdown(InetAddress serverAddr,
227: InetAddress clientAddr) {
228: try {
229: // close the socket connection before handling any signal
230: // but get the addresses first so they are not corrupted
231: if (isSameAddress(serverAddr, clientAddr)) {
232: cm.stop();
233: // same behavior as in past, because it seems that
234: // stopping everything doesn't work - need to figure
235: // out what happens with the threads ( XXX )
236:
237: // XXX It should work now - but will fail if servlets create
238: // threads
239: System.exit(0);
240: }
241: } catch (Exception ignored) {
242: log("Ignored " + ignored);
243: }
244: log("Shutdown command ignored");
245: return false;
246: }
247:
248: // legacy, should be removed
249: public void setServer(Object contextM) {
250: this .cm = (ContextManager) contextM;
251: }
252:
253: public Object getInfo(Context ctx, Request request, int id,
254: String key) {
255: if (!(request instanceof Ajp14Request)) {
256: return null;
257: }
258: Ajp14Request ajp14req = (Ajp14Request) request;
259: return ajp14req.ajpReq.getAttribute(key);
260: }
261:
262: public int setInfo(Context ctx, Request request, int id,
263: String key, Object obj) {
264: if (!(request instanceof Ajp14Request)) {
265: return DECLINED;
266: }
267: Ajp14Request ajp14req = (Ajp14Request) request;
268: ajp14req.ajpReq.setAttribute(key, obj);
269: return OK;
270: }
271:
272: }
273:
274: //-------------------- Glue code for request/response.
275: // Probably not needed ( or can be simplified ), but it's
276: // not that bad.
277:
278: class Ajp14Request extends Request {
279: Ajp13 ajp13;
280: BaseRequest ajpReq;
281:
282: public Ajp14Request(Ajp13 ajp13, BaseRequest ajpReq) {
283: headers = ajpReq.headers();
284: methodMB = ajpReq.method();
285: protoMB = ajpReq.protocol();
286: uriMB = ajpReq.requestURI();
287: queryMB = ajpReq.queryString();
288: remoteAddrMB = ajpReq.remoteAddr();
289: remoteHostMB = ajpReq.remoteHost();
290: serverNameMB = ajpReq.serverName();
291:
292: // XXX sync cookies
293: scookies = new Cookies(headers);
294: urlDecoder = new UDecoder();
295:
296: // XXX sync headers
297:
298: params.setQuery(queryMB);
299: params.setURLDecoder(urlDecoder);
300: params.setHeaders(headers);
301: initRequest();
302:
303: this .ajp13 = ajp13;
304: this .ajpReq = ajpReq;
305: }
306:
307: // -------------------- Wrappers for changed method names, and to use the buffers
308: // XXX Move BaseRequest into util !!! ( it's just a stuct with some MessageBytes )
309:
310: public int getServerPort() {
311: return ajpReq.getServerPort();
312: }
313:
314: public void setServerPort(int i) {
315: ajpReq.setServerPort(i);
316: }
317:
318: public void setRemoteUser(String s) {
319: super .setRemoteUser(s);
320: ajpReq.remoteUser().setString(s);
321: }
322:
323: public String getRemoteUser() {
324: String s = ajpReq.remoteUser().toString();
325: if (s == null)
326: s = super .getRemoteUser();
327: return s;
328: }
329:
330: public String getAuthType() {
331: return ajpReq.authType().toString();
332: }
333:
334: public void setAuthType(String s) {
335: ajpReq.authType().setString(s);
336: }
337:
338: public String getJvmRoute() {
339: return ajpReq.jvmRoute().toString();
340: }
341:
342: public void setJvmRoute(String s) {
343: ajpReq.jvmRoute().setString(s);
344: }
345:
346: // XXX scheme
347:
348: public boolean isSecure() {
349: return ajpReq.getSecure();
350: }
351:
352: public int getContentLength() {
353: int i = ajpReq.getContentLength();
354: if (i >= 0)
355: return i;
356: i = super .getContentLength();
357: return i;
358: }
359:
360: public void setContentLength(int i) {
361: super .setContentLength(i); // XXX sync
362: }
363:
364: // XXX broken
365: // public Iterator getAttributeNames() {
366: // return attributes.keySet().iterator();
367: // }
368:
369: // --------------------
370:
371: public void recycle() {
372: super .recycle();
373: ajpReq.recycle();
374: if (ajp13 != null)
375: ajp13.recycle();
376: }
377:
378: public String dumpRequest() {
379: return ajpReq.toString();
380: }
381:
382: // --------------------
383:
384: // XXX This should go away if we introduce an InputBuffer.
385: // We almost have it as result of encoding fixes, but for now
386: // just keep this here, doesn't hurt too much.
387: public int doRead() throws IOException {
388: if (available <= 0)
389: return -1;
390: available--;
391: return ajp13.reqHandler.doRead(ajp13);
392: }
393:
394: public int doRead(byte[] b, int off, int len) throws IOException {
395: if (available <= 0)
396: return -1;
397: int rd = ajp13.reqHandler.doRead(ajp13, b, off, len);
398: available -= rd;
399: return rd;
400: }
401:
402: }
403:
404: class Ajp14Response extends Response {
405: Ajp13 ajp13;
406: boolean finished = false;
407:
408: public Ajp14Response(Ajp13 ajp13) {
409: super ();
410: this .ajp13 = ajp13;
411: }
412:
413: public void recycle() {
414: super .recycle();
415: finished = false;
416: }
417:
418: // XXX if more headers that MAX_SIZE, send 2 packets!
419: // XXX Can be implemented using module notification, no need to extend
420: public void endHeaders() throws IOException {
421: super .endHeaders();
422:
423: if (request.protocol().isNull()) {
424: return;
425: }
426:
427: ajp13.reqHandler.sendHeaders(ajp13, ajp13.outBuf, getStatus(),
428: HttpMessages.getMessage(status), getMimeHeaders());
429: }
430:
431: // XXX Can be implemented using module notification, no need to extend
432: public void finish() throws IOException {
433: if (!finished) {
434: super .finish();
435: finished = true; // Avoid END_OF_RESPONSE sent 2 times
436: ajp13.reqHandler.finish(ajp13, ajp13.outBuf);
437: }
438: }
439:
440: // XXX Can be implemented using the buffers, no need to extend
441: public void doWrite(byte b[], int off, int len) throws IOException {
442: ajp13.reqHandler.doWrite(ajp13, ajp13.outBuf, b, off, len);
443: }
444:
445: }
|