1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.coyote;
19
20 import java.io.IOException;
21 import java.util.HashMap;
22
23 import org.apache.tomcat.util.buf.ByteChunk;
24 import org.apache.tomcat.util.buf.MessageBytes;
25 import org.apache.tomcat.util.buf.UDecoder;
26
27 import org.apache.tomcat.util.http.MimeHeaders;
28 import org.apache.tomcat.util.http.Parameters;
29 import org.apache.tomcat.util.http.ContentType;
30 import org.apache.tomcat.util.http.Cookies;
31
32 /**
33 * This is a low-level, efficient representation of a server request. Most
34 * fields are GC-free, expensive operations are delayed until the user code
35 * needs the information.
36 *
37 * Processing is delegated to modules, using a hook mechanism.
38 *
39 * This class is not intended for user code - it is used internally by tomcat
40 * for processing the request in the most efficient way. Users ( servlets ) can
41 * access the information using a facade, which provides the high-level view
42 * of the request.
43 *
44 * For lazy evaluation, the request uses the getInfo() hook. The following ids
45 * are defined:
46 * <ul>
47 * <li>req.encoding - returns the request encoding
48 * <li>req.attribute - returns a module-specific attribute ( like SSL keys, etc ).
49 * </ul>
50 *
51 * Tomcat defines a number of attributes:
52 * <ul>
53 * <li>"org.apache.tomcat.request" - allows access to the low-level
54 * request object in trusted applications
55 * </ul>
56 *
57 * @author James Duncan Davidson [duncan@eng.sun.com]
58 * @author James Todd [gonzo@eng.sun.com]
59 * @author Jason Hunter [jch@eng.sun.com]
60 * @author Harish Prabandham
61 * @author Alex Cruikshank [alex@epitonic.com]
62 * @author Hans Bergsten [hans@gefionsoftware.com]
63 * @author Costin Manolache
64 * @author Remy Maucherat
65 */
66 public final class Request {
67
68
69 // ----------------------------------------------------------- Constructors
70
71
72 public Request() {
73
74 parameters.setQuery(queryMB);
75 parameters.setURLDecoder(urlDecoder);
76 parameters.setHeaders(headers);
77
78 }
79
80
81 // ----------------------------------------------------- Instance Variables
82
83
84 private int serverPort = -1;
85 private MessageBytes serverNameMB = MessageBytes.newInstance();
86
87 private int remotePort;
88 private int localPort;
89
90 private MessageBytes schemeMB = MessageBytes.newInstance();
91
92 private MessageBytes methodMB = MessageBytes.newInstance();
93 private MessageBytes unparsedURIMB = MessageBytes.newInstance();
94 private MessageBytes uriMB = MessageBytes.newInstance();
95 private MessageBytes decodedUriMB = MessageBytes.newInstance();
96 private MessageBytes queryMB = MessageBytes.newInstance();
97 private MessageBytes protoMB = MessageBytes.newInstance();
98
99 // remote address/host
100 private MessageBytes remoteAddrMB = MessageBytes.newInstance();
101 private MessageBytes localNameMB = MessageBytes.newInstance();
102 private MessageBytes remoteHostMB = MessageBytes.newInstance();
103 private MessageBytes localAddrMB = MessageBytes.newInstance();
104
105 private MimeHeaders headers = new MimeHeaders();
106
107 private MessageBytes instanceId = MessageBytes.newInstance();
108
109 /**
110 * Notes.
111 */
112 private Object notes[] = new Object[Constants.MAX_NOTES];
113
114
115 /**
116 * Associated input buffer.
117 */
118 private InputBuffer inputBuffer = null;
119
120
121 /**
122 * URL decoder.
123 */
124 private UDecoder urlDecoder = new UDecoder();
125
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=MessageBytes.newInstance();
137 private MessageBytes authType=MessageBytes.newInstance();
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 private int available = 0;
147
148 private RequestInfo reqProcessorMX=new RequestInfo(this);
149 // ------------------------------------------------------------- Properties
150
151
152 /**
153 * Get the instance id (or JVM route). Curently Ajp is sending it with each
154 * request. In future this should be fixed, and sent only once ( or
155 * 'negociated' at config time so both tomcat and apache share the same name.
156 *
157 * @return the instance id
158 */
159 public MessageBytes instanceId() {
160 return instanceId;
161 }
162
163
164 public MimeHeaders getMimeHeaders() {
165 return headers;
166 }
167
168
169 public UDecoder getURLDecoder() {
170 return urlDecoder;
171 }
172
173 // -------------------- Request data --------------------
174
175
176 public MessageBytes scheme() {
177 return schemeMB;
178 }
179
180 public MessageBytes method() {
181 return methodMB;
182 }
183
184 public MessageBytes unparsedURI() {
185 return unparsedURIMB;
186 }
187
188 public MessageBytes requestURI() {
189 return uriMB;
190 }
191
192 public MessageBytes decodedURI() {
193 return decodedUriMB;
194 }
195
196 public MessageBytes query() {
197 return queryMB;
198 }
199
200 public MessageBytes queryString() {
201 return queryMB;
202 }
203
204 public MessageBytes protocol() {
205 return protoMB;
206 }
207
208 /**
209 * Return the buffer holding the server name, if
210 * any. Use isNull() to check if there is no value
211 * set.
212 * This is the "virtual host", derived from the
213 * Host: header.
214 */
215 public MessageBytes serverName() {
216 return serverNameMB;
217 }
218
219 public int getServerPort() {
220 return serverPort;
221 }
222
223 public void setServerPort(int serverPort ) {
224 this.serverPort=serverPort;
225 }
226
227 public MessageBytes remoteAddr() {
228 return remoteAddrMB;
229 }
230
231 public MessageBytes remoteHost() {
232 return remoteHostMB;
233 }
234
235 public MessageBytes localName() {
236 return localNameMB;
237 }
238
239 public MessageBytes localAddr() {
240 return localAddrMB;
241 }
242
243 public int getRemotePort(){
244 return remotePort;
245 }
246
247 public void setRemotePort(int port){
248 this.remotePort = port;
249 }
250
251 public int getLocalPort(){
252 return localPort;
253 }
254
255 public void setLocalPort(int port){
256 this.localPort = port;
257 }
258
259 // -------------------- encoding/type --------------------
260
261
262 /**
263 * Get the character encoding used for this request.
264 */
265 public String getCharacterEncoding() {
266
267 if (charEncoding != null)
268 return charEncoding;
269
270 charEncoding = ContentType.getCharsetFromContentType(getContentType());
271 return charEncoding;
272
273 }
274
275
276 public void setCharacterEncoding(String enc) {
277 this.charEncoding = enc;
278 }
279
280
281 public void setContentLength(int len) {
282 this.contentLength = len;
283 }
284
285
286 public int getContentLength() {
287 long length = getContentLengthLong();
288
289 if (length < Integer.MAX_VALUE) {
290 return (int) length;
291 }
292 return -1;
293 }
294
295 public long getContentLengthLong() {
296 if( contentLength > -1 ) return contentLength;
297
298 MessageBytes clB = headers.getUniqueValue("content-length");
299 contentLength = (clB == null || clB.isNull()) ? -1 : clB.getLong();
300
301 return contentLength;
302 }
303
304 public String getContentType() {
305 contentType();
306 if ((contentTypeMB == null) || contentTypeMB.isNull())
307 return null;
308 return contentTypeMB.toString();
309 }
310
311
312 public void setContentType(String type) {
313 contentTypeMB.setString(type);
314 }
315
316
317 public MessageBytes contentType() {
318 if (contentTypeMB == null)
319 contentTypeMB = headers.getValue("content-type");
320 return contentTypeMB;
321 }
322
323
324 public void setContentType(MessageBytes mb) {
325 contentTypeMB=mb;
326 }
327
328
329 public String getHeader(String name) {
330 return headers.getHeader(name);
331 }
332
333 // -------------------- Associated response --------------------
334
335 public Response getResponse() {
336 return response;
337 }
338
339 public void setResponse( Response response ) {
340 this.response=response;
341 response.setRequest( this );
342 }
343
344 public void action(ActionCode actionCode, Object param) {
345 if( hook==null && response!=null )
346 hook=response.getHook();
347
348 if (hook != null) {
349 if( param==null )
350 hook.action(actionCode, this);
351 else
352 hook.action(actionCode, param);
353 }
354 }
355
356
357 // -------------------- Cookies --------------------
358
359
360 public Cookies getCookies() {
361 return cookies;
362 }
363
364
365 // -------------------- Parameters --------------------
366
367
368 public Parameters getParameters() {
369 return parameters;
370 }
371
372
373 // -------------------- Other attributes --------------------
374 // We can use notes for most - need to discuss what is of general interest
375
376 public void setAttribute( String name, Object o ) {
377 attributes.put( name, o );
378 }
379
380 public HashMap getAttributes() {
381 return attributes;
382 }
383
384 public Object getAttribute(String name ) {
385 return attributes.get(name);
386 }
387
388 public MessageBytes getRemoteUser() {
389 return remoteUser;
390 }
391
392 public MessageBytes getAuthType() {
393 return authType;
394 }
395
396 public int getAvailable() {
397 return available;
398 }
399
400 public void setAvailable(int available) {
401 this.available = available;
402 }
403
404 // -------------------- Input Buffer --------------------
405
406
407 public InputBuffer getInputBuffer() {
408 return inputBuffer;
409 }
410
411
412 public void setInputBuffer(InputBuffer inputBuffer) {
413 this.inputBuffer = inputBuffer;
414 }
415
416
417 /**
418 * Read data from the input buffer and put it into a byte chunk.
419 *
420 * The buffer is owned by the protocol implementation - it will be reused on the next read.
421 * The Adapter must either process the data in place or copy it to a separate buffer if it needs
422 * to hold it. In most cases this is done during byte->char conversions or via InputStream. Unlike
423 * InputStream, this interface allows the app to process data in place, without copy.
424 *
425 */
426 public int doRead(ByteChunk chunk)
427 throws IOException {
428 int n = inputBuffer.doRead(chunk, this);
429 if (n > 0) {
430 bytesRead+=n;
431 }
432 return n;
433 }
434
435
436 // -------------------- debug --------------------
437
438 public String toString() {
439 return "R( " + requestURI().toString() + ")";
440 }
441
442 public long getStartTime() {
443 return startTime;
444 }
445
446 public void setStartTime(long startTime) {
447 this.startTime = startTime;
448 }
449
450 // -------------------- Per-Request "notes" --------------------
451
452
453 /**
454 * Used to store private data. Thread data could be used instead - but
455 * if you have the req, getting/setting a note is just a array access, may
456 * be faster than ThreadLocal for very frequent operations.
457 *
458 * Example use:
459 * Jk:
460 * HandlerRequest.HOSTBUFFER = 10 CharChunk, buffer for Host decoding
461 * WorkerEnv: SSL_CERT_NOTE=16 - MessageBytes containing the cert
462 *
463 * Catalina CoyoteAdapter:
464 * ADAPTER_NOTES = 1 - stores the HttpServletRequest object ( req/res)
465 *
466 * To avoid conflicts, note in the range 0 - 8 are reserved for the
467 * servlet container ( catalina connector, etc ), and values in 9 - 16
468 * for connector use.
469 *
470 * 17-31 range is not allocated or used.
471 */
472 public final void setNote(int pos, Object value) {
473 notes[pos] = value;
474 }
475
476
477 public final Object getNote(int pos) {
478 return notes[pos];
479 }
480
481
482 // -------------------- Recycling --------------------
483
484
485 public void recycle() {
486 bytesRead=0;
487
488 contentLength = -1;
489 contentTypeMB = null;
490 charEncoding = null;
491 headers.recycle();
492 serverNameMB.recycle();
493 serverPort=-1;
494 localPort = -1;
495 remotePort = -1;
496 available = 0;
497
498 cookies.recycle();
499 parameters.recycle();
500
501 unparsedURIMB.recycle();
502 uriMB.recycle();
503 decodedUriMB.recycle();
504 queryMB.recycle();
505 methodMB.recycle();
506 protoMB.recycle();
507
508 schemeMB.recycle();
509
510 instanceId.recycle();
511 remoteUser.recycle();
512 authType.recycle();
513 attributes.clear();
514 }
515
516 // -------------------- Info --------------------
517 public void updateCounters() {
518 reqProcessorMX.updateCounters();
519 }
520
521 public RequestInfo getRequestProcessor() {
522 return reqProcessorMX;
523 }
524
525 public int getBytesRead() {
526 return bytesRead;
527 }
528
529 public void setBytesRead(int bytesRead) {
530 this.bytesRead = bytesRead;
531 }
532 }