/* * 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;
/** * HTTP specific fields.
*/
String contentType = null;
String contentLanguage = null;
Charset charset = null; // Retain the original name used to set the charset so exactly that name is // used in the ContentType header. Some (arguably non-specification // compliant) user agents are very particular
String characterEncoding = null; long contentLength = -1; private Locale locale = DEFAULT_LOCALE;
// General information privatelong contentWritten = 0; privatelong commitTimeNanos = -1;
/** * 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>
*/ privatefinal AtomicInteger errorState = new AtomicInteger(0);
// -------------------- State --------------------
publicint getStatus() { return status;
}
/** * Set the response status. * * @param status The status value to set
*/ publicvoid 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
*/ publicvoid setMessage(String message) { this.message = message;
}
publicboolean isCommitted() { return committed;
}
publicvoid 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
*/ publiclong 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
*/ publiclong getCommitTimeNanos() { return commitTimeNanos;
}
// -----------------Error State --------------------
/** * Set the error Exception that occurred during the writing of the response processing. * * @param ex The exception that occurred
*/ publicvoid 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;
}
/** * Set the error flag. * * @return <code>false</code> if the error flag was already set * * @deprecated This method will be changed to return void in Tomcat 11 onwards
*/
@Deprecated publicboolean setError() { return errorState.compareAndSet(0, 1);
}
/** * Error flag accessor. * * @return <code>true</code> if the response has encountered an error
*/ publicboolean isError() { return errorState.get() > 0;
}
if (committed) { thrownew IllegalStateException();
}
recycle();
}
// -------------------- 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.
*/ publicboolean containsHeader(String name) { return headers.getHeader(name) != null;
}
publicvoid setHeader(String name, String value) { char cc = name.charAt(0); if (cc == 'C' || cc == 'c') { if (checkSpecialHeader(name, value)) { return;
}
}
headers.setValue(name).setString(value);
}
publicvoid 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);
}
publicvoid setTrailerFields(Supplier<Map<String, String>> supplier) {
AtomicBoolean trailerFieldsSupported = new AtomicBoolean(false);
action(ActionCode.IS_TRAILER_FIELDS_SUPPORTED, trailerFieldsSupported); if (!trailerFieldsSupported.get()) { thrownew 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.
*/ privateboolean checkSpecialHeader(String name, String value) { // XXX Eliminate redundant fields !!! // ( both header and in special fields ) if (name.equalsIgnoreCase("Content-Type")) {
setContentType(value); returntrue;
} if (name.equalsIgnoreCase("Content-Length")) { try { long cL = Long.parseLong(value);
setContentLength(cL); returntrue;
} catch (NumberFormatException ex) { // Do nothing - the spec doesn't have any "throws" // and the user might know what they're doing returnfalse;
}
} returnfalse;
}
/** * Signal that we're done with the headers, and body will follow. Any implementation needs to notify ContextManager, * to allow interceptors to fix headers.
*/ publicvoid sendHeaders() {
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
*/ publicvoid setLocale(Locale locale) {
// 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
*/ publicvoid setCharacterEncoding(String characterEncoding) throws UnsupportedEncodingException { if (isCommitted()) { return;
} if (characterEncoding == null) { this.charset = null; this.characterEncoding = null; return;
}
/** * @return The name of the current encoding
*/ public String getCharacterEncoding() { return characterEncoding;
}
/** * 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
*/ publicvoid setContentType(String type) {
if (type == null) { this.contentType = null; return;
}
MediaType m = null; try {
m = MediaType.parseMediaType(new StringReader(type));
} catch (IOException e) { // 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;
}
this.contentType = m.toStringNoCharset();
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.length() > 0) { try {
charset = B2CConverter.getCharset(charsetValue);
} catch (UnsupportedEncodingException e) {
log.warn(sm.getString("response.encoding.invalid", charsetValue), e);
}
}
}
}
/** * Write a chunk of bytes. * * @param chunk The ByteBuffer to write * * @throws IOException If an I/O error occurs during the write
*/ publicvoid doWrite(ByteBuffer chunk) throws IOException { int len = chunk.remaining();
outputBuffer.doWrite(chunk);
contentWritten += len - chunk.remaining();
}
/** * 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.
*/ publiclong 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
*/ publiclong 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() privateboolean fireListener = false; // Tracks write registration to prevent duplicate registrations privateboolean registeredForWrite = false; // Lock used to manage concurrent access to above flags privatefinal Object nonBlockingStateLock = new Object();
public WriteListener getWriteListener() { return listener;
}
publicvoid setWriteListener(WriteListener listener) { if (listener == null) { thrownew NullPointerException(sm.getString("response.nullWriteListener"));
} if (getWriteListener() != null) { thrownew 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()) { thrownew 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);
}
}
}
publicboolean isReady() { if (listener == null) { if (log.isDebugEnabled()) {
log.debug(sm.getString("response.notNonBlocking"));
} returnfalse;
} // Assume write is not possible boolean ready = false; synchronized (nonBlockingStateLock) { if (registeredForWrite) {
fireListener = true; returnfalse;
}
ready = checkRegisterForWrite();
fireListener = !ready;
} return ready;
}
publicvoid 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();
}
}
}
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.