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.coyote;
018:
019: import java.io.IOException;
020: import java.util.HashMap;
021:
022: import org.apache.tomcat.util.buf.ByteChunk;
023: import org.apache.tomcat.util.buf.MessageBytes;
024: import org.apache.tomcat.util.buf.UDecoder;
025:
026: import org.apache.tomcat.util.http.MimeHeaders;
027: import org.apache.tomcat.util.http.Parameters;
028: import org.apache.tomcat.util.http.ContentType;
029: import org.apache.tomcat.util.http.Cookies;
030:
031: /**
032: * This is a low-level, efficient representation of a server request. Most
033: * fields are GC-free, expensive operations are delayed until the user code
034: * needs the information.
035: *
036: * Processing is delegated to modules, using a hook mechanism.
037: *
038: * This class is not intended for user code - it is used internally by tomcat
039: * for processing the request in the most efficient way. Users ( servlets ) can
040: * access the information using a facade, which provides the high-level view
041: * of the request.
042: *
043: * For lazy evaluation, the request uses the getInfo() hook. The following ids
044: * are defined:
045: * <ul>
046: * <li>req.encoding - returns the request encoding
047: * <li>req.attribute - returns a module-specific attribute ( like SSL keys, etc ).
048: * </ul>
049: *
050: * Tomcat defines a number of attributes:
051: * <ul>
052: * <li>"org.apache.tomcat.request" - allows access to the low-level
053: * request object in trusted applications
054: * </ul>
055: *
056: * @author James Duncan Davidson [duncan@eng.sun.com]
057: * @author James Todd [gonzo@eng.sun.com]
058: * @author Jason Hunter [jch@eng.sun.com]
059: * @author Harish Prabandham
060: * @author Alex Cruikshank [alex@epitonic.com]
061: * @author Hans Bergsten [hans@gefionsoftware.com]
062: * @author Costin Manolache
063: * @author Remy Maucherat
064: */
065: public final class Request {
066:
067: // ----------------------------------------------------------- Constructors
068:
069: public Request() {
070:
071: parameters.setQuery(queryMB);
072: parameters.setURLDecoder(urlDecoder);
073: parameters.setHeaders(headers);
074:
075: schemeMB.setString("http");
076: methodMB.setString("GET");
077: uriMB.setString("/");
078: queryMB.setString("");
079: protoMB.setString("HTTP/1.0");
080:
081: }
082:
083: // ----------------------------------------------------- Instance Variables
084:
085: private int serverPort = -1;
086: private MessageBytes serverNameMB = new MessageBytes();
087:
088: private String localHost;
089:
090: private int remotePort;
091: private int localPort;
092:
093: private MessageBytes schemeMB = new MessageBytes();
094:
095: private MessageBytes methodMB = new MessageBytes();
096: private MessageBytes unparsedURIMB = new MessageBytes();
097: private MessageBytes uriMB = new MessageBytes();
098: private MessageBytes decodedUriMB = new MessageBytes();
099: private MessageBytes queryMB = new MessageBytes();
100: private MessageBytes protoMB = new MessageBytes();
101:
102: // remote address/host
103: private MessageBytes remoteAddrMB = new MessageBytes();
104: private MessageBytes localNameMB = new MessageBytes();
105: private MessageBytes remoteHostMB = new MessageBytes();
106: private MessageBytes localAddrMB = new MessageBytes();
107:
108: private MimeHeaders headers = new MimeHeaders();
109:
110: private MessageBytes instanceId = new MessageBytes();
111:
112: /**
113: * Notes.
114: */
115: private Object notes[] = new Object[Constants.MAX_NOTES];
116:
117: /**
118: * Associated input buffer.
119: */
120: private InputBuffer inputBuffer = null;
121:
122: /**
123: * URL decoder.
124: */
125: private UDecoder urlDecoder = new UDecoder();
126:
127: /**
128: * HTTP specific fields. (remove them ?)
129: */
130: private long contentLength = -1;
131: private MessageBytes contentTypeMB = null;
132: private String charEncoding = null;
133: private Cookies cookies = new Cookies(headers);
134: private Parameters parameters = new Parameters();
135:
136: private MessageBytes remoteUser = new MessageBytes();
137: private MessageBytes authType = new MessageBytes();
138: private HashMap attributes = new HashMap();
139:
140: private Response response;
141: private ActionHook hook;
142:
143: private int bytesRead = 0;
144: // Time of the request - usefull to avoid repeated calls to System.currentTime
145: private long startTime = 0L;
146:
147: private RequestInfo reqProcessorMX = new RequestInfo(this );
148:
149: // ------------------------------------------------------------- Properties
150:
151: /**
152: * Get the instance id (or JVM route). Curently Ajp is sending it with each
153: * request. In future this should be fixed, and sent only once ( or
154: * 'negociated' at config time so both tomcat and apache share the same name.
155: *
156: * @return the instance id
157: */
158: public MessageBytes instanceId() {
159: return instanceId;
160: }
161:
162: public MimeHeaders getMimeHeaders() {
163: return headers;
164: }
165:
166: public UDecoder getURLDecoder() {
167: return urlDecoder;
168: }
169:
170: // -------------------- Request data --------------------
171:
172: public MessageBytes scheme() {
173: return schemeMB;
174: }
175:
176: public MessageBytes method() {
177: return methodMB;
178: }
179:
180: public MessageBytes unparsedURI() {
181: return unparsedURIMB;
182: }
183:
184: public MessageBytes requestURI() {
185: return uriMB;
186: }
187:
188: public MessageBytes decodedURI() {
189: return decodedUriMB;
190: }
191:
192: public MessageBytes query() {
193: return queryMB;
194: }
195:
196: public MessageBytes queryString() {
197: return queryMB;
198: }
199:
200: public MessageBytes protocol() {
201: return protoMB;
202: }
203:
204: /**
205: * Return the buffer holding the server name, if
206: * any. Use isNull() to check if there is no value
207: * set.
208: * This is the "virtual host", derived from the
209: * Host: header.
210: */
211: public MessageBytes serverName() {
212: return serverNameMB;
213: }
214:
215: public int getServerPort() {
216: return serverPort;
217: }
218:
219: public void setServerPort(int serverPort) {
220: this .serverPort = serverPort;
221: }
222:
223: public MessageBytes remoteAddr() {
224: return remoteAddrMB;
225: }
226:
227: public MessageBytes remoteHost() {
228: return remoteHostMB;
229: }
230:
231: public MessageBytes localName() {
232: return localNameMB;
233: }
234:
235: public MessageBytes localAddr() {
236: return localAddrMB;
237: }
238:
239: public String getLocalHost() {
240: return localHost;
241: }
242:
243: public void setLocalHost(String host) {
244: this .localHost = host;
245: }
246:
247: public int getRemotePort() {
248: return remotePort;
249: }
250:
251: public void setRemotePort(int port) {
252: this .remotePort = port;
253: }
254:
255: public int getLocalPort() {
256: return localPort;
257: }
258:
259: public void setLocalPort(int port) {
260: this .localPort = port;
261: }
262:
263: // -------------------- encoding/type --------------------
264:
265: /**
266: * Get the character encoding used for this request.
267: */
268: public String getCharacterEncoding() {
269:
270: if (charEncoding != null)
271: return charEncoding;
272:
273: charEncoding = ContentType
274: .getCharsetFromContentType(getContentType());
275: return charEncoding;
276:
277: }
278:
279: public void setCharacterEncoding(String enc) {
280: this .charEncoding = enc;
281: }
282:
283: public void setContentLength(int len) {
284: this .contentLength = len;
285: }
286:
287: public int getContentLength() {
288: long length = getContentLengthLong();
289:
290: if (length < Integer.MAX_VALUE) {
291: return (int) length;
292: }
293: return -1;
294: }
295:
296: public long getContentLengthLong() {
297: if (contentLength > -1)
298: return contentLength;
299:
300: MessageBytes clB = headers.getValue("content-length");
301: contentLength = (clB == null || clB.isNull()) ? -1 : clB
302: .getLong();
303:
304: return contentLength;
305: }
306:
307: public String getContentType() {
308: contentType();
309: if ((contentTypeMB == null) || contentTypeMB.isNull())
310: return null;
311: return contentTypeMB.toString();
312: }
313:
314: public void setContentType(String type) {
315: contentTypeMB.setString(type);
316: }
317:
318: public MessageBytes contentType() {
319: if (contentTypeMB == null)
320: contentTypeMB = headers.getValue("content-type");
321: return contentTypeMB;
322: }
323:
324: public void setContentType(MessageBytes mb) {
325: contentTypeMB = mb;
326: }
327:
328: public String getHeader(String name) {
329: return headers.getHeader(name);
330: }
331:
332: // -------------------- Associated response --------------------
333:
334: public Response getResponse() {
335: return response;
336: }
337:
338: public void setResponse(Response response) {
339: this .response = response;
340: response.setRequest(this );
341: }
342:
343: public void action(ActionCode actionCode, Object param) {
344: if (hook == null && response != null)
345: hook = response.getHook();
346:
347: if (hook != null) {
348: if (param == null)
349: hook.action(actionCode, this );
350: else
351: hook.action(actionCode, param);
352: }
353: }
354:
355: // -------------------- Cookies --------------------
356:
357: public Cookies getCookies() {
358: return cookies;
359: }
360:
361: // -------------------- Parameters --------------------
362:
363: public Parameters getParameters() {
364: return parameters;
365: }
366:
367: // -------------------- Other attributes --------------------
368: // We can use notes for most - need to discuss what is of general interest
369:
370: public void setAttribute(String name, Object o) {
371: attributes.put(name, o);
372: }
373:
374: public HashMap getAttributes() {
375: return attributes;
376: }
377:
378: public Object getAttribute(String name) {
379: return attributes.get(name);
380: }
381:
382: public MessageBytes getRemoteUser() {
383: return remoteUser;
384: }
385:
386: public MessageBytes getAuthType() {
387: return authType;
388: }
389:
390: // -------------------- Input Buffer --------------------
391:
392: public InputBuffer getInputBuffer() {
393: return inputBuffer;
394: }
395:
396: public void setInputBuffer(InputBuffer inputBuffer) {
397: this .inputBuffer = inputBuffer;
398: }
399:
400: /**
401: * Read data from the input buffer and put it into a byte chunk.
402: */
403: public int doRead(ByteChunk chunk) throws IOException {
404: int n = inputBuffer.doRead(chunk, this );
405: if (n > 0) {
406: bytesRead += n;
407: }
408: return n;
409: }
410:
411: // -------------------- debug --------------------
412:
413: public String toString() {
414: return "R( " + requestURI().toString() + ")";
415: }
416:
417: public long getStartTime() {
418: return startTime;
419: }
420:
421: public void setStartTime(long startTime) {
422: this .startTime = startTime;
423: }
424:
425: // -------------------- Per-Request "notes" --------------------
426:
427: public final void setNote(int pos, Object value) {
428: notes[pos] = value;
429: }
430:
431: public final Object getNote(int pos) {
432: return notes[pos];
433: }
434:
435: // -------------------- Recycling --------------------
436:
437: public void recycle() {
438: bytesRead = 0;
439:
440: contentLength = -1;
441: contentTypeMB = null;
442: charEncoding = null;
443: headers.recycle();
444: serverNameMB.recycle();
445: serverPort = -1;
446: localPort = -1;
447: remotePort = -1;
448:
449: cookies.recycle();
450: parameters.recycle();
451:
452: unparsedURIMB.recycle();
453: uriMB.recycle();
454: decodedUriMB.recycle();
455: queryMB.recycle();
456: methodMB.recycle();
457: protoMB.recycle();
458: //remoteAddrMB.recycle();
459: //remoteHostMB.recycle();
460:
461: // XXX Do we need such defaults ?
462: schemeMB.recycle();
463: methodMB.setString("GET");
464: uriMB.setString("/");
465: queryMB.setString("");
466: protoMB.setString("HTTP/1.0");
467: //remoteAddrMB.setString("127.0.0.1");
468: //remoteHostMB.setString("localhost");
469:
470: instanceId.recycle();
471: remoteUser.recycle();
472: authType.recycle();
473: attributes.clear();
474: }
475:
476: // -------------------- Info --------------------
477: public void updateCounters() {
478: reqProcessorMX.updateCounters();
479: }
480:
481: public RequestInfo getRequestProcessor() {
482: return reqProcessorMX;
483: }
484:
485: public int getBytesRead() {
486: return bytesRead;
487: }
488:
489: public void setBytesRead(int bytesRead) {
490: this.bytesRead = bytesRead;
491: }
492: }
|