de.unkrig.commons.net.http.HttpClientConnectionHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of de-unkrig-commons Show documentation
Show all versions of de-unkrig-commons Show documentation
A versatile Java(TM) library that implements many useful container and utility classes.
/*
* de.unkrig.commons - A general-purpose Java class library
*
* Copyright (c) 2012, Arno Unkrig
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package de.unkrig.commons.net.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.logging.Logger;
import de.unkrig.commons.io.FileBufferedChannel;
import de.unkrig.commons.io.Multiplexer;
import de.unkrig.commons.lang.protocol.ConsumerWhichThrows;
import de.unkrig.commons.lang.protocol.Stoppable;
import de.unkrig.commons.net.http.HttpResponse.Status;
import de.unkrig.commons.net.http.servlett.Servlett;
import de.unkrig.commons.util.collections.IterableUtil;
/**
* A container of {@link Servlett}s that handle requests that are received from a client connection.
*/
public
class HttpClientConnectionHandler implements Stoppable {
private static final Logger LOGGER = Logger.getLogger(HttpClientConnectionHandler.class.getName());
private Iterable servletts;
private final Collection stoppables = Collections.synchronizedCollection(new HashSet());
public
HttpClientConnectionHandler() {
this.servletts = Collections.emptyList();
}
public
HttpClientConnectionHandler(Servlett servlett) {
this.servletts = Collections.singletonList(servlett);
}
public
HttpClientConnectionHandler(Iterable servletts) {
this.servletts = servletts;
}
/**
* @return The {@link Servlett}s currently registered with this {@link HttpClientConnectionHandler}
*/
public Iterable
getServletts() {
return IterableUtil.unmodifiableIterable(this.servletts);
}
/**
* Clears the set of currently registerd {@link Servlett}s and registers the given {@code servlett}.
*/
public void
setServlett(Servlett servlett) {
this.servletts = Collections.singletonList(servlett);
}
/**
* Clears the set of currently registerd {@link Servlett}s and registers the given {@code servletts}.
*/
public void
setServletts(Iterable servletts) {
this.servletts = servletts;
}
/**
* This one is called from the {@link HttpServer}. Override if you want to process the connection before
* request processing begins.
*
* @param localSocketAddress Could be examined by implementations that override this method
* @param remoteSocketAddress Could be examined by implementations that override this method
* @param stoppable Stopping this will break the connection
*/
public void
handleConnection(
InputStream in,
OutputStream out,
InetSocketAddress localSocketAddress,
InetSocketAddress remoteSocketAddress,
Stoppable stoppable
) throws IOException, InvalidHttpMessageException {
this.processRequests(in, out, stoppable);
}
/**
* This one is called from the {@link NioHttpServer}. Override if you want to process the connection before
* request processing begins.
*
* @param localSocketAddress Could be examined by implementations that override this method
* @param remoteSocketAddress Could be examined by implementations that override this method
* @param stoppable Stopping this will break the connection
*/
public void
handleConnection(
ReadableByteChannel in,
WritableByteChannel out,
InetSocketAddress localSocketAddress,
InetSocketAddress remoteSocketAddress,
Multiplexer multiplexer,
Stoppable stoppable
) throws IOException {
this.processRequests(in, out, multiplexer, stoppable);
}
/**
* Processes HTTP requests from the client until the connection breaks.
*
* @param stoppable Stopping this will break the connection
*/
protected void
processRequests(InputStream in, OutputStream out, Stoppable stoppable)
throws IOException, InvalidHttpMessageException {
try {
this.stoppables.add(stoppable);
LOGGER.fine("Reading request from client");
HttpRequest httpRequest = HttpRequest.read(in);
HttpResponse httpResponse;
HANDLE:
{
for (Servlett servlett : this.servletts) {
httpResponse = servlett.handleRequest(httpRequest);
if (httpResponse != null) break HANDLE;
}
httpResponse = HttpResponse.response(
Status.INTERNAL_SERVER_ERROR,
"None of " + this.servletts + " handled the request"
);
}
assert httpResponse != null; // Redundant.
LOGGER.fine("Sending response to client");
httpResponse.write(out);
httpRequest.removeBody().dispose();
} finally {
this.stoppables.remove(stoppable);
if (this.stoppables.isEmpty()) {
for (Servlett servlett : this.servletts) {
try { servlett.close(); } catch (IOException ioe) {}
}
}
}
}
private void
processRequests(
final ReadableByteChannel in,
final WritableByteChannel out,
final Multiplexer multiplexer,
Stoppable stoppable
) throws IOException {
this.stoppables.add(stoppable);
final FileBufferedChannel fbc = new FileBufferedChannel(multiplexer, (SelectableChannel) out);
ConsumerWhichThrows requestConsumer = (
new ConsumerWhichThrows() {
@Override public void
consume(HttpRequest httpRequest) throws IOException {
HttpResponse httpResponse;
HANDLE:
{
for (Servlett servlett : HttpClientConnectionHandler.this.servletts) {
HttpResponse tmp = servlett.handleRequest(httpRequest);
if (tmp != null) {
httpResponse = tmp;
break HANDLE;
}
}
httpResponse = HttpResponse.response(
Status.INTERNAL_SERVER_ERROR,
"None of " + HttpClientConnectionHandler.this.servletts + " handled the request"
);
}
httpRequest.removeBody().dispose();
LOGGER.fine("Sending response to client");
httpResponse.write(Channels.newOutputStream(fbc));
}
}
);
HttpRequest.read(in, multiplexer, requestConsumer);
}
@Override public void
stop() {
for (Stoppable stoppable : this.stoppables) stoppable.stop();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy