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

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

      
    
Rootfs path

      
    
Size
23263 (22.7 KB)
MD5
4ae2f57c851a960648c5446d108cb5f0
SHA1
8a8c22979b04d1ba853d882a8ae2bd8d6348910a
SHA256
5abf6f8c9edf51fbc32f2ba4023a99d2dd2493b24c0daa02e8dfc1358897d483
SHA512

      
    
SHA1_git
c1c927cfbfdeae166052ac54aa2caf98b88917bf
Is binary

      
    
Is text
True
Is archive

      
    
Is media

      
    
Is legal

      
    
Is manifest

      
    
Is readme

      
    
Is top level

      
    
Is key file

      
    
Response.java | 22.7 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.ByteBuffer; import java.nio.charset.Charset; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import jakarta.servlet.WriteListener; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.CharsetHolder; import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.http.MimeHeaders; import org.apache.tomcat.util.http.parser.MediaType; import org.apache.tomcat.util.res.StringManager; /** * Response object. */ public final class Response { private static final StringManager sm = StringManager.getManager(Response.class); private static final Log log = LogFactory.getLog(Response.class); // ----------------------------------------------------- Class Variables /** * Default locale as mandated by the spec. */ private static final Locale DEFAULT_LOCALE = Locale.getDefault(); // ----------------------------------------------------- Instance Variables /** * Status code. */ int status = 200; /** * Status message. */ String message = null; /** * Response headers. */ final MimeHeaders headers = new MimeHeaders(); private Supplier<Map<String,String>> trailerFieldsSupplier = null; /** * Associated output buffer. */ OutputBuffer outputBuffer; /** * Notes. */ final Object[] notes = new Object[Constants.MAX_NOTES]; /** * Committed flag. */ volatile boolean committed = false; /** * Action hook. */ volatile ActionHook hook; /** * HTTP specific fields. */ String contentType = null; String contentLanguage = null; private CharsetHolder charsetHolder = CharsetHolder.EMPTY; long contentLength = -1; private Locale locale = DEFAULT_LOCALE; // General information private long contentWritten = 0; private long commitTimeNanos = -1; /** * Holds response writing error exception. */ private Exception errorException = null; /** * With the introduction of async processing and the possibility of non-container threads calling sendError() * tracking the current error state and ensuring that the correct error page is called becomes more complicated. * This state attribute helps by tracking the current error state and informing callers that attempt to change state * if the change was successful or if another thread got there first. * * <pre> * The state machine is very simple: * * 0 - NONE * 1 - NOT_REPORTED * 2 - REPORTED * * * -->---->-- >NONE * | | | * | | | setError() * ^ ^ | * | | \|/ * | |-<-NOT_REPORTED * | | * ^ | report() * | | * | \|/ * |----<----REPORTED * </pre> */ private final AtomicInteger errorState = new AtomicInteger(0); Request req; // ------------------------------------------------------------- Properties public Request getRequest() { return req; } public void setRequest(Request req) { this.req = req; } public void setOutputBuffer(OutputBuffer outputBuffer) { this.outputBuffer = outputBuffer; } public MimeHeaders getMimeHeaders() { return headers; } void setHook(ActionHook hook) { this.hook = hook; } // -------------------- Per-Response "notes" -------------------- public void setNote(int pos, Object value) { notes[pos] = value; } public Object getNote(int pos) { return notes[pos]; } // -------------------- Actions -------------------- public void action(ActionCode actionCode, Object param) { if (hook != null) { hook.action(actionCode, Objects.requireNonNullElse(param, this)); } } // -------------------- State -------------------- public int getStatus() { return status; } /** * Set the response status. * * @param status The status value to set */ public void setStatus(int status) { this.status = status; } /** * Get the status message. * * @return The message associated with the current status */ public String getMessage() { return message; } /** * Set the status message. * * @param message The status message to set */ public void setMessage(String message) { this.message = message; } public boolean isCommitted() { return committed; } public void setCommitted(boolean v) { if (v && !this.committed) { this.commitTimeNanos = System.nanoTime(); } this.committed = v; } /** * Return the time the response was committed (based on System.currentTimeMillis). * * @return the time the response was committed */ public long getCommitTime() { return System.currentTimeMillis() - TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - commitTimeNanos); } /** * Return the time the response was committed (based on System.nanoTime). * * @return the time the response was committed */ public long getCommitTimeNanos() { return commitTimeNanos; } // -----------------Error State -------------------- /** * Set the error Exception that occurred during the writing of the response processing. * * @param ex The exception that occurred */ public void setErrorException(Exception ex) { if (errorException == null) { 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; } /** * Set the error flag if not already set. */ public void setError() { errorState.compareAndSet(0, 1); } /** * Error flag accessor. * * @return <code>true</code> if the response has encountered an error */ public boolean isError() { return errorState.get() > 0; } public boolean isErrorReportRequired() { return errorState.get() == 1; } public boolean setErrorReported() { return errorState.compareAndSet(1, 2); } public void resetError() { errorState.set(0); } // -------------------- Methods -------------------- public void reset() throws IllegalStateException { if (committed) { throw new IllegalStateException(); } recycle(false); } // -------------------- Headers -------------------- /** * Does the response contain the given header. <br> * Warning: This method always returns <code>false</code> for Content-Type and Content-Length. * * @param name The name of the header of interest * * @return {@code true} if the response contains the header. */ public boolean containsHeader(String name) { return headers.getHeader(name) != null; } public void setHeader(String name, String value) { char cc = name.charAt(0); if (cc == 'C' || cc == 'c') { if (checkSpecialHeader(name, value)) { return; } } if (value == null) { headers.removeHeader(name); } else { headers.setValue(name).setString(value); } } public void addHeader(String name, String value) { addHeader(name, value, null); } public void addHeader(String name, String value, Charset charset) { char cc = name.charAt(0); if (cc == 'C' || cc == 'c') { if (checkSpecialHeader(name, value)) { return; } } MessageBytes mb = headers.addValue(name); if (charset != null) { mb.setCharset(charset); } mb.setString(value); } public void setTrailerFields(Supplier<Map<String,String>> supplier) { AtomicBoolean trailerFieldsSupported = new AtomicBoolean(false); action(ActionCode.IS_TRAILER_FIELDS_SUPPORTED, trailerFieldsSupported); if (!trailerFieldsSupported.get()) { throw new IllegalStateException(sm.getString("response.noTrailers.notSupported")); } this.trailerFieldsSupplier = supplier; } public Supplier<Map<String,String>> getTrailerFields() { return trailerFieldsSupplier; } /** * Set internal fields for special header names. Called from set/addHeader. Return true if the header is special, no * need to set the header. */ private boolean checkSpecialHeader(String name, String value) { // XXX Eliminate redundant fields !!! // ( both header and in special fields ) if (name.equalsIgnoreCase("Content-Type")) { setContentType(value); return true; } if (name.equalsIgnoreCase("Content-Length")) { try { if (value == null) { setContentLength(-1); return true; } long cL = Long.parseLong(value); setContentLength(cL); return true; } catch (NumberFormatException ex) { // Do nothing - the spec doesn't have any "throws" // and the user might know what they're doing return false; } } return false; } /** * Signal that we're done with the headers, and body will follow. */ public void commit() { action(ActionCode.COMMIT, this); setCommitted(true); } // -------------------- I18N -------------------- public Locale getLocale() { return locale; } /** * Called explicitly by user to set the Content-Language and the default encoding. * * @param locale The locale to use for this response */ public void setLocale(Locale locale) { if (locale == null) { this.locale = null; this.contentLanguage = null; return; } // Save the locale for use by getLocale() this.locale = locale; // Set the contentLanguage for header output contentLanguage = locale.toLanguageTag(); } /** * Return the content language. * * @return The language code for the language currently associated with this response */ public String getContentLanguage() { return contentLanguage; } /** * Overrides the character encoding used in the body of the response. This method must be called prior to writing * output using getWriter(). * * @param characterEncoding The name of character encoding. * * @throws UnsupportedEncodingException If the specified name is not recognised * * @deprecated Unused. Will be removed in Tomcat 12. */ @Deprecated public void setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException { if (isCommitted()) { return; } charsetHolder = CharsetHolder.getInstance(characterEncoding); charsetHolder.validate(); } /** * Returns the current character set. * * @return The current character set * * @deprecated Unused. Will be removed in Tomcat 12. */ @Deprecated public Charset getCharset() { return charsetHolder.getCharset(); } /** * Returns the name of the current encoding. * * @return The name of the current encoding * * @deprecated Unused. Will be removed in Tomcat 12. */ @Deprecated public String getCharacterEncoding() { return charsetHolder.getName(); } public CharsetHolder getCharsetHolder() { return charsetHolder; } public void setCharsetHolder(CharsetHolder charsetHolder) { this.charsetHolder = charsetHolder; } /** * Sets the content type. This method must preserve any response charset that may already have been set via a call * to response.setContentType(), response.setLocale(), or response.setCharacterEncoding(). * * @param type the content type */ public void setContentType(String type) { if (type == null) { this.contentType = null; return; } MediaType m = null; try { m = MediaType.parseMediaType(new StringReader(type)); } catch (IOException ignore) { // Ignore - null test below handles this } if (m == null) { // Invalid - Assume no charset and just pass through whatever // the user provided. this.contentType = type; return; } String charsetValue = m.getCharset(); if (charsetValue == null) { // No charset and we know value is valid as parser was successful // Pass-through user provided value in case user-agent is buggy and // requires specific format this.contentType = type; } else { // There is a charset so have to rebuild content-type without it this.contentType = m.toStringNoCharset(); charsetValue = charsetValue.trim(); if (!charsetValue.isEmpty()) { charsetHolder = CharsetHolder.getInstance(charsetValue); try { charsetHolder.validate(); } catch (UnsupportedEncodingException e) { log.warn(sm.getString("response.encoding.invalid", charsetValue), e); } } } } public void setContentTypeNoCharset(String type) { this.contentType = type; } public String getContentType() { String ret = contentType; if (ret != null) { String charsetName = charsetHolder.getName(); if (charsetName != null) { ret = ret + ";charset=" + charsetName; } } return ret; } public void setContentLength(long contentLength) { this.contentLength = contentLength; } public int getContentLength() { long length = getContentLengthLong(); if (length < Integer.MAX_VALUE) { return (int) length; } return -1; } public long getContentLengthLong() { return contentLength; } /** * Write a chunk of bytes. * * @param chunk The ByteBuffer to write * * @throws IOException If an I/O error occurs during the write */ public void doWrite(ByteBuffer chunk) throws IOException { int len = chunk.remaining(); outputBuffer.doWrite(chunk); contentWritten += len - chunk.remaining(); } public void recycle() { recycle(true); } private void recycle(boolean responseComplete) { contentType = null; contentLanguage = null; locale = DEFAULT_LOCALE; charsetHolder = CharsetHolder.EMPTY; contentLength = -1; status = 200; message = null; committed = false; commitTimeNanos = -1; errorException = null; resetError(); headers.recycle(); trailerFieldsSupplier = null; // Servlet 3.1 non-blocking write listener listener = null; synchronized (nonBlockingStateLock) { fireListener = false; registeredForWrite = false; } // update counters contentWritten = 0; if (responseComplete && hook instanceof NonPipeliningProcessor) { /* * No requirement to maintain state between responses so clear the hook (a.k.a. Processor) and the output * buffer to aid GC. * * Only clear these between responses. They need to be retained when the response is reset. */ setHook(null); setOutputBuffer(null); } } /** * Bytes written by application - i.e. before compression, chunking, etc. * * @return The total number of bytes written to the response by the application. This will not be the number of * bytes written to the network which may be more or less than this value. */ public long getContentWritten() { return contentWritten; } /** * Bytes written to socket - i.e. after compression, chunking, etc. * * @param flush Should any remaining bytes be flushed before returning the total? If {@code false} bytes remaining * in the buffer will not be included in the returned value * * @return The total number of bytes written to the socket for this response */ public long getBytesWritten(boolean flush) { if (flush) { action(ActionCode.CLIENT_FLUSH, this); } return outputBuffer.getBytesWritten(); } /* * State for non-blocking output is maintained here as it is the one point easily reachable from the * CoyoteOutputStream and the Processor which both need access to state. */ volatile WriteListener listener; // Ensures listener is only fired after a call is isReady() private boolean fireListener = false; // Tracks write registration to prevent duplicate registrations private boolean registeredForWrite = false; // Lock used to manage concurrent access to above flags private final Object nonBlockingStateLock = new Object(); public WriteListener getWriteListener() { return listener; } public void setWriteListener(WriteListener listener) { if (listener == null) { throw new NullPointerException(sm.getString("response.nullWriteListener")); } if (getWriteListener() != null) { throw new IllegalStateException(sm.getString("response.writeListenerSet")); } // 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("response.notAsync")); } this.listener = listener; // The container is responsible for the first call to // listener.onWritePossible(). If isReady() returns true, the container // needs to call listener.onWritePossible() from a new thread. If // isReady() returns false, the socket will be registered for write and // the container will call listener.onWritePossible() once data can be // written. if (isReady()) { synchronized (nonBlockingStateLock) { // Ensure we don't get multiple write registrations if // ServletOutputStream.isReady() returns false during a call to // onDataAvailable() registeredForWrite = true; // Need to set the fireListener flag otherwise when the // container tries to trigger onWritePossible, nothing will // happen fireListener = true; } action(ActionCode.DISPATCH_WRITE, null); if (!req.isRequestThread()) { // Not on a container thread so need to execute the dispatch action(ActionCode.DISPATCH_EXECUTE, null); } } } public boolean isReady() { if (listener == null) { return true; } // Assume write is not possible boolean ready; synchronized (nonBlockingStateLock) { if (registeredForWrite) { fireListener = true; return false; } ready = checkRegisterForWrite(); fireListener = !ready; } return ready; } public boolean checkRegisterForWrite() { AtomicBoolean ready = new AtomicBoolean(false); synchronized (nonBlockingStateLock) { if (!registeredForWrite) { action(ActionCode.NB_WRITE_INTEREST, ready); registeredForWrite = !ready.get(); } } return ready.get(); } public void onWritePossible() throws IOException { // Any buffered data left over from a previous non-blocking write is // written in the Processor so if this point is reached the app is able // to write data. boolean fire = false; synchronized (nonBlockingStateLock) { registeredForWrite = false; if (fireListener) { fireListener = false; fire = true; } } if (fire) { listener.onWritePossible(); } } }
Detected license expression
apache-2.0
Detected license expression (SPDX)
Apache-2.0
Percentage of license text
5.62
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