001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.coyote;
019:
020: import java.io.IOException;
021: import java.util.HashMap;
022:
023: import org.apache.tomcat.util.buf.ByteChunk;
024: import org.apache.tomcat.util.buf.MessageBytes;
025: import org.apache.tomcat.util.buf.UDecoder;
026:
027: import org.apache.tomcat.util.http.MimeHeaders;
028: import org.apache.tomcat.util.http.Parameters;
029: import org.apache.tomcat.util.http.ContentType;
030: import org.apache.tomcat.util.http.Cookies;
031:
032: /**
033: * This is a low-level, efficient representation of a server request. Most
034: * fields are GC-free, expensive operations are delayed until the user code
035: * needs the information.
036: *
037: * Processing is delegated to modules, using a hook mechanism.
038: *
039: * This class is not intended for user code - it is used internally by tomcat
040: * for processing the request in the most efficient way. Users ( servlets ) can
041: * access the information using a facade, which provides the high-level view
042: * of the request.
043: *
044: * For lazy evaluation, the request uses the getInfo() hook. The following ids
045: * are defined:
046: * <ul>
047: * <li>req.encoding - returns the request encoding
048: * <li>req.attribute - returns a module-specific attribute ( like SSL keys, etc ).
049: * </ul>
050: *
051: * Tomcat defines a number of attributes:
052: * <ul>
053: * <li>"org.apache.tomcat.request" - allows access to the low-level
054: * request object in trusted applications
055: * </ul>
056: *
057: * @author James Duncan Davidson [duncan@eng.sun.com]
058: * @author James Todd [gonzo@eng.sun.com]
059: * @author Jason Hunter [jch@eng.sun.com]
060: * @author Harish Prabandham
061: * @author Alex Cruikshank [alex@epitonic.com]
062: * @author Hans Bergsten [hans@gefionsoftware.com]
063: * @author Costin Manolache
064: * @author Remy Maucherat
065: */
066: public final class Request {
067:
068: // ----------------------------------------------------------- Constructors
069:
070: public Request() {
071:
072: parameters.setQuery(queryMB);
073: parameters.setURLDecoder(urlDecoder);
074: parameters.setHeaders(headers);
075:
076: }
077:
078: // ----------------------------------------------------- Instance Variables
079:
080: private int serverPort = -1;
081: private MessageBytes serverNameMB = MessageBytes.newInstance();
082:
083: private int remotePort;
084: private int localPort;
085:
086: private MessageBytes schemeMB = MessageBytes.newInstance();
087:
088: private MessageBytes methodMB = MessageBytes.newInstance();
089: private MessageBytes unparsedURIMB = MessageBytes.newInstance();
090: private MessageBytes uriMB = MessageBytes.newInstance();
091: private MessageBytes decodedUriMB = MessageBytes.newInstance();
092: private MessageBytes queryMB = MessageBytes.newInstance();
093: private MessageBytes protoMB = MessageBytes.newInstance();
094:
095: // remote address/host
096: private MessageBytes remoteAddrMB = MessageBytes.newInstance();
097: private MessageBytes localNameMB = MessageBytes.newInstance();
098: private MessageBytes remoteHostMB = MessageBytes.newInstance();
099: private MessageBytes localAddrMB = MessageBytes.newInstance();
100:
101: private MimeHeaders headers = new MimeHeaders();
102:
103: private MessageBytes instanceId = MessageBytes.newInstance();
104:
105: /**
106: * Notes.
107: */
108: private Object notes[] = new Object[Constants.MAX_NOTES];
109:
110: /**
111: * Associated input buffer.
112: */
113: private InputBuffer inputBuffer = null;
114:
115: /**
116: * URL decoder.
117: */
118: private UDecoder urlDecoder = new UDecoder();
119:
120: /**
121: * HTTP specific fields. (remove them ?)
122: */
123: private long contentLength = -1;
124: private MessageBytes contentTypeMB = null;
125: private String charEncoding = null;
126: private Cookies cookies = new Cookies(headers);
127: private Parameters parameters = new Parameters();
128:
129: private MessageBytes remoteUser = MessageBytes.newInstance();
130: private MessageBytes authType = MessageBytes.newInstance();
131: private HashMap attributes = new HashMap();
132:
133: private Response response;
134: private ActionHook hook;
135:
136: private int bytesRead = 0;
137: // Time of the request - usefull to avoid repeated calls to System.currentTime
138: private long startTime = 0L;
139: private int available = 0;
140:
141: private RequestInfo reqProcessorMX = new RequestInfo(this );
142:
143: // ------------------------------------------------------------- Properties
144:
145: /**
146: * Get the instance id (or JVM route). Curently Ajp is sending it with each
147: * request. In future this should be fixed, and sent only once ( or
148: * 'negociated' at config time so both tomcat and apache share the same name.
149: *
150: * @return the instance id
151: */
152: public MessageBytes instanceId() {
153: return instanceId;
154: }
155:
156: public MimeHeaders getMimeHeaders() {
157: return headers;
158: }
159:
160: public UDecoder getURLDecoder() {
161: return urlDecoder;
162: }
163:
164: // -------------------- Request data --------------------
165:
166: public MessageBytes scheme() {
167: return schemeMB;
168: }
169:
170: public MessageBytes method() {
171: return methodMB;
172: }
173:
174: public MessageBytes unparsedURI() {
175: return unparsedURIMB;
176: }
177:
178: public MessageBytes requestURI() {
179: return uriMB;
180: }
181:
182: public MessageBytes decodedURI() {
183: return decodedUriMB;
184: }
185:
186: public MessageBytes query() {
187: return queryMB;
188: }
189:
190: public MessageBytes queryString() {
191: return queryMB;
192: }
193:
194: public MessageBytes protocol() {
195: return protoMB;
196: }
197:
198: /**
199: * Return the buffer holding the server name, if
200: * any. Use isNull() to check if there is no value
201: * set.
202: * This is the "virtual host", derived from the
203: * Host: header.
204: */
205: public MessageBytes serverName() {
206: return serverNameMB;
207: }
208:
209: public int getServerPort() {
210: return serverPort;
211: }
212:
213: public void setServerPort(int serverPort) {
214: this .serverPort = serverPort;
215: }
216:
217: public MessageBytes remoteAddr() {
218: return remoteAddrMB;
219: }
220:
221: public MessageBytes remoteHost() {
222: return remoteHostMB;
223: }
224:
225: public MessageBytes localName() {
226: return localNameMB;
227: }
228:
229: public MessageBytes localAddr() {
230: return localAddrMB;
231: }
232:
233: public int getRemotePort() {
234: return remotePort;
235: }
236:
237: public void setRemotePort(int port) {
238: this .remotePort = port;
239: }
240:
241: public int getLocalPort() {
242: return localPort;
243: }
244:
245: public void setLocalPort(int port) {
246: this .localPort = port;
247: }
248:
249: // -------------------- encoding/type --------------------
250:
251: /**
252: * Get the character encoding used for this request.
253: */
254: public String getCharacterEncoding() {
255:
256: if (charEncoding != null)
257: return charEncoding;
258:
259: charEncoding = ContentType
260: .getCharsetFromContentType(getContentType());
261: return charEncoding;
262:
263: }
264:
265: public void setCharacterEncoding(String enc) {
266: this .charEncoding = enc;
267: }
268:
269: public void setContentLength(int len) {
270: this .contentLength = len;
271: }
272:
273: public int getContentLength() {
274: long length = getContentLengthLong();
275:
276: if (length < Integer.MAX_VALUE) {
277: return (int) length;
278: }
279: return -1;
280: }
281:
282: public long getContentLengthLong() {
283: if (contentLength > -1)
284: return contentLength;
285:
286: MessageBytes clB = headers.getUniqueValue("content-length");
287: contentLength = (clB == null || clB.isNull()) ? -1 : clB
288: .getLong();
289:
290: return contentLength;
291: }
292:
293: public String getContentType() {
294: contentType();
295: if ((contentTypeMB == null) || contentTypeMB.isNull())
296: return null;
297: return contentTypeMB.toString();
298: }
299:
300: public void setContentType(String type) {
301: contentTypeMB.setString(type);
302: }
303:
304: public MessageBytes contentType() {
305: if (contentTypeMB == null)
306: contentTypeMB = headers.getValue("content-type");
307: return contentTypeMB;
308: }
309:
310: public void setContentType(MessageBytes mb) {
311: contentTypeMB = mb;
312: }
313:
314: public String getHeader(String name) {
315: return headers.getHeader(name);
316: }
317:
318: // -------------------- Associated response --------------------
319:
320: public Response getResponse() {
321: return response;
322: }
323:
324: public void setResponse(Response response) {
325: this .response = response;
326: response.setRequest(this );
327: }
328:
329: public void action(ActionCode actionCode, Object param) {
330: if (hook == null && response != null)
331: hook = response.getHook();
332:
333: if (hook != null) {
334: if (param == null)
335: hook.action(actionCode, this );
336: else
337: hook.action(actionCode, param);
338: }
339: }
340:
341: // -------------------- Cookies --------------------
342:
343: public Cookies getCookies() {
344: return cookies;
345: }
346:
347: // -------------------- Parameters --------------------
348:
349: public Parameters getParameters() {
350: return parameters;
351: }
352:
353: // -------------------- Other attributes --------------------
354: // We can use notes for most - need to discuss what is of general interest
355:
356: public void setAttribute(String name, Object o) {
357: attributes.put(name, o);
358: }
359:
360: public HashMap getAttributes() {
361: return attributes;
362: }
363:
364: public Object getAttribute(String name) {
365: return attributes.get(name);
366: }
367:
368: public MessageBytes getRemoteUser() {
369: return remoteUser;
370: }
371:
372: public MessageBytes getAuthType() {
373: return authType;
374: }
375:
376: public int getAvailable() {
377: return available;
378: }
379:
380: public void setAvailable(int available) {
381: this .available = available;
382: }
383:
384: // -------------------- Input Buffer --------------------
385:
386: public InputBuffer getInputBuffer() {
387: return inputBuffer;
388: }
389:
390: public void setInputBuffer(InputBuffer inputBuffer) {
391: this .inputBuffer = inputBuffer;
392: }
393:
394: /**
395: * Read data from the input buffer and put it into a byte chunk.
396: *
397: * The buffer is owned by the protocol implementation - it will be reused on the next read.
398: * The Adapter must either process the data in place or copy it to a separate buffer if it needs
399: * to hold it. In most cases this is done during byte->char conversions or via InputStream. Unlike
400: * InputStream, this interface allows the app to process data in place, without copy.
401: *
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: /**
428: * Used to store private data. Thread data could be used instead - but
429: * if you have the req, getting/setting a note is just a array access, may
430: * be faster than ThreadLocal for very frequent operations.
431: *
432: * Example use:
433: * Jk:
434: * HandlerRequest.HOSTBUFFER = 10 CharChunk, buffer for Host decoding
435: * WorkerEnv: SSL_CERT_NOTE=16 - MessageBytes containing the cert
436: *
437: * Catalina CoyoteAdapter:
438: * ADAPTER_NOTES = 1 - stores the HttpServletRequest object ( req/res)
439: *
440: * To avoid conflicts, note in the range 0 - 8 are reserved for the
441: * servlet container ( catalina connector, etc ), and values in 9 - 16
442: * for connector use.
443: *
444: * 17-31 range is not allocated or used.
445: */
446: public final void setNote(int pos, Object value) {
447: notes[pos] = value;
448: }
449:
450: public final Object getNote(int pos) {
451: return notes[pos];
452: }
453:
454: // -------------------- Recycling --------------------
455:
456: public void recycle() {
457: bytesRead = 0;
458:
459: contentLength = -1;
460: contentTypeMB = null;
461: charEncoding = null;
462: headers.recycle();
463: serverNameMB.recycle();
464: serverPort = -1;
465: localPort = -1;
466: remotePort = -1;
467: available = 0;
468:
469: cookies.recycle();
470: parameters.recycle();
471:
472: unparsedURIMB.recycle();
473: uriMB.recycle();
474: decodedUriMB.recycle();
475: queryMB.recycle();
476: methodMB.recycle();
477: protoMB.recycle();
478:
479: schemeMB.recycle();
480:
481: instanceId.recycle();
482: remoteUser.recycle();
483: authType.recycle();
484: attributes.clear();
485: }
486:
487: // -------------------- Info --------------------
488: public void updateCounters() {
489: reqProcessorMX.updateCounters();
490: }
491:
492: public RequestInfo getRequestProcessor() {
493: return reqProcessorMX;
494: }
495:
496: public int getBytesRead() {
497: return bytesRead;
498: }
499:
500: public void setBytesRead(int bytesRead) {
501: this.bytesRead = bytesRead;
502: }
503: }
|