package test.httpd;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
class ChannelState {
public ChannelState(FileChannel fchannel, long position, long total, ByteBuffer buffer) {
this.fchannel = fchannel;
this.position = position;
this.total_file_size = total;
this.buffer = buffer;
}
public FileChannel fchannel;
public ByteBuffer buffer;
public long position;
public long total_file_size;
}
public class webserver {
public static void main(String[] args) {
// File wwwBase=new File("h:/workspace/web server/");
// File wwwBase=new File("/home/jacasey/workspace/web server/");
File wwwBase = new File("d:/Temp/");
try {
ServerSocketChannel channel = ServerSocketChannel.open();
InetSocketAddress address = new InetSocketAddress(8080);
channel.socket().bind(address);
channel.configureBlocking(false);
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int n = selector.select();
if (n == 0) {
continue;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> ready = readyKeys.iterator();
while (ready.hasNext()) {
SelectionKey selkey = ready.next();
ready.remove();
try {
if (selkey.isValid() == false) {
continue;
} else if (selkey.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel) selkey.channel();
SocketChannel client = server.accept();
if (client == null) continue;
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
ByteBuffer buffer = ByteBuffer.allocate(3276);
SelectionKey key = client.keyFor(selector);
key.attach(buffer);
} else if (selkey.isReadable()) {
handle_ready_read(wwwBase, selkey);
} else if (selkey.isWritable()) {
handle_write_ready(selector, selkey);
}
} catch (IOException err) {
selkey.channel().close();
err.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void handle_write_ready(Selector selector, SelectionKey selkey) throws IOException,
ClosedChannelException {
SocketChannel client = (SocketChannel) selkey.channel();
ChannelState state = (ChannelState) selkey.attachment();
if (state.buffer != null) {
client.write(state.buffer);
if (state.buffer.hasRemaining()) {
state.buffer.compact();
client.register(selkey.selector(), SelectionKey.OP_WRITE);
selkey.attach(state);
} else {
transfer_file(selkey, client, state);
}
} else {
transfer_file(selkey, client, state);
}
}
@SuppressWarnings("unused")
public static void handle_ready_read(File wwwBase, SelectionKey selkey) throws IOException, MalformedURLException,
FileNotFoundException {
ByteBuffer buffer = (ByteBuffer) selkey.attachment();
SocketChannel client = (SocketChannel) selkey.channel();
long BYTES_READ = client.read(buffer);
int position = buffer.position();
buffer.flip();
if (BYTES_READ >= 4) {
// String tmp=new String(buffer.array());
// System.err.println(tmp);
boolean short_read = process_request(buffer);
if (short_read == false) {
// determine what sort of request it is
String requestMethod = "";
String queryString = process_query_string(buffer, requestMethod);
// File data = new File(wwwBase + queryString);
// FileInputStream fis = new FileInputStream(data);
// FileChannel fchannel = fis.getChannel();
// use the inbuilt class to guess the type of data we are going
// towrite back to the client
String contentType = "text/html";
// System.out.println("request: " + data.toString());
// contentType = URLConnection.guessContentTypeFromName(data.toString());
// if (data.isFile()) {
StringBuffer output = new StringBuffer();
output.append("HTTP/1.0 200 OK\r\n");
output.append("Server: proxy\r\n");
// output.append("Connection: close\r\n"); // eventually I
// will have to detect whether a client is capable of
// persistance OK for now.
output.append("Connection: keep-alive\r\n");
output.append("Content-Type: " + contentType + "\r\n");
output.append(("Content-Length: 21\r\n"));
output.append("\r\n");
buffer.clear();
buffer.put(output.toString().getBytes());
buffer.flip();
client.write(buffer);
ByteBuffer body = ByteBuffer.allocate(1024);
body.put("<h1>Hello world!</h1>".getBytes());
body.flip();
client.write(body);
selkey.cancel();
client.close();
// if (buffer.hasRemaining()) {
// // short header write
// buffer.compact();
//
// client.register(selkey.selector(), SelectionKey.OP_WRITE);
// selkey.attach(buffer);
// } else {
// transfer_file(selkey, client, new ChannelState(fchannel, 0, data.length(), buffer));
// }
// } else {
// // file does not exist
// }
} else {
// short read
client.register(selkey.selector(), SelectionKey.OP_READ);
// reset the buffer so that we can read more data in
buffer.position(position);
buffer.limit(buffer.capacity());
selkey.attach(buffer);
}
} else if (BYTES_READ > 0) {
// short read
client.register(selkey.selector(), SelectionKey.OP_READ);
// reset the buffer so that we can read more data in
buffer.position(position);
buffer.limit(buffer.capacity());
selkey.attach(buffer);
} else if (BYTES_READ == -1) {
System.err.println("closed");
client.close();
}
}
public static String process_query_string(ByteBuffer buffer, String requestMethod) {
byte[] request = new byte[buffer.position()];
buffer.flip();
buffer.get(request);
buffer.clear();
int i = 0;
for (; i < request.length; i++) {
if (request[i] == ' ') {
i++;
break;
}
requestMethod += (char) request[i];
}
// extract the query_string if we are dealing with a get request
StringBuffer queryString = new StringBuffer(100);
if (requestMethod.equalsIgnoreCase("GET")) {
for (; i < request.length; i++) {
if (request[i] == ' ') break;
queryString.append((char) request[i]);
}
} else {
System.err.println("unhandled method" + requestMethod);
// unhandled method
}
return queryString.toString();
}
public static boolean process_request(ByteBuffer buffer) {
boolean short_read = false;
byte END_HTTP[] = "\r\n\r\n".getBytes();
byte END_OF_REQUEST[] = new byte[4];
buffer.position(buffer.limit() - 4);
buffer.get(END_OF_REQUEST, 0, 4);
for (int j = 0; j < END_OF_REQUEST.length; j++) {
if (END_HTTP[j] != END_OF_REQUEST[j]) {
short_read = true;
break;
}
}
return short_read;
}
public static void transfer_file(SelectionKey selkey, SocketChannel client, ChannelState cs) throws IOException,
ClosedChannelException {
ByteBuffer buffer = cs.buffer;
// pipe the file channel data back to our socket channel
long write = cs.fchannel.transferTo(cs.position, 32768, client);
// if we have a short write select OP_WRITE
if (cs.position + write < cs.total_file_size) {
client.register(selkey.selector(), SelectionKey.OP_WRITE);
cs.position += write;
selkey.attach(cs);
} else {
cs.fchannel.close();
client.register(selkey.selector(), SelectionKey.OP_READ);
buffer.clear();
selkey.attach(buffer);
}
}
}