All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.server.DetectorConnectionFactory Maven / Gradle / Ivy

There is a newer version: 12.1.0.alpha0
Show newest version
//
//  ========================================================================
//  Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.server;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;

/**
 * A {@link ConnectionFactory} combining multiple {@link Detecting} instances that will upgrade to
 * the first one recognizing the bytes in the buffer.
 */
public class DetectorConnectionFactory extends AbstractConnectionFactory implements ConnectionFactory.Detecting
{
    private static final Logger LOG = Log.getLogger(DetectorConnectionFactory.class);

    private final List _detectingConnectionFactories;

    /**
     * 

When the first bytes are not recognized by the {@code detectingConnectionFactories}, the default behavior is to * upgrade to the protocol returned by {@link #findNextProtocol(Connector)}.

* @param detectingConnectionFactories the {@link Detecting} instances. */ public DetectorConnectionFactory(Detecting... detectingConnectionFactories) { super(toProtocolString(detectingConnectionFactories)); _detectingConnectionFactories = Arrays.asList(detectingConnectionFactories); for (Detecting detectingConnectionFactory : detectingConnectionFactories) { addBean(detectingConnectionFactory); } } private static String toProtocolString(Detecting... detectingConnectionFactories) { if (detectingConnectionFactories.length == 0) throw new IllegalArgumentException("At least one detecting instance is required"); // remove protocol duplicates while keeping their ordering -> use LinkedHashSet LinkedHashSet protocols = Arrays.stream(detectingConnectionFactories).map(ConnectionFactory::getProtocol).collect(Collectors.toCollection(LinkedHashSet::new)); String protocol = protocols.stream().collect(Collectors.joining("|", "[", "]")); if (LOG.isDebugEnabled()) LOG.debug("Detector generated protocol name : {}", protocol); return protocol; } /** * Performs a detection using multiple {@link ConnectionFactory.Detecting} instances and returns the aggregated outcome. * @param buffer the buffer to perform a detection against. * @return A {@link Detecting.Detection} value with the detection outcome of the {@code detectingConnectionFactories}. */ @Override public Detecting.Detection detect(ByteBuffer buffer) { if (LOG.isDebugEnabled()) LOG.debug("Detector {} detecting from buffer {} using {}", getProtocol(), BufferUtil.toHexString(buffer), _detectingConnectionFactories); boolean needMoreBytes = true; for (Detecting detectingConnectionFactory : _detectingConnectionFactories) { Detecting.Detection detection = detectingConnectionFactory.detect(buffer); if (detection == Detecting.Detection.RECOGNIZED) { if (LOG.isDebugEnabled()) LOG.debug("Detector {} recognized bytes using {}", getProtocol(), detection); return Detecting.Detection.RECOGNIZED; } needMoreBytes &= detection == Detection.NEED_MORE_BYTES; } if (LOG.isDebugEnabled()) LOG.debug("Detector {} {}", getProtocol(), (needMoreBytes ? "requires more bytes" : "failed to recognize bytes")); return needMoreBytes ? Detection.NEED_MORE_BYTES : Detection.NOT_RECOGNIZED; } /** * Utility method that performs an upgrade to the specified connection factory, disposing of the given resources when needed. * @param connectionFactory the connection factory to upgrade to. * @param connector the connector. * @param endPoint the endpoint. */ protected static void upgradeToConnectionFactory(ConnectionFactory connectionFactory, Connector connector, EndPoint endPoint) throws IllegalStateException { if (LOG.isDebugEnabled()) LOG.debug("Upgrading to connection factory {}", connectionFactory); if (connectionFactory == null) throw new IllegalStateException("Cannot upgrade: connection factory must not be null for " + endPoint); Connection nextConnection = connectionFactory.newConnection(connector, endPoint); if (!(nextConnection instanceof Connection.UpgradeTo)) throw new IllegalStateException("Cannot upgrade: " + nextConnection + " does not implement " + Connection.UpgradeTo.class.getName() + " for " + endPoint); endPoint.upgrade(nextConnection); if (LOG.isDebugEnabled()) LOG.debug("Upgraded to connection factory {} and released buffer", connectionFactory); } /** *

Callback method called when detection was unsuccessful. * This implementation upgrades to the protocol returned by {@link #findNextProtocol(Connector)}.

* @param connector the connector. * @param endPoint the endpoint. * @param buffer the buffer. */ protected void nextProtocol(Connector connector, EndPoint endPoint, ByteBuffer buffer) throws IllegalStateException { String nextProtocol = findNextProtocol(connector); if (LOG.isDebugEnabled()) LOG.debug("Detector {} detection unsuccessful, found '{}' as the next protocol to upgrade to", getProtocol(), nextProtocol); if (nextProtocol == null) throw new IllegalStateException("Cannot find protocol following '" + getProtocol() + "' in connector's protocol list " + connector.getProtocols() + " for " + endPoint); upgradeToConnectionFactory(connector.getConnectionFactory(nextProtocol), connector, endPoint); } @Override public Connection newConnection(Connector connector, EndPoint endPoint) { return configure(new DetectorConnection(endPoint, connector), connector, endPoint); } private class DetectorConnection extends AbstractConnection implements Connection.UpgradeFrom, Connection.UpgradeTo { private final Connector _connector; private final ByteBuffer _buffer; private DetectorConnection(EndPoint endp, Connector connector) { super(endp, connector.getExecutor()); _connector = connector; _buffer = connector.getByteBufferPool().acquire(getInputBufferSize(), true); } @Override public void onUpgradeTo(ByteBuffer buffer) { if (LOG.isDebugEnabled()) LOG.debug("Detector {} copying unconsumed buffer {}", getProtocol(), BufferUtil.toDetailString(buffer)); BufferUtil.append(_buffer, buffer); } @Override public ByteBuffer onUpgradeFrom() { if (_buffer.hasRemaining()) { ByteBuffer unconsumed = ByteBuffer.allocateDirect(_buffer.remaining()); unconsumed.put(_buffer); unconsumed.flip(); _connector.getByteBufferPool().release(_buffer); return unconsumed; } return null; } @Override public void onOpen() { super.onOpen(); if (!detectAndUpgrade()) fillInterested(); } @Override public void onFillable() { try { while (BufferUtil.space(_buffer) > 0) { // Read data int fill = getEndPoint().fill(_buffer); if (LOG.isDebugEnabled()) LOG.debug("Detector {} filled buffer with {} bytes", getProtocol(), fill); if (fill < 0) { _connector.getByteBufferPool().release(_buffer); getEndPoint().shutdownOutput(); return; } if (fill == 0) { fillInterested(); return; } if (detectAndUpgrade()) return; } // all Detecting instances want more bytes than this buffer can store LOG.warn("Detector {} failed to detect upgrade target on {} for {}", getProtocol(), _detectingConnectionFactories, getEndPoint()); releaseAndClose(); } catch (Throwable x) { LOG.warn("Detector {} error for {}", getProtocol(), getEndPoint(), x); releaseAndClose(); } } /** * @return true when upgrade was performed, false otherwise. */ private boolean detectAndUpgrade() { if (BufferUtil.isEmpty(_buffer)) { if (LOG.isDebugEnabled()) LOG.debug("Detector {} skipping detection on an empty buffer", getProtocol()); return false; } if (LOG.isDebugEnabled()) LOG.debug("Detector {} performing detection with {} bytes", getProtocol(), _buffer.remaining()); boolean notRecognized = true; for (Detecting detectingConnectionFactory : _detectingConnectionFactories) { Detecting.Detection detection = detectingConnectionFactory.detect(_buffer); if (LOG.isDebugEnabled()) LOG.debug("Detector {} performed detection from {} with {} which returned {}", getProtocol(), BufferUtil.toDetailString(_buffer), detectingConnectionFactory, detection); if (detection == Detecting.Detection.RECOGNIZED) { try { // This DetectingConnectionFactory recognized those bytes -> upgrade to the next one. Connection nextConnection = detectingConnectionFactory.newConnection(_connector, getEndPoint()); if (!(nextConnection instanceof UpgradeTo)) throw new IllegalStateException("Cannot upgrade: " + nextConnection + " does not implement " + UpgradeTo.class.getName()); getEndPoint().upgrade(nextConnection); if (LOG.isDebugEnabled()) LOG.debug("Detector {} upgraded to {}", getProtocol(), nextConnection); return true; } catch (DetectionFailureException e) { // It's just bubbling up from a nested Detector, so it's already handled, just rethrow it. if (LOG.isDebugEnabled()) LOG.debug("Detector {} failed to upgrade, rethrowing", getProtocol(), e); throw e; } catch (Exception e) { // Two reasons that can make us end up here: // 1) detectingConnectionFactory.newConnection() failed? probably because it cannot find the next protocol // 2) nextConnection is not instanceof UpgradeTo // -> release the resources before rethrowing as DetectionFailureException if (LOG.isDebugEnabled()) LOG.debug("Detector {} failed to upgrade", getProtocol()); releaseAndClose(); throw new DetectionFailureException(e); } } notRecognized &= detection == Detecting.Detection.NOT_RECOGNIZED; } if (notRecognized) { // No DetectingConnectionFactory recognized those bytes -> call unsuccessful detection callback. if (LOG.isDebugEnabled()) LOG.debug("Detector {} failed to detect a known protocol, falling back to nextProtocol()", getProtocol()); nextProtocol(_connector, getEndPoint(), _buffer); if (LOG.isDebugEnabled()) LOG.debug("Detector {} call to nextProtocol() succeeded, assuming upgrade performed", getProtocol()); return true; } return false; } private void releaseAndClose() { if (LOG.isDebugEnabled()) LOG.debug("Detector {} releasing buffer and closing", getProtocol()); _connector.getByteBufferPool().release(_buffer); close(); } } private static class DetectionFailureException extends RuntimeException { public DetectionFailureException(Throwable cause) { super(cause); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy