package org.pasif.server.http;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;
import javax.swing.JTextArea;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
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.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.ResponseConnControl;
import org.apache.http.protocol.ResponseContent;
import org.apache.http.protocol.ResponseDate;
import org.apache.http.protocol.ResponseServer;
import org.pas.utils.Utils;
public class PasAysHttpFileServer {
private JTextArea output;
private int port;
private String folder;
public PasAysHttpFileServer(JTextArea output, int port, String folder) {
this.output = output;
this.port = port;
this.folder = folder;
}
public void start() {
HttpParams params = new BasicHttpParams();
params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 30000).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");
BasicHttpProcessor httpproc = new BasicHttpProcessor();
httpproc.addInterceptor(new ResponseDate());
httpproc.addInterceptor(new ResponseServer());
httpproc.addInterceptor(new ResponseContent());
httpproc.addInterceptor(new ResponseConnControl());
AsyncNHttpServiceHandler handler = new AsyncNHttpServiceHandler(
httpproc, new DefaultHttpResponseFactory(),
new DefaultConnectionReuseStrategy(), params);
final HttpFileHandler filehandler = new HttpFileHandler(folder, true);
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);
try {
output.append(Utils.getCurrentTimeToString() + ":开启服务器,端口:9999\n");
output.setCaretPosition(output.getText().length());
ListeningIOReactor ioReactor = new DefaultListeningIOReactor(2, params);
ioReactor.listen(new InetSocketAddress(port));
ioReactor.execute(ioEventDispatch);
} catch (InterruptedIOException ex) {
System.err.println("Interrupted");
} catch (IOException e) {
System.err.println("I/O error: " + e.getMessage());
}
output.append(Utils.getCurrentTimeToString() + "服务器关闭。\n");
output.setCaretPosition(output.getText().length());
}
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;charset=UTF-8", useFileChannels);
response.setEntity(entity);
}
}
}
class FileWriteListener implements ContentListener {
private final File file;
private final FileInputStream inputFile;
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.inputFile = new FileInputStream(file);
this.fileChannel = inputFile.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,
Long.MAX_VALUE);
}
if (transferred > 0) {
idx += transferred;
}
}
public void finished() {
try {
inputFile.close();
} catch (IOException ignored) {
output.append(Utils.getCurrentTimeToString() + ignored.getMessage());
output.setCaretPosition(output.getText().length());
}
try {
fileChannel.close();
} catch (IOException ignored) {
output.append(Utils.getCurrentTimeToString() + ignored.getMessage());
output.setCaretPosition(output.getText().length());
}
}
}
class EventLogger implements EventListener {
public void connectionOpen(final NHttpConnection conn) {
output.append(Utils.getCurrentTimeToString() + conn + ":连接到服务器。\n");
output.setCaretPosition(output.getText().length());
}
public void connectionTimeout(final NHttpConnection conn) {
output.append(Utils.getCurrentTimeToString() + conn + ":连接超时。\n");
output.setCaretPosition(output.getText().length());
}
public void connectionClosed(final NHttpConnection conn) {
output.append(Utils.getCurrentTimeToString() + conn + ":远程关闭连接。\n");
output.setCaretPosition(output.getText().length());
}
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());
}
public void fatalProtocolException(org.apache.http.HttpException ex, NHttpConnection conn) {
System.err.println("HTTP error: " + ex.getMessage());
}
}
}