ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/coyote/Request.java

Path
ttomcat-1778514358873.zip-extract/apache-tomcat-11.0.18-src/java/org/apache/coyote/Request.java
Status
scanned
Type
file
Name
Request.java
Extension
.java
Programming language
Java
Mime type
text/plain
File type
ASCII text, with CRLF line terminators
Tag

      
    
Rootfs path

      
    
Size
28691 (28.0 KB)
MD5
213b4afd6f81a59e5f458f15361d6da2
SHA1
fdf19dcdf45a02435a650fe823ed535560b2b002
SHA256
6a0d112481b58253a4ba64f3a4d2072dd31adae0714cc5814f74787154a523a9
SHA512

      
    
SHA1_git
55f7977bb63af8fe0f26768ed488edb552368889
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
Request.java | 28.0 KB |

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.coyote; import java.io.IOException; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.time.Instant; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletConnection; import org.apache.tomcat.util.buf.CharsetHolder; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.buf.UDecoder; import org.apache.tomcat.util.http.Method; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.Parameters; import org.apache.tomcat.util.http.ServerCookies; import org.apache.tomcat.util.http.parser.MediaType; import org.apache.tomcat.util.net.ApplicationBufferHandler; import org.apache.tomcat.util.res.StringManager; /** * This is a low-level, efficient representation of a server request. Most fields are GC-free, expensive operations are * delayed until the user code needs the information. Processing is delegated to modules, using a hook mechanism. This * class is not intended for user code - it is used internally by tomcat for processing the request in the most * efficient way. Users ( servlets ) can access the information using a facade, which provides the high-level view of * the request. Tomcat defines a number of attributes: * <ul> * <li>"org.apache.tomcat.request" - allows access to the low-level request object in trusted applications * </ul> */ public final class Request { private static final StringManager sm = StringManager.getManager(Request.class); // Expected maximum typical number of cookies per request. private static final int INITIAL_COOKIE_SIZE = 4; /* * At 100,000 requests a second there are enough IDs here for ~3,000,000 years before it overflows (and then we have * another 3,000,000 years before it gets back to zero). * * Local testing shows that 5, 10, 50, 500 or 1000 threads can obtain 60,000,000+ IDs a second from a single * AtomicLong. That is about 17ns per request. It does not appear that the introduction of this counter will cause a * bottleneck for request processing. */ private static final AtomicLong requestIdGenerator = new AtomicLong(0); // ----------------------------------------------------------- Constructors public Request() { parameters.setQuery(queryMB); parameters.setURLDecoder(urlDecoder); } // ----------------------------------------------------- Instance Variables private int serverPort = -1; private final MessageBytes serverNameMB = MessageBytes.newInstance(); private int remotePort; private int localPort; private final MessageBytes schemeMB = MessageBytes.newInstance(); private final MessageBytes methodMB = MessageBytes.newInstance(); private final MessageBytes uriMB = MessageBytes.newInstance(); private final MessageBytes decodedUriMB = MessageBytes.newInstance(); private final MessageBytes queryMB = MessageBytes.newInstance(); private final MessageBytes protoMB = MessageBytes.newInstance(); private volatile String requestId = Long.toString(requestIdGenerator.getAndIncrement()); // remote address/host private final MessageBytes remoteAddrMB = MessageBytes.newInstance(); private final MessageBytes peerAddrMB = MessageBytes.newInstance(); private final MessageBytes localNameMB = MessageBytes.newInstance(); private final MessageBytes remoteHostMB = MessageBytes.newInstance(); private final MessageBytes localAddrMB = MessageBytes.newInstance(); private final MimeHeaders headers = new MimeHeaders(); private final MimeHeaders trailerFields = new MimeHeaders(); /** * Path parameters */ private final Map<String,String> pathParameters = new HashMap<>(); /** * Notes. */ private final Object[] notes = new Object[Constants.MAX_NOTES]; /** * Associated input buffer. */ private InputBuffer inputBuffer = null; /** * URL decoder. */ private final UDecoder urlDecoder = new UDecoder(); /** * HTTP specific fields. (remove them ?) */ private long contentLength = -1; private MessageBytes contentTypeMB = null; private CharsetHolder charsetHolder = null; /** * Is there an expectation ? */ private boolean expectation = false; private final ServerCookies serverCookies = new ServerCookies(INITIAL_COOKIE_SIZE); private final Parameters parameters = new Parameters(); private final MessageBytes remoteUser = MessageBytes.newInstance(); private boolean remoteUserNeedsAuthorization = false; private final MessageBytes authType = MessageBytes.newInstance(); private final HashMap<String,Object> attributes = new HashMap<>(); private Response response; private volatile ActionHook hook; private long bytesRead = 0; // Time of the request - useful to avoid repeated calls to System.currentTime private long startTimeNanos = -1; private Instant startInstant = null; private long threadId = 0; private int available = 0; private final RequestInfo reqProcessorMX = new RequestInfo(this); private boolean sendfile = true; /** * Holds request body reading error exception. */ private Exception errorException = null; /* * State for non-blocking output is maintained here as it is the one point easily reachable from the * CoyoteInputStream and the CoyoteAdapter which both need access to state. */ volatile ReadListener listener; // Ensures listener is only fired after a call is isReady() private boolean fireListener = false; // Tracks read registration to prevent duplicate registrations private boolean registeredForRead = false; // Lock used to manage concurrent access to above flags private final Object nonBlockingStateLock = new Object(); public ReadListener getReadListener() { return listener; } public void setReadListener(ReadListener listener) { if (listener == null) { throw new NullPointerException(sm.getString("request.nullReadListener")); } if (getReadListener() != null) { throw new IllegalStateException(sm.getString("request.readListenerSet")); } // Note: This class is not used for HTTP upgrade so only need to test // for async AtomicBoolean result = new AtomicBoolean(false); action(ActionCode.ASYNC_IS_ASYNC, result); if (!result.get()) { throw new IllegalStateException(sm.getString("request.notAsync")); } this.listener = listener; // The container is responsible for the first call to // listener.onDataAvailable(). If isReady() returns true, the container // needs to call listener.onDataAvailable() from a new thread. If // isReady() returns false, the socket will be registered for read and // the container will call listener.onDataAvailable() once data arrives. // Must call isFinished() first as a call to isReady() if the request // has been finished will register the socket for read interest and that // is not required. if (!isFinished() && isReady()) { synchronized (nonBlockingStateLock) { // Ensure we don't get multiple read registrations registeredForRead = true; // Need to set the fireListener flag otherwise when the // container tries to trigger onDataAvailable, nothing will // happen fireListener = true; } action(ActionCode.DISPATCH_READ, null); if (!isRequestThread()) { // Not on a container thread so need to execute the dispatch action(ActionCode.DISPATCH_EXECUTE, null); } } } public boolean isReady() { // Assume read is not possible boolean ready; synchronized (nonBlockingStateLock) { if (registeredForRead) { fireListener = true; return false; } ready = checkRegisterForRead(); fireListener = !ready; } return ready; } private boolean checkRegisterForRead() { AtomicBoolean ready = new AtomicBoolean(false); synchronized (nonBlockingStateLock) { if (!registeredForRead) { action(ActionCode.NB_READ_INTEREST, ready); registeredForRead = !ready.get(); } } return ready.get(); } public void onDataAvailable() throws IOException { boolean fire = false; synchronized (nonBlockingStateLock) { registeredForRead = false; if (fireListener) { fireListener = false; fire = true; } } if (fire) { listener.onDataAvailable(); } } private final AtomicBoolean allDataReadEventSent = new AtomicBoolean(false); public boolean sendAllDataReadEvent() { return allDataReadEventSent.compareAndSet(false, true); } // ------------------------------------------------------------- Properties public MimeHeaders getMimeHeaders() { return headers; } public boolean isTrailerFieldsReady() { AtomicBoolean result = new AtomicBoolean(false); action(ActionCode.IS_TRAILER_FIELDS_READY, result); return result.get(); } public Map<String,String> getTrailerFields() { return trailerFields.toMap(); } public MimeHeaders getMimeTrailerFields() { return trailerFields; } public UDecoder getURLDecoder() { return urlDecoder; } // -------------------- Request data -------------------- public MessageBytes scheme() { return schemeMB; } /** * Get a MessageBytes instance that holds the current request's HTTP method. * * @return a MessageBytes instance that holds the current request's HTTP method. * * @deprecated Use {@link #getMethod()}, {@link Request#setMethod(String)} and {@link #setMethod(byte[], int, int)} */ @Deprecated public MessageBytes method() { return methodMB; } public void setMethod(String method) { methodMB.setString(method); } public void setMethod(byte[] buf, int start, int len) { String method = Method.bytesToString(buf, start, len); if (method == null) { methodMB.setBytes(buf, start, len); method = methodMB.toStringType(); } else { methodMB.setString(method); } } public String getMethod() { return methodMB.toStringType(); } public MessageBytes requestURI() { return uriMB; } public MessageBytes decodedURI() { return decodedUriMB; } public MessageBytes queryString() { return queryMB; } public MessageBytes protocol() { return protoMB; } /** * Get the "virtual host", derived from the Host: header associated with this request. * * @return The buffer holding the server name, if any. Use isNull() to check if there is no value set. */ public MessageBytes serverName() { return serverNameMB; } public int getServerPort() { return serverPort; } public void setServerPort(int serverPort) { this.serverPort = serverPort; } public MessageBytes remoteAddr() { return remoteAddrMB; } public MessageBytes peerAddr() { return peerAddrMB; } public MessageBytes remoteHost() { return remoteHostMB; } public MessageBytes localName() { return localNameMB; } public MessageBytes localAddr() { return localAddrMB; } public int getRemotePort() { return remotePort; } public void setRemotePort(int port) { this.remotePort = port; } public int getLocalPort() { return localPort; } public void setLocalPort(int port) { this.localPort = port; } // -------------------- encoding/type -------------------- /** * Get the character encoding used for this request. * * @return The value set via {@link #setCharset(Charset)} or if no call has been made to that method try to obtain * if from the content type. * * @deprecated Unused. This method will be removed in Tomcat 12. */ @Deprecated public String getCharacterEncoding() { if (charsetHolder.getName() == null) { charsetHolder = CharsetHolder.getInstance(getCharsetFromContentType(getContentType())); } return charsetHolder.getName(); } /** * Get the character encoding used for this request. * * @return The value set via {@link #setCharset(Charset)} or if no call has been made to that method try to obtain * if from the content type. * * @throws UnsupportedEncodingException If the user agent has specified an invalid character encoding * * @deprecated Unused. This method will be removed in Tomcat 12. */ @Deprecated public Charset getCharset() throws UnsupportedEncodingException { if (charsetHolder.getName() == null) { // Populates charsetHolder getCharacterEncoding(); } return charsetHolder.getValidatedCharset(); } /** * Unused. * * @param charset The Charset to use for the request * * @deprecated Unused. This method will be removed in Tomcat 12. */ @Deprecated public void setCharset(Charset charset) { charsetHolder = CharsetHolder.getInstance(charset); } public CharsetHolder getCharsetHolder() { if (charsetHolder == null) { charsetHolder = CharsetHolder.getInstance(getCharsetFromContentType(getContentType())); } return charsetHolder; } public void setCharsetHolder(CharsetHolder charsetHolder) { if (charsetHolder == null || charsetHolder.getName() == null) { this.charsetHolder = null; } else { this.charsetHolder = charsetHolder; } } public void setContentLength(long len) { this.contentLength = len; } public int getContentLength() { long length = getContentLengthLong(); if (length < Integer.MAX_VALUE) { return (int) length; } return -1; } public long getContentLengthLong() { if (contentLength > -1) { return contentLength; } MessageBytes clB = headers.getUniqueValue("content-length"); contentLength = (clB == null || clB.isNull()) ? -1 : clB.getLong(); return contentLength; } public String getContentType() { contentType(); if (contentTypeMB == null || contentTypeMB.isNull()) { return null; } return contentTypeMB.toStringType(); } public void setContentType(String type) { contentTypeMB.setString(type); } public MessageBytes contentType() { if (contentTypeMB == null) { contentTypeMB = headers.getValue("content-type"); } return contentTypeMB; } public void setContentType(MessageBytes mb) { contentTypeMB = mb; } public String getHeader(String name) { return headers.getHeader(name); } public void setExpectation(boolean expectation) { this.expectation = expectation; } public boolean hasExpectation() { return expectation; } // -------------------- Associated response -------------------- public Response getResponse() { return response; } public void setResponse(Response response) { this.response = response; response.setRequest(this); } void setHook(ActionHook hook) { this.hook = hook; } public void action(ActionCode actionCode, Object param) { if (hook != null) { hook.action(actionCode, Objects.requireNonNullElse(param, this)); } } // -------------------- Cookies -------------------- public ServerCookies getCookies() { return serverCookies; } // -------------------- Parameters -------------------- public Parameters getParameters() { return parameters; } public void addPathParameter(String name, String value) { pathParameters.put(name, value); } public String getPathParameter(String name) { return pathParameters.get(name); } // -------------------- Other attributes -------------------- // We can use notes for most - need to discuss what is of general interest public void setAttribute(String name, Object o) { attributes.put(name, o); } public HashMap<String,Object> getAttributes() { return attributes; } public Object getAttribute(String name) { return attributes.get(name); } public MessageBytes getRemoteUser() { return remoteUser; } public boolean getRemoteUserNeedsAuthorization() { return remoteUserNeedsAuthorization; } public void setRemoteUserNeedsAuthorization(boolean remoteUserNeedsAuthorization) { this.remoteUserNeedsAuthorization = remoteUserNeedsAuthorization; } public MessageBytes getAuthType() { return authType; } public int getAvailable() { return available; } public void setAvailable(int available) { this.available = available; } public boolean getSendfile() { return sendfile; } public void setSendfile(boolean sendfile) { this.sendfile = sendfile; } public boolean isFinished() { AtomicBoolean result = new AtomicBoolean(false); action(ActionCode.REQUEST_BODY_FULLY_READ, result); return result.get(); } public boolean getSupportsRelativeRedirects() { return !protocol().equals("") && !protocol().equals("HTTP/1.0"); } // -------------------- Input Buffer -------------------- public InputBuffer getInputBuffer() { return inputBuffer; } public void setInputBuffer(InputBuffer inputBuffer) { this.inputBuffer = inputBuffer; } /** * Read data from the input buffer and put it into ApplicationBufferHandler. The buffer is owned by the protocol * implementation - it will be reused on the next read. The Adapter must either process the data in place or copy it * to a separate buffer if it needs to hold it. In most cases this is done during byte-&gt;char conversions or via * InputStream. Unlike InputStream, this interface allows the app to process data in place, without copy. * * @param handler The destination to which to copy the data * * @return The number of bytes copied * * @throws IOException If an I/O error occurs during the copy */ public int doRead(ApplicationBufferHandler handler) throws IOException { if (getBytesRead() == 0 && !response.isCommitted()) { action(ActionCode.ACK, ContinueResponseTiming.ON_REQUEST_BODY_READ); } int n = inputBuffer.doRead(handler); if (n > 0) { bytesRead += n; } return n; } // -------------------- Error tracking -------------------- /** * Set the error Exception that occurred during the writing of the response processing. * * @param ex The exception that occurred */ public void setErrorException(Exception ex) { errorException = ex; } /** * Get the Exception that occurred during the writing of the response. * * @return The exception that occurred */ public Exception getErrorException() { return errorException; } public boolean isExceptionPresent() { return errorException != null; } // -------------------- debug -------------------- public String getRequestId() { return requestId; } public String getProtocolRequestId() { AtomicReference<String> ref = new AtomicReference<>(); hook.action(ActionCode.PROTOCOL_REQUEST_ID, ref); return ref.get(); } public ServletConnection getServletConnection() { AtomicReference<ServletConnection> ref = new AtomicReference<>(); hook.action(ActionCode.SERVLET_CONNECTION, ref); return ref.get(); } @Override public String toString() { return "R( " + requestURI().toString() + ")"; } public long getStartTime() { return System.currentTimeMillis() - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTimeNanos); } public long getStartTimeNanos() { return startTimeNanos; } /** * Set the start time using the value provided by {@code System.nanoTime()}. * * @param startTimeNanos The value returned from {@code System.nanoTime()} at the point the requests started. * * @deprecated Unused. Will be removed in Tomcat 12 onwards. Use {@link #markStartTime()}. */ @Deprecated public void setStartTimeNanos(long startTimeNanos) { this.startTimeNanos = startTimeNanos; startInstant = Instant.now(); } public void markStartTime() { startTimeNanos = System.nanoTime(); startInstant = Instant.now(); } public Instant getStartInstant() { return startInstant; } public long getThreadId() { return threadId; } public void clearRequestThread() { threadId = 0; } @SuppressWarnings("deprecation") public void setRequestThread() { Thread t = Thread.currentThread(); threadId = t.getId(); getRequestProcessor().setWorkerThreadName(t.getName()); } @SuppressWarnings("deprecation") public boolean isRequestThread() { return Thread.currentThread().getId() == threadId; } // -------------------- Per-Request "notes" -------------------- /** * Used to store private data. Thread data could be used instead - but if you have the req, getting/setting a note * is just an array access, may be faster than ThreadLocal for very frequent operations. Example use: Catalina * CoyoteAdapter: ADAPTER_NOTES = 1 - stores the HttpServletRequest object ( req/res) To avoid conflicts, note in * the range 0 - 8 are reserved for the servlet container ( catalina connector, etc ), and values in 9 - 16 for * connector use. 17-31 range is not allocated or used. * * @param pos Index to use to store the note * @param value The value to store at that index */ public void setNote(int pos, Object value) { notes[pos] = value; } public Object getNote(int pos) { return notes[pos]; } // -------------------- Recycling -------------------- public void recycle() { bytesRead = 0; contentLength = -1; contentTypeMB = null; charsetHolder = null; expectation = false; headers.recycle(); trailerFields.recycle(); /* * Trailer fields are limited in size by bytes. The following call ensures that any request with a large number * of small trailer fields doesn't result in a long lasting, large array of headers inside the MimeHeader * instance. */ trailerFields.setLimit(MimeHeaders.DEFAULT_HEADER_SIZE); serverNameMB.recycle(); serverPort = -1; localAddrMB.recycle(); localNameMB.recycle(); localPort = -1; peerAddrMB.recycle(); remoteAddrMB.recycle(); remoteHostMB.recycle(); remotePort = -1; available = 0; sendfile = true; // There may be multiple calls to recycle but only the first should // trigger a change in the request ID until a new request has been // started. Use startTimeNanos to detect when a request has started so a // subsequent call to recycle() will trigger a change in the request ID. if (startTimeNanos != -1) { requestId = Long.toHexString(requestIdGenerator.getAndIncrement()); } serverCookies.recycle(); parameters.recycle(); pathParameters.clear(); uriMB.recycle(); decodedUriMB.recycle(); queryMB.recycle(); methodMB.recycle(); protoMB.recycle(); schemeMB.recycle(); remoteUser.recycle(); remoteUserNeedsAuthorization = false; authType.recycle(); attributes.clear(); errorException = null; listener = null; synchronized (nonBlockingStateLock) { fireListener = false; registeredForRead = false; } allDataReadEventSent.set(false); startTimeNanos = -1; startInstant = null; threadId = 0; if (hook instanceof NonPipeliningProcessor) { /* * No requirement to maintain state between requests so clear the hook (a.k.a. Processor) and the input * buffer to aid GC. */ setHook(null); setInputBuffer(null); } } // -------------------- Info -------------------- public void updateCounters() { reqProcessorMX.updateCounters(); } public RequestInfo getRequestProcessor() { return reqProcessorMX; } public long getBytesRead() { return bytesRead; } public boolean isProcessing() { return reqProcessorMX.getStage() == Constants.STAGE_SERVICE; } /** * Parse the character encoding from the specified content type header. If the content type is null, or there is no * explicit character encoding, <code>null</code> is returned. * * @param contentType a content type header */ private static String getCharsetFromContentType(String contentType) { if (contentType == null) { return null; } MediaType mediaType = null; try { mediaType = MediaType.parseMediaType(new StringReader(contentType)); } catch (IOException ioe) { // Ignore - null test below handles this } if (mediaType != null) { return mediaType.getCharset(); } return null; } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
4.51
Copyrights

      
    
Holders

      
    
Authors

      
    
License detections License expression License expression SPDX
apache_2_0-4bde3f57-78aa-4201-96bf-531cba09e7de apache-2.0 Apache-2.0
URL Start line End line
http://www.apache.org/licenses/LICENSE-2.0 9 9