/* * 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;
/** * The string manager for this package.
*/ privatestaticfinal StringManager sm = StringManager.getManager(AbstractProtocol.class);
/** * Counter used to generate unique JMX names for connectors using automatic port binding.
*/ privatestaticfinal AtomicInteger nameCounter = new AtomicInteger(0);
/** * Unique ID for this connector. Only used if the connector is configured to use a random port as the port will * change if stop(), start() is called.
*/ privateint nameIndex = 0;
/** * Endpoint that provides low-level network I/O - must be matched to the ProtocolHandler implementation * (ProtocolHandler using NIO, requires NIO Endpoint etc.).
*/ privatefinal AbstractEndpoint<S, ?> endpoint;
/** * Generic property setter used by the digester. Other code should not need to use this. The digester will only use * this method if it can't find a more specific setter. That means the property belongs to the Endpoint, the * ServerSocketFactory or some other lower level component. This method ensures that it is visible to both. * * @param name The name of the property to set * @param value The value, in string form, to set for the property * * @return <code>true</code> if the property was set successfully, otherwise <code>false</code>
*/ publicboolean setProperty(String name, String value) { return endpoint.setProperty(name, value);
}
/** * Generic property getter used by the digester. Other code should not need to use this. * * @param name The name of the property to get * * @return The value of the property converted to a string
*/ public String getProperty(String name) { return endpoint.getProperty(name);
}
// ------------------------------- Properties managed by the ProtocolHandler
/** * Name of MBean for the Global Request Processor.
*/ protected ObjectName rgOname = null;
public ObjectName getGlobalRequestProcessorMBeanName() { return rgOname;
}
/** * The adapter provides the link between the ProtocolHandler and the connector.
*/ protected Adapter adapter;
@Override public Adapter getAdapter() { return adapter;
}
/** * The maximum number of idle processors that will be retained in the cache and re-used with a subsequent request. * The default is 200. A value of -1 means unlimited. In the unlimited case, the theoretical maximum number of * cached Processor objects is {@link #getMaxConnections()} although it will usually be closer to * {@link #getMaxThreads()}.
*/ protectedint processorCache = 200;
/** * When client certificate information is presented in a form other than instances of * {@link java.security.cert.X509Certificate} it needs to be converted before it can be used and this property * controls which JSSE provider is used to perform the conversion. For example it is used with the AJP connectors * and with the {@link org.apache.catalina.valves.SSLValve}. If not specified, the default provider will be used. * * @return The name of the JSSE provider to use
*/ public String getClientCertProvider() { return clientCertProvider;
}
/** * The time Tomcat will wait for a subsequent request before closing the connection. The default is * {@link #getConnectionTimeout()}. * * @return The timeout in milliseconds
*/ publicint getKeepAliveTimeout() { return endpoint.getKeepAliveTimeout();
}
/* * When Tomcat expects data from the client, this is the time Tomcat will wait for that data to arrive before * closing the connection.
*/ publicint getConnectionTimeout() { return endpoint.getConnectionTimeout();
}
/** * The name will be prefix-address-port if address is non-null and prefix-port if the address is null. * * @return A name for this protocol instance that is appropriately quoted for use in an ObjectName.
*/ public String getName() { return ObjectName.quote(getNameInternal());
}
private String getNameInternal() {
StringBuilder name = new StringBuilder(getNamePrefix());
name.append('-');
String id = getId(); if (id != null) {
name.append(id);
} else { if (getAddress() != null) {
name.append(getAddress().getHostAddress());
name.append('-');
} int port = getPortWithOffset(); if (port == 0) { // Auto binding is in use. Check if port is known
name.append("auto-");
name.append(getNameIndex());
port = getLocalPort(); if (port != -1) {
name.append('-');
name.append(port);
}
} else {
name.append(port);
}
} return name.toString();
}
/* * Primarily for debugging and testing. Could be exposed via JMX if considered useful.
*/ publicint getWaitingProcessorCount() { return waitingProcessors.size();
}
// ----------------------------------------------- Accessors for sub-classes
/** * Concrete implementations need to provide access to their logger to be used by the abstract classes. * * @return the logger
*/ protectedabstract Log getLog();
/** * Obtain the prefix to be used when construction a name for this protocol handler. The name will be * prefix-address-port. * * @return the prefix
*/ protectedabstract String getNamePrefix();
/** * Obtain the name of the protocol, (Http, Ajp, etc.). Used with JMX. * * @return the protocol name
*/ protectedabstract String getProtocolName();
/** * Find a suitable handler for the protocol negotiated at the network layer. * * @param name The name of the requested negotiated protocol. * * @return The instance where {@link UpgradeProtocol#getAlpnName()} matches the requested protocol
*/ protectedabstract UpgradeProtocol getNegotiatedProtocol(String name);
/** * Find a suitable handler for the protocol upgraded name specified. This is used for direct connection protocol * selection. * * @param name The name of the requested negotiated protocol. * * @return The instance where {@link UpgradeProtocol#getAlpnName()} matches the requested protocol
*/ protectedabstract UpgradeProtocol getUpgradeProtocol(String name);
/** * Create and configure a new Processor instance for the current protocol implementation. * * @return A fully configured Processor instance that is ready to use
*/ protectedabstract Processor createProcessor();
/* * NOTE: There is no maintenance of state or checking for valid transitions within this class. It is expected that * the connector will maintain state and prevent invalid state transitions.
*/
/** * Note: The name of this method originated with the Servlet 3.0 asynchronous processing but evolved over time to * represent a timeout that is triggered independently of the socket read/write timeouts.
*/ protectedvoid startAsyncTimeout() { if (timeoutFuture == null || timeoutFuture.isDone()) { if (timeoutFuture != null && timeoutFuture.isDone()) { // There was an error executing the scheduled task, get it and log it try {
timeoutFuture.get();
} catch (InterruptedException | ExecutionException e) {
getLog().error(sm.getString("abstractProtocolHandler.asyncTimeoutError"), e);
}
}
timeoutFuture = getUtilityExecutor().scheduleAtFixedRate(() -> { long now = System.currentTimeMillis(); for (Processor processor : waitingProcessors) {
processor.timeoutAsync(now);
}
}, 1, 1, TimeUnit.SECONDS);
}
}
@Override public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) { if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.process", wrapper.getSocket(), status));
} if (wrapper == null) { // Nothing to do. Socket has been closed. return SocketState.CLOSED;
}
S socket = wrapper.getSocket();
// We take complete ownership of the Processor inside of this method to ensure // no other thread can release it while we're using it. Whatever processor is // held by this variable will be associated with the SocketWrapper before this // method returns.
Processor processor = (Processor) wrapper.takeCurrentProcessor(); if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet", processor, socket));
}
// Timeouts are calculated on a dedicated thread and then // dispatched. Because of delays in the dispatch process, the // timeout may no longer be required. Check here and avoid // unnecessary processing. if (SocketEvent.TIMEOUT == status && (processor == null || !processor.isAsync() && !processor.isUpgrade() ||
processor.isAsync() && !processor.checkAsyncTimeoutGeneration())) { // This is effectively a NO-OP return SocketState.OPEN;
}
if (processor != null) { // Make sure an async timeout doesn't fire
getProtocol().removeWaitingProcessor(processor);
} elseif (status == SocketEvent.DISCONNECT || status == SocketEvent.ERROR) { // Nothing to do. Endpoint requested a close and there is no // longer a processor associated with this socket. return SocketState.CLOSED;
}
try { if (processor == null) {
String negotiatedProtocol = wrapper.getNegotiatedProtocol(); // OpenSSL typically returns null whereas JSSE typically // returns "" when no protocol is negotiated if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) {
UpgradeProtocol upgradeProtocol = getProtocol().getNegotiatedProtocol(negotiatedProtocol); if (upgradeProtocol != null) {
processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter()); if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
}
} elseif (negotiatedProtocol.equals("http/1.1")) { // Explicitly negotiated the default protocol. // Obtain a processor below.
} else { // TODO: // OpenSSL 1.0.2's ALPN callback doesn't support // failing the handshake with an error if no // protocol can be negotiated. Therefore, we need to // fail the connection here. Once this is fixed, // replace the code below with the commented out // block. if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.negotiatedProcessor.fail",
negotiatedProtocol));
} return SocketState.CLOSED; /* * To replace the code above once OpenSSL 1.1.0 is used. // Failed to create processor. This * is a bug. throw new IllegalStateException(sm.getString( * "abstractConnectionHandler.negotiatedProcessor.fail", negotiatedProtocol));
*/
}
}
} if (processor == null) {
processor = recycledProcessors.pop(); if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.processorPop", processor));
}
} if (processor == null) {
processor = getProtocol().createProcessor();
register(processor); if (getLog().isDebugEnabled()) {
getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
}
}
processor.setSslSupport(wrapper.getSslSupport());
SocketState state = SocketState.CLOSED; do {
state = processor.process(wrapper, status);
if (state == SocketState.UPGRADING) { // Get the HTTP upgrade handler
UpgradeToken upgradeToken = processor.getUpgradeToken(); // Restore leftover input to the wrapper so the upgrade // processor can process it.
ByteBuffer leftOverInput = processor.getLeftoverInput();
wrapper.unRead(leftOverInput); if (upgradeToken == null) { // Assume direct HTTP/2 connection
UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c"); if (upgradeProtocol != null) { // Release the Http11 processor to be re-used
release(processor); // Create the upgrade processor
processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
} else { if (getLog().isDebugEnabled()) {
getLog().debug(
sm.getString("abstractConnectionHandler.negotiatedProcessor.fail", "h2c"));
} // Exit loop and trigger appropriate clean-up
state = SocketState.CLOSED;
}
} else {
HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler(); // Release the Http11 processor to be re-used
release(processor); // Create the upgrade processor
processor = getProtocol().createUpgradeProcessor(wrapper, upgradeToken); if (getLog().isDebugEnabled()) {
getLog().debug(
sm.getString("abstractConnectionHandler.upgradeCreate", processor, wrapper));
} // Initialise the upgrade handler (which may trigger // some IO using the new protocol which is why the lines // above are necessary) // This cast should be safe. If it fails the error // handling for the surrounding try/catch will deal with // it. if (upgradeToken.getInstanceManager() == null) {
httpUpgradeHandler.init((WebConnection) processor);
} else {
ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null); try {
httpUpgradeHandler.init((WebConnection) processor);
} finally {
upgradeToken.getContextBind().unbind(false, oldCL);
}
} if (httpUpgradeHandler instanceof InternalHttpUpgradeHandler) { if (((InternalHttpUpgradeHandler) httpUpgradeHandler).hasAsyncIO()) { // The handler will initiate all further I/O
state = SocketState.ASYNC_IO;
}
}
}
}
} while (state == SocketState.UPGRADING);
if (state == SocketState.LONG) { // In the middle of processing a request/response. Keep the // socket associated with the processor. Exact requirements // depend on type of long poll
longPoll(wrapper, processor); if (processor.isAsync()) {
getProtocol().addWaitingProcessor(processor);
}
} elseif (state == SocketState.OPEN) { // In keep-alive but between requests. OK to recycle // processor. Continue to poll for the next request.
release(processor);
processor = null;
wrapper.registerReadInterest();
} elseif (state == SocketState.SENDFILE) { // Sendfile in progress. If it fails, the socket will be // closed. If it works, the socket either be added to the // poller (or equivalent) to await more data or processed // if there are any pipe-lined requests remaining.
} elseif (state == SocketState.UPGRADED) { // Don't add sockets back to the poller if this was a // non-blocking write otherwise the poller may trigger // multiple read events which may lead to thread starvation // in the connector. The write() method will add this socket // to the poller if necessary. if (status != SocketEvent.OPEN_WRITE) {
longPoll(wrapper, processor);
getProtocol().addWaitingProcessor(processor);
}
} elseif (state == SocketState.ASYNC_IO) { // Don't add sockets back to the poller. // The handler will initiate all further I/O if (status != SocketEvent.OPEN_WRITE) {
getProtocol().addWaitingProcessor(processor);
}
} elseif (state == SocketState.SUSPENDED) { // Don't add sockets back to the poller. // The resumeProcessing() method will add this socket // to the poller.
} else { // Connection closed. OK to recycle the processor. // Processors handling upgrades require additional clean-up // before release. if (processor != null && processor.isUpgrade()) {
UpgradeToken upgradeToken = processor.getUpgradeToken();
HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
InstanceManager instanceManager = upgradeToken.getInstanceManager(); if (instanceManager == null) {
httpUpgradeHandler.destroy();
} else {
ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null); try {
httpUpgradeHandler.destroy();
} finally { try {
instanceManager.destroyInstance(httpUpgradeHandler);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e);
getLog().error(sm.getString("abstractConnectionHandler.error"), e);
}
upgradeToken.getContextBind().unbind(false, oldCL);
}
}
}
release(processor);
processor = null;
}
if (processor != null) {
wrapper.setCurrentProcessor(processor);
} return state;
} catch (SocketException e) { // SocketExceptions are normal
getLog().debug(sm.getString("abstractConnectionHandler.socketexception.debug"), e);
} catch (IOException e) { // IOExceptions are normal
getLog().debug(sm.getString("abstractConnectionHandler.ioexception.debug"), e);
} catch (ProtocolException e) { // Protocol exceptions normally mean the client sent invalid or // incomplete data.
getLog().debug(sm.getString("abstractConnectionHandler.protocolexception.debug"), e);
} // Future developers: if you discover any other // rare-but-nonfatal exceptions, catch them here, and log as // above. catch (OutOfMemoryError oome) { // Try and handle this here to give Tomcat a chance to close the // connection and prevent clients waiting until they time out. // Worst case, it isn't recoverable and the attempt at logging // will trigger another OOME.
getLog().error(sm.getString("abstractConnectionHandler.oome"), oome);
} catch (Throwable e) {
ExceptionUtils.handleThrowable(e); // any other exception or error is odd. Here we log it // with "ERROR" level, so it will show up even on // less-than-verbose logs.
getLog().error(sm.getString("abstractConnectionHandler.error"), e);
}
// Make sure socket/processor is removed from the list of current // connections
release(processor); return SocketState.CLOSED;
}
protectedvoid longPoll(SocketWrapperBase<?> socket, Processor processor) { if (!processor.isAsync()) { // This is currently only used with HTTP // Either: // - this is an upgraded connection // - the request line/headers have not been completely // read
socket.registerReadInterest();
}
}
/** * Expected to be used by the handler once the processor is no longer required. Care must be taken to ensure * that this method is only called once per processor, after the request processing has completed. * * @param processor Processor being released (that was associated with the socket)
*/ privatevoid release(Processor processor) { if (processor != null) {
processor.recycle(); if (processor.isUpgrade()) { // While UpgradeProcessor instances should not normally be // present in waitingProcessors there are various scenarios // where this can happen. E.g.: // - when AsyncIO is used // - WebSocket I/O error on non-container thread // Err on the side of caution and always try and remove any // UpgradeProcessor instances from waitingProcessors
getProtocol().removeWaitingProcessor(processor);
} else { // After recycling, only instances of UpgradeProcessorBase // will return true for isUpgrade(). // Instances of UpgradeProcessorBase should not be added to // recycledProcessors since that pool is only for AJP or // HTTP processors
recycledProcessors.push(processor); if (getLog().isDebugEnabled()) {
getLog().debug("Pushed Processor [" + processor + "]");
}
}
}
}
/** * Expected to be used by the Endpoint to release resources on socket close, errors etc.
*/
@Override publicvoid release(SocketWrapperBase<S> socketWrapper) {
Processor processor = (Processor) socketWrapper.takeCurrentProcessor();
release(processor);
}
@Override publicfinalvoid pause() { /* * Inform all the processors associated with current connections that the endpoint is being paused. Most * won't care. Those processing multiplexed streams may wish to take action. For example, HTTP/2 may wish to * stop accepting new streams. * * Note that even if the endpoint is resumed, there is (currently) no API to inform the Processors of this.
*/ for (SocketWrapperBase<S> wrapper : proto.getEndpoint().getConnections()) {
Processor processor = (Processor) wrapper.getCurrentProcessor(); if (processor != null) {
processor.pause();
}
}
}
}
privatefinaltransient ConnectionHandler<?> handler; protectedfinal AtomicInteger size = new AtomicInteger(0);
public RecycledProcessors(ConnectionHandler<?> handler) { this.handler = handler;
}
@SuppressWarnings("sync-override") // Size may exceed cache size a bit
@Override publicboolean push(Processor processor) { int cacheSize = handler.getProtocol().getProcessorCache(); boolean offer = cacheSize == -1 ? true : size.get() < cacheSize; // avoid over growing our cache or add after we have stopped boolean result = false; if (offer) {
result = super.push(processor); if (result) {
size.incrementAndGet();
}
} if (!result) {
handler.unregister(processor);
} return result;
}
@SuppressWarnings("sync-override") // OK if size is too big briefly
@Override public Processor pop() {
Processor result = super.pop(); if (result != null) {
size.decrementAndGet();
} return result;
}
@Override publicsynchronizedvoid clear() {
Processor next = pop(); while (next != null) {
handler.unregister(next);
next = pop();
} super.clear();
size.set(0);
}
}
}
¤ Dauer der Verarbeitung: 0.14 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.