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 package org.apache.coyote;
18
19 import java.io.IOException;
20 import java.io.UnsupportedEncodingException;
21 import java.nio.charset.Charset;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.concurrent.atomic.AtomicBoolean;
25
26 import javax.servlet.ReadListener;
27
28 import org.apache.tomcat.util.buf.B2CConverter;
29 import org.apache.tomcat.util.buf.MessageBytes;
30 import org.apache.tomcat.util.buf.UDecoder;
31 import org.apache.tomcat.util.http.MimeHeaders;
32 import org.apache.tomcat.util.http.Parameters;
33 import org.apache.tomcat.util.http.ServerCookies;
34 import org.apache.tomcat.util.net.ApplicationBufferHandler;
35 import org.apache.tomcat.util.res.StringManager;
36
37 /**
38  * This is a low-level, efficient representation of a server request. Most
39  * fields are GC-free, expensive operations are delayed until the  user code
40  * needs the information.
41  *
42  * Processing is delegated to modules, using a hook mechanism.
43  *
44  * This class is not intended for user code - it is used internally by tomcat
45  * for processing the request in the most efficient way. Users ( servlets ) can
46  * access the information using a facade, which provides the high-level view
47  * of the request.
48  *
49  * Tomcat defines a number of attributes:
50  * <ul>
51  *   <li>"org.apache.tomcat.request" - allows access to the low-level
52  *       request object in trusted applications
53  * </ul>
54  *
55  * @author James Duncan Davidson [duncan@eng.sun.com]
56  * @author James Todd [gonzo@eng.sun.com]
57  * @author Jason Hunter [jch@eng.sun.com]
58  * @author Harish Prabandham
59  * @author Alex Cruikshank [alex@epitonic.com]
60  * @author Hans Bergsten [hans@gefionsoftware.com]
61  * @author Costin Manolache
62  * @author Remy Maucherat
63  */

64 public final class Request {
65
66     private static final StringManager sm = StringManager.getManager(Request.class);
67
68     // Expected maximum typical number of cookies per request.
69     private static final int INITIAL_COOKIE_SIZE = 4;
70
71     // ----------------------------------------------------------- Constructors
72
73     public Request() {
74         parameters.setQuery(queryMB);
75         parameters.setURLDecoder(urlDecoder);
76     }
77
78
79     // ----------------------------------------------------- Instance Variables
80
81     private int serverPort = -1;
82     private final MessageBytes serverNameMB = MessageBytes.newInstance();
83
84     private int remotePort;
85     private int localPort;
86
87     private final MessageBytes schemeMB = MessageBytes.newInstance();
88
89     private final MessageBytes methodMB = MessageBytes.newInstance();
90     private final MessageBytes uriMB = MessageBytes.newInstance();
91     private final MessageBytes decodedUriMB = MessageBytes.newInstance();
92     private final MessageBytes queryMB = MessageBytes.newInstance();
93     private final MessageBytes protoMB = MessageBytes.newInstance();
94
95     // remote address/host
96     private final MessageBytes remoteAddrMB = MessageBytes.newInstance();
97     private final MessageBytes localNameMB = MessageBytes.newInstance();
98     private final MessageBytes remoteHostMB = MessageBytes.newInstance();
99     private final MessageBytes localAddrMB = MessageBytes.newInstance();
100
101     private final MimeHeaders headers = new MimeHeaders();
102     private final Map<String,String> trailerFields = new HashMap<>();
103
104
105     /**
106      * Path parameters
107      */

108     private final Map<String,String> pathParameters = new HashMap<>();
109
110     /**
111      * Notes.
112      */

113     private final Object notes[] = new Object[Constants.MAX_NOTES];
114
115
116     /**
117      * Associated input buffer.
118      */

119     private InputBuffer inputBuffer = null;
120
121
122     /**
123      * URL decoder.
124      */

125     private final UDecoder urlDecoder = new UDecoder();
126
127
128     /**
129      * HTTP specific fields. (remove them ?)
130      */

131     private long contentLength = -1;
132     private MessageBytes contentTypeMB = null;
133     private Charset charset = null;
134     // Retain the original, user specified character encoding so it can be
135     // returned even if it is invalid
136     private String characterEncoding = null;
137
138     /**
139      * Is there an expectation ?
140      */

141     private boolean expectation = false;
142
143     private final ServerCookies serverCookies = new ServerCookies(INITIAL_COOKIE_SIZE);
144     private final Parameters parameters = new Parameters();
145
146     private final MessageBytes remoteUser = MessageBytes.newInstance();
147     private boolean remoteUserNeedsAuthorization = false;
148     private final MessageBytes authType = MessageBytes.newInstance();
149     private final HashMap<String,Object> attributes = new HashMap<>();
150
151     private Response response;
152     private volatile ActionHook hook;
153
154     private long bytesRead=0;
155     // Time of the request - useful to avoid repeated calls to System.currentTime
156     private long startTime = -1;
157     private int available = 0;
158
159     private final RequestInfo reqProcessorMX=new RequestInfo(this);
160
161     private boolean sendfile = true;
162
163     volatile ReadListener listener;
164
165     public ReadListener getReadListener() {
166         return listener;
167     }
168
169     public void setReadListener(ReadListener listener) {
170         if (listener == null) {
171             throw new NullPointerException(
172                     sm.getString("request.nullReadListener"));
173         }
174         if (getReadListener() != null) {
175             throw new IllegalStateException(
176                     sm.getString("request.readListenerSet"));
177         }
178         // Note: This class is not used for HTTP upgrade so only need to test
179         //       for async
180         AtomicBoolean result = new AtomicBoolean(false);
181         action(ActionCode.ASYNC_IS_ASYNC, result);
182         if (!result.get()) {
183             throw new IllegalStateException(
184                     sm.getString("request.notAsync"));
185         }
186
187         this.listener = listener;
188     }
189
190     private final AtomicBoolean allDataReadEventSent = new AtomicBoolean(false);
191
192     public boolean sendAllDataReadEvent() {
193         return allDataReadEventSent.compareAndSet(falsetrue);
194     }
195
196
197     // ------------------------------------------------------------- Properties
198
199     public MimeHeaders getMimeHeaders() {
200         return headers;
201     }
202
203
204     public boolean isTrailerFieldsReady() {
205         AtomicBoolean result = new AtomicBoolean(false);
206         action(ActionCode.IS_TRAILER_FIELDS_READY, result);
207         return result.get();
208     }
209
210
211     public Map<String,String> getTrailerFields() {
212         return trailerFields;
213     }
214
215
216     public UDecoder getURLDecoder() {
217         return urlDecoder;
218     }
219
220
221     // -------------------- Request data --------------------
222
223     public MessageBytes scheme() {
224         return schemeMB;
225     }
226
227     public MessageBytes method() {
228         return methodMB;
229     }
230
231     public MessageBytes requestURI() {
232         return uriMB;
233     }
234
235     public MessageBytes decodedURI() {
236         return decodedUriMB;
237     }
238
239     public MessageBytes queryString() {
240         return queryMB;
241     }
242
243     public MessageBytes protocol() {
244         return protoMB;
245     }
246
247     /**
248      * Get the "virtual host", derived from the Host: header associated with
249      * this request.
250      *
251      * @return The buffer holding the server name, if any. Use isNull() to check
252      *         if there is no value set.
253      */

254     public MessageBytes serverName() {
255         return serverNameMB;
256     }
257
258     public int getServerPort() {
259         return serverPort;
260     }
261
262     public void setServerPort(int serverPort ) {
263         this.serverPort=serverPort;
264     }
265
266     public MessageBytes remoteAddr() {
267         return remoteAddrMB;
268     }
269
270     public MessageBytes remoteHost() {
271         return remoteHostMB;
272     }
273
274     public MessageBytes localName() {
275         return localNameMB;
276     }
277
278     public MessageBytes localAddr() {
279         return localAddrMB;
280     }
281
282     public int getRemotePort(){
283         return remotePort;
284     }
285
286     public void setRemotePort(int port){
287         this.remotePort = port;
288     }
289
290     public int getLocalPort(){
291         return localPort;
292     }
293
294     public void setLocalPort(int port){
295         this.localPort = port;
296     }
297
298
299     // -------------------- encoding/type --------------------
300
301     /**
302      * Get the character encoding used for this request.
303      *
304      * @return The value set via {@link #setCharset(Charset)} or if no
305      *         call has been made to that method try to obtain if from the
306      *         content type.
307      */

308     public String getCharacterEncoding() {
309         if (characterEncoding == null) {
310             characterEncoding = getCharsetFromContentType(getContentType());
311         }
312
313         return characterEncoding;
314     }
315
316
317     /**
318      * Get the character encoding used for this request.
319      *
320      * @return The value set via {@link #setCharset(Charset)} or if no
321      *         call has been made to that method try to obtain if from the
322      *         content type.
323      *
324      * @throws UnsupportedEncodingException If the user agent has specified an
325      *         invalid character encoding
326      */

327     public Charset getCharset() throws UnsupportedEncodingException {
328         if (charset == null) {
329             getCharacterEncoding();
330             if (characterEncoding != null) {
331                 charset = B2CConverter.getCharset(characterEncoding);
332             }
333          }
334
335         return charset;
336     }
337
338
339     public void setCharset(Charset charset) {
340         this.charset = charset;
341         this.characterEncoding = charset.name();
342     }
343
344
345     public void setContentLength(long len) {
346         this.contentLength = len;
347     }
348
349
350     public int getContentLength() {
351         long length = getContentLengthLong();
352
353         if (length < Integer.MAX_VALUE) {
354             return (int) length;
355         }
356         return -1;
357     }
358
359     public long getContentLengthLong() {
360         if( contentLength > -1 ) {
361             return contentLength;
362         }
363
364         MessageBytes clB = headers.getUniqueValue("content-length");
365         contentLength = (clB == null || clB.isNull()) ? -1 : clB.getLong();
366
367         return contentLength;
368     }
369
370     public String getContentType() {
371         contentType();
372         if ((contentTypeMB == null) || contentTypeMB.isNull()) {
373             return null;
374         }
375         return contentTypeMB.toString();
376     }
377
378
379     public void setContentType(String type) {
380         contentTypeMB.setString(type);
381     }
382
383
384     public MessageBytes contentType() {
385         if (contentTypeMB == null) {
386             contentTypeMB = headers.getValue("content-type");
387         }
388         return contentTypeMB;
389     }
390
391
392     public void setContentType(MessageBytes mb) {
393         contentTypeMB=mb;
394     }
395
396
397     public String getHeader(String name) {
398         return headers.getHeader(name);
399     }
400
401
402     public void setExpectation(boolean expectation) {
403         this.expectation = expectation;
404     }
405
406
407     public boolean hasExpectation() {
408         return expectation;
409     }
410
411
412     // -------------------- Associated response --------------------
413
414     public Response getResponse() {
415         return response;
416     }
417
418     public void setResponse(Response response) {
419         this.response = response;
420         response.setRequest(this);
421     }
422
423     protected void setHook(ActionHook hook) {
424         this.hook = hook;
425     }
426
427     public void action(ActionCode actionCode, Object param) {
428         if (hook != null) {
429             if (param == null) {
430                 hook.action(actionCode, this);
431             } else {
432                 hook.action(actionCode, param);
433             }
434         }
435     }
436
437
438     // -------------------- Cookies --------------------
439
440     public ServerCookies getCookies() {
441         return serverCookies;
442     }
443
444
445     // -------------------- Parameters --------------------
446
447     public Parameters getParameters() {
448         return parameters;
449     }
450
451
452     public void addPathParameter(String name, String value) {
453         pathParameters.put(name, value);
454     }
455
456     public String getPathParameter(String name) {
457         return pathParameters.get(name);
458     }
459
460
461     // -------------------- Other attributes --------------------
462     // We can use notes for most - need to discuss what is of general interest
463
464     public void setAttribute( String name, Object o ) {
465         attributes.put( name, o );
466     }
467
468     public HashMap<String,Object> getAttributes() {
469         return attributes;
470     }
471
472     public Object getAttribute(String name ) {
473         return attributes.get(name);
474     }
475
476     public MessageBytes getRemoteUser() {
477         return remoteUser;
478     }
479
480     public boolean getRemoteUserNeedsAuthorization() {
481         return remoteUserNeedsAuthorization;
482     }
483
484     public void setRemoteUserNeedsAuthorization(boolean remoteUserNeedsAuthorization) {
485         this.remoteUserNeedsAuthorization = remoteUserNeedsAuthorization;
486     }
487
488     public MessageBytes getAuthType() {
489         return authType;
490     }
491
492     public int getAvailable() {
493         return available;
494     }
495
496     public void setAvailable(int available) {
497         this.available = available;
498     }
499
500     public boolean getSendfile() {
501         return sendfile;
502     }
503
504     public void setSendfile(boolean sendfile) {
505         this.sendfile = sendfile;
506     }
507
508     public boolean isFinished() {
509         AtomicBoolean result = new AtomicBoolean(false);
510         action(ActionCode.REQUEST_BODY_FULLY_READ, result);
511         return result.get();
512     }
513
514     public boolean getSupportsRelativeRedirects() {
515         if (protocol().equals("") || protocol().equals("HTTP/1.0")) {
516             return false;
517         }
518         return true;
519     }
520
521
522     // -------------------- Input Buffer --------------------
523
524     public InputBuffer getInputBuffer() {
525         return inputBuffer;
526     }
527
528
529     public void setInputBuffer(InputBuffer inputBuffer) {
530         this.inputBuffer = inputBuffer;
531     }
532
533
534     /**
535      * Read data from the input buffer and put it into ApplicationBufferHandler.
536      *
537      * The buffer is owned by the protocol implementation - it will be reused on
538      * the next read. The Adapter must either process the data in place or copy
539      * it to a separate buffer if it needs to hold it. In most cases this is
540      * done during byte-&gt;char conversions or via InputStream. Unlike
541      * InputStream, this interface allows the app to process data in place,
542      * without copy.
543      *
544      * @param handler The destination to which to copy the data
545      *
546      * @return The number of bytes copied
547      *
548      * @throws IOException If an I/O error occurs during the copy
549      */

550     public int doRead(ApplicationBufferHandler handler) throws IOException {
551         int n = inputBuffer.doRead(handler);
552         if (n > 0) {
553             bytesRead+=n;
554         }
555         return n;
556     }
557
558
559     // -------------------- debug --------------------
560
561     @Override
562     public String toString() {
563         return "R( " + requestURI().toString() + ")";
564     }
565
566     public long getStartTime() {
567         return startTime;
568     }
569
570     public void setStartTime(long startTime) {
571         this.startTime = startTime;
572     }
573
574     // -------------------- Per-Request "notes" --------------------
575
576
577     /**
578      * Used to store private data. Thread data could be used instead - but
579      * if you have the req, getting/setting a note is just an array access, may
580      * be faster than ThreadLocal for very frequent operations.
581      *
582      *  Example use:
583      *   Catalina CoyoteAdapter:
584      *      ADAPTER_NOTES = 1 - stores the HttpServletRequest object ( req/res)
585      *
586      *   To avoid conflicts, note in the range 0 - 8 are reserved for the
587      *   servlet container ( catalina connector, etc ), and values in 9 - 16
588      *   for connector use.
589      *
590      *   17-31 range is not allocated or used.
591      *
592      * @param pos Index to use to store the note
593      * @param value The value to store at that index
594      */

595     public final void setNote(int pos, Object value) {
596         notes[pos] = value;
597     }
598
599
600     public final Object getNote(int pos) {
601         return notes[pos];
602     }
603
604
605     // -------------------- Recycling --------------------
606
607
608     public void recycle() {
609         bytesRead=0;
610
611         contentLength = -1;
612         contentTypeMB = null;
613         charset = null;
614         characterEncoding = null;
615         expectation = false;
616         headers.recycle();
617         trailerFields.clear();
618         serverNameMB.recycle();
619         serverPort=-1;
620         localAddrMB.recycle();
621         localNameMB.recycle();
622         localPort = -1;
623         remoteAddrMB.recycle();
624         remoteHostMB.recycle();
625         remotePort = -1;
626         available = 0;
627         sendfile = true;
628
629         serverCookies.recycle();
630         parameters.recycle();
631         pathParameters.clear();
632
633         uriMB.recycle();
634         decodedUriMB.recycle();
635         queryMB.recycle();
636         methodMB.recycle();
637         protoMB.recycle();
638
639         schemeMB.recycle();
640
641         remoteUser.recycle();
642         remoteUserNeedsAuthorization = false;
643         authType.recycle();
644         attributes.clear();
645
646         listener = null;
647         allDataReadEventSent.set(false);
648
649         startTime = -1;
650     }
651
652     // -------------------- Info  --------------------
653     public void updateCounters() {
654         reqProcessorMX.updateCounters();
655     }
656
657     public RequestInfo getRequestProcessor() {
658         return reqProcessorMX;
659     }
660
661     public long getBytesRead() {
662         return bytesRead;
663     }
664
665     public boolean isProcessing() {
666         return reqProcessorMX.getStage()==org.apache.coyote.Constants.STAGE_SERVICE;
667     }
668
669     /**
670      * Parse the character encoding from the specified content type header.
671      * If the content type is null, or there is no explicit character encoding,
672      * <code>null</code> is returned.
673      *
674      * @param contentType a content type header
675      */

676     private static String getCharsetFromContentType(String contentType) {
677
678         if (contentType == null) {
679             return null;
680         }
681         int start = contentType.indexOf("charset=");
682         if (start < 0) {
683             return null;
684         }
685         String encoding = contentType.substring(start + 8);
686         int end = encoding.indexOf(';');
687         if (end >= 0) {
688             encoding = encoding.substring(0, end);
689         }
690         encoding = encoding.trim();
691         if ((encoding.length() > 2) && (encoding.startsWith("\""))
692             && (encoding.endsWith("\""))) {
693             encoding = encoding.substring(1, encoding.length() - 1);
694         }
695
696         return encoding.trim();
697     }
698 }
699