/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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 io.undertow.server.handlers;
import io.undertow.Handlers;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.HttpUpgradeListener;
import io.undertow.util.CopyOnWriteMap;
import io.undertow.util.Headers;
import io.undertow.util.Methods;
import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
import org.xnio.StreamConnection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
An HTTP request handler which upgrades the HTTP request and hands it off as a socket to any XNIO consumer.
Author: David M. Lloyd, Stuart Douglas
/**
* An HTTP request handler which upgrades the HTTP request and hands it off as a socket to any XNIO consumer.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
* @author Stuart Douglas
*/
public final class ChannelUpgradeHandler implements HttpHandler {
private final CopyOnWriteMap<String, List<Holder>> handlers = new CopyOnWriteMap<>();
private volatile HttpHandler nonUpgradeHandler = ResponseCodeHandler.HANDLE_404;
Add a protocol to this handler.
Params: - productString – the product string to match
- openListener – the open listener to call
- handshake – a handshake implementation that can be used to verify the client request and modify the response
/**
* Add a protocol to this handler.
*
* @param productString the product string to match
* @param openListener the open listener to call
* @param handshake a handshake implementation that can be used to verify the client request and modify the response
*/
public synchronized void addProtocol(String productString, ChannelListener<? super StreamConnection> openListener, final HttpUpgradeHandshake handshake) {
addProtocol(productString, null, openListener, handshake);
}
Add a protocol to this handler.
Params: - productString – the product string to match
- openListener – the open listener to call
- handshake – a handshake implementation that can be used to verify the client request and modify the response
/**
* Add a protocol to this handler.
*
* @param productString the product string to match
* @param openListener the open listener to call
* @param handshake a handshake implementation that can be used to verify the client request and modify the response
*/
public synchronized void addProtocol(String productString, HttpUpgradeListener openListener, final HttpUpgradeHandshake handshake) {
addProtocol(productString, openListener, null, handshake);
}
private synchronized void addProtocol(String productString, HttpUpgradeListener openListener, final ChannelListener<? super StreamConnection> channelListener, final HttpUpgradeHandshake handshake) {
if (productString == null) {
throw new IllegalArgumentException("productString is null");
}
if (openListener == null && channelListener == null) {
throw new IllegalArgumentException("openListener is null");
}
if(openListener == null) {
openListener = new HttpUpgradeListener() {
@Override
public void handleUpgrade(StreamConnection streamConnection, HttpServerExchange exchange) {
ChannelListeners.invokeChannelListener(streamConnection, channelListener);
}
};
}
List<Holder> list = handlers.get(productString);
if (list == null) {
handlers.put(productString, list = new CopyOnWriteArrayList<>());
}
list.add(new Holder(openListener, handshake, channelListener));
}
Add a protocol to this handler.
Params: - productString – the product string to match
- openListener – the open listener to call
/**
* Add a protocol to this handler.
*
* @param productString the product string to match
* @param openListener the open listener to call
*/
public void addProtocol(String productString, ChannelListener<? super StreamConnection> openListener) {
addProtocol(productString, openListener, null);
}
Add a protocol to this handler.
Params: - productString – the product string to match
- openListener – the open listener to call
/**
* Add a protocol to this handler.
*
* @param productString the product string to match
* @param openListener the open listener to call
*/
public void addProtocol(String productString, HttpUpgradeListener openListener) {
addProtocol(productString, openListener, null);
}
Remove a protocol from this handler. This will remove all upgrade handlers that match the product string
Params: - productString – the product string to match
/**
* Remove a protocol from this handler. This will remove all upgrade handlers that match the product string
*
* @param productString the product string to match
*/
public synchronized void removeProtocol(String productString) {
handlers.remove(productString);
}
Remove a protocol from this handler.
Params: - productString – the product string to match
- openListener – The open listener
/**
* Remove a protocol from this handler.
*
* @param productString the product string to match
* @param openListener The open listener
*/
public synchronized void removeProtocol(String productString, ChannelListener<? super StreamConnection> openListener) {
List<Holder> holders = handlers.get(productString);
if (holders == null) {
return;
}
Iterator<Holder> it = holders.iterator();
while (it.hasNext()) {
Holder holder = it.next();
if (holder.channelListener == openListener) {
holders.remove(holder);
break;
}
}
if (holders.isEmpty()) {
handlers.remove(productString);
}
}
Remove a protocol from this handler.
Params: - productString – the product string to match
- upgradeListener – The upgrade listener
/**
* Remove a protocol from this handler.
*
* @param productString the product string to match
* @param upgradeListener The upgrade listener
*/
public synchronized void removeProtocol(String productString, HttpUpgradeListener upgradeListener) {
List<Holder> holders = handlers.get(productString);
if (holders == null) {
return;
}
Iterator<Holder> it = holders.iterator();
while (it.hasNext()) {
Holder holder = it.next();
if (holder.listener == upgradeListener) {
holders.remove(holder);
break;
}
}
if (holders.isEmpty()) {
handlers.remove(productString);
}
}
Get the non-upgrade delegate handler.
Returns: the non-upgrade delegate handler
/**
* Get the non-upgrade delegate handler.
*
* @return the non-upgrade delegate handler
*/
public HttpHandler getNonUpgradeHandler() {
return nonUpgradeHandler;
}
Set the non-upgrade delegate handler.
Params: - nonUpgradeHandler – the non-upgrade delegate handler
/**
* Set the non-upgrade delegate handler.
*
* @param nonUpgradeHandler the non-upgrade delegate handler
*/
public ChannelUpgradeHandler setNonUpgradeHandler(final HttpHandler nonUpgradeHandler) {
Handlers.handlerNotNull(nonUpgradeHandler);
this.nonUpgradeHandler = nonUpgradeHandler;
return this;
}
public void handleRequest(final HttpServerExchange exchange) throws Exception {
final List<String> upgradeStrings = exchange.getRequestHeaders().get(Headers.UPGRADE);
if (upgradeStrings != null && exchange.getRequestMethod().equals(Methods.GET)) {
for (String string : upgradeStrings) {
final List<Holder> holders = handlers.get(string);
if (holders != null) {
for (Holder holder : holders) {
final HttpUpgradeListener listener = holder.listener;
if (holder.handshake != null) {
if (!holder.handshake.handleUpgrade(exchange)) {
//handshake did not match, try again
continue;
}
}
exchange.upgradeChannel(string,listener);
exchange.endExchange();
return;
}
}
}
}
nonUpgradeHandler.handleRequest(exchange);
}
private static final class Holder {
final HttpUpgradeListener listener;
final HttpUpgradeHandshake handshake;
final ChannelListener<? super StreamConnection> channelListener;
private Holder(final HttpUpgradeListener listener, final HttpUpgradeHandshake handshake, ChannelListener<? super StreamConnection> channelListener) {
this.listener = listener;
this.handshake = handshake;
this.channelListener = channelListener;
}
}
}