blob: 74c5e261a26e38a92291c9b2828a6aa0692513a4 [file] [log] [blame]
/*
* ====================================================================
* 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.examples.nio;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.HttpStatus;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
import org.apache.http.impl.nio.reactor.DefaultListeningIOReactor;
import org.apache.http.nio.ContentDecoder;
import org.apache.http.nio.ContentDecoderChannel;
import org.apache.http.nio.FileContentDecoder;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.entity.ConsumingNHttpEntity;
import org.apache.http.nio.entity.ConsumingNHttpEntityTemplate;
import org.apache.http.nio.entity.ContentListener;
import org.apache.http.nio.entity.NFileEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.nio.protocol.AsyncNHttpServiceHandler;
import org.apache.http.nio.protocol.EventListener;
import org.apache.http.nio.protocol.NHttpRequestHandler;
import org.apache.http.nio.protocol.NHttpRequestHandlerResolver;
import org.apache.http.nio.protocol.SimpleNHttpRequestHandler;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.nio.reactor.ListeningIOReactor;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.SyncBasicHttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
/**
* Basic, yet fully functional and spec compliant, HTTP/1.1 file server based on the non-blocking
* I/O model.
* <p>
* Please note the purpose of this application is demonstrate the usage of HttpCore APIs.
* It is NOT intended to demonstrate the most efficient way of building an HTTP file server.
*
*
*/
public class NHttpFileServer {
public static void main(String[] args) throws Exception {
if (args.length < 1) {
System.err.println("Please specify document root directory");
System.exit(1);
}
boolean useFileChannels = true;
if (args.length >= 2) {
String s = args[1];
if (s.equalsIgnoreCase("disableFileChannels")) {
useFileChannels = false;
}
}
HttpParams params = new SyncBasicHttpParams();
params
.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 20000)
.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
.setParameter(CoreProtocolPNames.ORIGIN_SERVER, "HttpComponents/1.1");
HttpProcessor httpproc = new ImmutableHttpProcessor(new HttpResponseInterceptor[] {
new ResponseDate(),
new ResponseServer(),
new ResponseContent(),
new ResponseConnControl()
});
AsyncNHttpServiceHandler handler = new AsyncNHttpServiceHandler(
httpproc,
new DefaultHttpResponseFactory(),
new DefaultConnectionReuseStrategy(),
params);
final HttpFileHandler filehandler = new HttpFileHandler(args[0], useFileChannels);
NHttpRequestHandlerResolver resolver = new NHttpRequestHandlerResolver() {
public NHttpRequestHandler lookup(String requestURI) {
return filehandler;
}
};
handler.setHandlerResolver(resolver);
// Provide an event logger
handler.setEventListener(new EventLogger());
IOEventDispatch ioEventDispatch = new DefaultServerIOEventDispatch(handler, params);
ListeningIOReactor ioReactor = new DefaultListeningIOReactor(2, params);
try {
ioReactor.listen(new InetSocketAddress(8080));
ioReactor.execute(ioEventDispatch);
} catch (InterruptedIOException ex) {
System.err.println("Interrupted");
} catch (IOException e) {
System.err.println("I/O error: " + e.getMessage());
}
System.out.println("Shutdown");
}
static class HttpFileHandler extends SimpleNHttpRequestHandler {
private final String docRoot;
private final boolean useFileChannels;
public HttpFileHandler(final String docRoot, boolean useFileChannels) {
this.docRoot = docRoot;
this.useFileChannels = useFileChannels;
}
public ConsumingNHttpEntity entityRequest(
final HttpEntityEnclosingRequest request,
final HttpContext context) throws HttpException, IOException {
return new ConsumingNHttpEntityTemplate(
request.getEntity(),
new FileWriteListener(useFileChannels));
}
@Override
public void handle(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
String target = request.getRequestLine().getUri();
final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8"));
if (!file.exists()) {
response.setStatusCode(HttpStatus.SC_NOT_FOUND);
NStringEntity entity = new NStringEntity(
"<html><body><h1>File" + file.getPath() +
" not found</h1></body></html>",
"UTF-8");
entity.setContentType("text/html; charset=UTF-8");
response.setEntity(entity);
} else if (!file.canRead() || file.isDirectory()) {
response.setStatusCode(HttpStatus.SC_FORBIDDEN);
NStringEntity entity = new NStringEntity(
"<html><body><h1>Access denied</h1></body></html>",
"UTF-8");
entity.setContentType("text/html; charset=UTF-8");
response.setEntity(entity);
} else {
response.setStatusCode(HttpStatus.SC_OK);
NFileEntity entity = new NFileEntity(file, "text/html", useFileChannels);
response.setEntity(entity);
}
}
}
static class FileWriteListener implements ContentListener {
private final File file;
private final FileOutputStream outputFile;
private final FileChannel fileChannel;
private final boolean useFileChannels;
private long idx = 0;
public FileWriteListener(boolean useFileChannels) throws IOException {
this.file = File.createTempFile("tmp", ".tmp", null);
this.outputFile = new FileOutputStream(file, true);
this.fileChannel = outputFile.getChannel();
this.useFileChannels = useFileChannels;
}
public void contentAvailable(ContentDecoder decoder, IOControl ioctrl)
throws IOException {
long transferred;
if(useFileChannels && decoder instanceof FileContentDecoder) {
transferred = ((FileContentDecoder) decoder).transfer(
fileChannel, idx, Long.MAX_VALUE);
} else {
transferred = fileChannel.transferFrom(
new ContentDecoderChannel(decoder), idx, Integer.MAX_VALUE);
}
if(transferred > 0)
idx += transferred;
}
public void finished() {
try {
outputFile.close();
} catch(IOException ignored) {}
try {
fileChannel.close();
} catch(IOException ignored) {}
}
}
static class EventLogger implements EventListener {
public void connectionOpen(final NHttpConnection conn) {
System.out.println("Connection open: " + conn);
}
public void connectionTimeout(final NHttpConnection conn) {
System.out.println("Connection timed out: " + conn);
}
public void connectionClosed(final NHttpConnection conn) {
System.out.println("Connection closed: " + conn);
}
public void fatalIOException(final IOException ex, final NHttpConnection conn) {
System.err.println("I/O error: " + ex.getMessage());
}
public void fatalProtocolException(final HttpException ex, final NHttpConnection conn) {
System.err.println("HTTP error: " + ex.getMessage());
}
}
}