diff --git a/src/main/java/com/jenkov/nioserver/IMessageProcessor.java b/src/main/java/com/jenkov/nioserver/IMessageProcessor.java index 3d5e9b9..f05e880 100644 --- a/src/main/java/com/jenkov/nioserver/IMessageProcessor.java +++ b/src/main/java/com/jenkov/nioserver/IMessageProcessor.java @@ -1,10 +1,13 @@ package com.jenkov.nioserver; /** - * Created by jjenkov on 16-10-2015. + * Project: java-nio-server
+ * File: HttpUtilTest.java
+ * Created: 16 Oct 2015
+ * + * @author jjenkov */ public interface IMessageProcessor { - public void process(Message message, WriteProxy writeProxy); - -} + public void process(Message message, WriteProxy writeProxy); +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/IMessageReader.java b/src/main/java/com/jenkov/nioserver/IMessageReader.java index 19a6d1b..16ec876 100644 --- a/src/main/java/com/jenkov/nioserver/IMessageReader.java +++ b/src/main/java/com/jenkov/nioserver/IMessageReader.java @@ -5,16 +5,17 @@ import java.nio.ByteBuffer; import java.util.List; /** - * Created by jjenkov on 16-10-2015. + * Project: java-nio-server
+ * File: HttpUtilTest.java
+ * Created: 16 Oct 2015
+ * + * @author jjenkov */ public interface IMessageReader { - public void init(MessageBuffer readMessageBuffer); + public void init(MessageBuffer readMessageBuffer); - public void read(Socket socket, ByteBuffer byteBuffer) throws IOException; + public void read(Socket socket, ByteBuffer byteBuffer) throws IOException; - public List getMessages(); - - - -} + public List getMessages(); +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/IMessageReaderFactory.java b/src/main/java/com/jenkov/nioserver/IMessageReaderFactory.java index 66bb80e..39b3139 100644 --- a/src/main/java/com/jenkov/nioserver/IMessageReaderFactory.java +++ b/src/main/java/com/jenkov/nioserver/IMessageReaderFactory.java @@ -1,10 +1,13 @@ package com.jenkov.nioserver; /** - * Created by jjenkov on 16-10-2015. + * Project: java-nio-server
+ * File: HttpUtilTest.java
+ * Created: 16 Oct 2015
+ * + * @author jjenkov */ public interface IMessageReaderFactory { - public IMessageReader createMessageReader(); - -} + public IMessageReader createMessageReader(); +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/Message.java b/src/main/java/com/jenkov/nioserver/Message.java index d11579a..50ba073 100644 --- a/src/main/java/com/jenkov/nioserver/Message.java +++ b/src/main/java/com/jenkov/nioserver/Message.java @@ -3,103 +3,92 @@ package com.jenkov.nioserver; import java.nio.ByteBuffer; /** - * Created by jjenkov on 16-10-2015. + * Project: java-nio-server
+ * File: HttpUtilTest.java
+ * Created: 16 Oct 2015
+ * + * @author jjenkov */ public class Message { - private MessageBuffer messageBuffer = null; + private MessageBuffer messageBuffer; - public long socketId = 0; // the id of source socket or destination socket, depending on whether is going in or out. + public long socketId; // the id of source socket or destination socket, depending on whether is going + // in or out. - public byte[] sharedArray = null; - public int offset = 0; //offset into sharedArray where this message data starts. - public int capacity = 0; //the size of the section in the sharedArray allocated to this message. - public int length = 0; //the number of bytes used of the allocated section. + public byte[] sharedArray; + public int offset; // offset into sharedArray where this message data starts. + public int capacity; // the size of the section in the sharedArray allocated to this message. + public int length; // the number of bytes used of the allocated section. - public Object metaData = null; + public Object metaData; - public Message(MessageBuffer messageBuffer) { - this.messageBuffer = messageBuffer; - } + public Message(MessageBuffer messageBuffer) { this.messageBuffer = messageBuffer; } - /** - * Writes data from the ByteBuffer into this message - meaning into the buffer backing this message. - * - * @param byteBuffer The ByteBuffer containing the message data to write. - * @return - */ - public int writeToMessage(ByteBuffer byteBuffer){ - int remaining = byteBuffer.remaining(); + /** + * Writes data from the ByteBuffer into this message - meaning into the buffer + * backing this message. + * + * @param byteBuffer The ByteBuffer containing the message data to write. + * @return + */ + public int writeToMessage(ByteBuffer byteBuffer) { + int remaining = byteBuffer.remaining(); - while(this.length + remaining > capacity){ - if(!this.messageBuffer.expandMessage(this)) { - return -1; - } - } + while (this.length + remaining > capacity) + if (!this.messageBuffer.expandMessage(this)) return -1; - int bytesToCopy = Math.min(remaining, this.capacity - this.length); - byteBuffer.get(this.sharedArray, this.offset + this.length, bytesToCopy); - this.length += bytesToCopy; + int bytesToCopy = Math.min(remaining, capacity - length); + byteBuffer.get(sharedArray, offset + length, bytesToCopy); + length += bytesToCopy; - return bytesToCopy; - } + return bytesToCopy; + } + /** + * Writes data from the byte array into this message - meaning into the buffer + * backing this message. + * + * @param byteArray The byte array containing the message data to write. + * @return + */ + public int writeToMessage(byte[] byteArray) { return writeToMessage(byteArray, 0, byteArray.length); } + /** + * Writes data from the byte array into this message - meaning into the buffer + * backing this message. + * + * @param byteArray The byte array containing the message data to write. + * @return + */ + public int writeToMessage(byte[] byteArray, int offset, int length) { + int remaining = length; + while (this.length + remaining > capacity) + if (!this.messageBuffer.expandMessage(this)) return -1; - /** - * Writes data from the byte array into this message - meaning into the buffer backing this message. - * - * @param byteArray The byte array containing the message data to write. - * @return - */ - public int writeToMessage(byte[] byteArray){ - return writeToMessage(byteArray, 0, byteArray.length); - } + int bytesToCopy = Math.min(remaining, capacity - length); + System.arraycopy(byteArray, offset, sharedArray, offset + this.length, bytesToCopy); + this.length += bytesToCopy; + return bytesToCopy; + } + /** + * In case the buffer backing the nextMessage contains more than one HTTP + * message, move all data after the first + * message to a new Message object. + * + * @param message The message containing the partial message (after the first + * message). + * @param endIndex The end index of the first message in the buffer of the + * message given as parameter. + */ + public void writePartialMessageToMessage(Message message, int endIndex) { + int startIndexOfPartialMessage = message.offset + endIndex; + int lengthOfPartialMessage = message.offset + message.length - endIndex; - /** - * Writes data from the byte array into this message - meaning into the buffer backing this message. - * - * @param byteArray The byte array containing the message data to write. - * @return - */ - public int writeToMessage(byte[] byteArray, int offset, int length){ - int remaining = length; + System.arraycopy(message.sharedArray, startIndexOfPartialMessage, sharedArray, offset, lengthOfPartialMessage); + } - while(this.length + remaining > capacity){ - if(!this.messageBuffer.expandMessage(this)) { - return -1; - } - } - - int bytesToCopy = Math.min(remaining, this.capacity - this.length); - System.arraycopy(byteArray, offset, this.sharedArray, this.offset + this.length, bytesToCopy); - this.length += bytesToCopy; - return bytesToCopy; - } - - - - - /** - * In case the buffer backing the nextMessage contains more than one HTTP message, move all data after the first - * message to a new Message object. - * - * @param message The message containing the partial message (after the first message). - * @param endIndex The end index of the first message in the buffer of the message given as parameter. - */ - public void writePartialMessageToMessage(Message message, int endIndex){ - int startIndexOfPartialMessage = message.offset + endIndex; - int lengthOfPartialMessage = (message.offset + message.length) - endIndex; - - System.arraycopy(message.sharedArray, startIndexOfPartialMessage, this.sharedArray, this.offset, lengthOfPartialMessage); - } - - public int writeToByteBuffer(ByteBuffer byteBuffer){ - return 0; - } - - - -} + public int writeToByteBuffer(ByteBuffer byteBuffer) { return 0; } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/MessageBuffer.java b/src/main/java/com/jenkov/nioserver/MessageBuffer.java index bf78a54..6a22287 100644 --- a/src/main/java/com/jenkov/nioserver/MessageBuffer.java +++ b/src/main/java/com/jenkov/nioserver/MessageBuffer.java @@ -1,88 +1,83 @@ package com.jenkov.nioserver; /** - * A shared buffer which can contain many messages inside. A message gets a section of the buffer to use. If the - * message outgrows the section in size, the message requests a larger section and the message is copied to that - * larger section. The smaller section is then freed again. + * A shared buffer which can contain many messages inside. A message gets a + * section of the buffer to use. If the message outgrows the section in size, + * the message requests a larger section and the message is copied to that + * larger section. The smaller section is then freed again.
+ *
+ * Project: java-nio-server
+ * File: MessageBuffer.java
+ * Created: 18 Oct 2015
* - * - * Created by jjenkov on 18-10-2015. + * @author jjenkov */ public class MessageBuffer { - public static int KB = 1024; - public static int MB = 1024 * KB; + public static int KB = 1024; + public static int MB = 1024 * KB; - private static final int CAPACITY_SMALL = 4 * KB; - private static final int CAPACITY_MEDIUM = 128 * KB; - private static final int CAPACITY_LARGE = 1024 * KB; + private static final int CAPACITY_SMALL = 4 * KB; + private static final int CAPACITY_MEDIUM = 128 * KB; + private static final int CAPACITY_LARGE = 1 * MB; - //package scope (default) - so they can be accessed from unit tests. - byte[] smallMessageBuffer = new byte[1024 * 4 * KB]; //1024 x 4KB messages = 4MB. - byte[] mediumMessageBuffer = new byte[128 * 128 * KB]; // 128 x 128KB messages = 16MB. - byte[] largeMessageBuffer = new byte[16 * 1 * MB]; // 16 * 1MB messages = 16MB. + // package scope (default) - so they can be accessed from unit tests. + byte[] smallMessageBuffer = new byte[1024 * 4 * KB]; // 1024 x 4KB messages = 4MB. + byte[] mediumMessageBuffer = new byte[128 * 128 * KB]; // 128 x 128KB messages = 16MB. + byte[] largeMessageBuffer = new byte[16 * 1 * MB]; // 16 * 1MB messages = 16MB. - QueueIntFlip smallMessageBufferFreeBlocks = new QueueIntFlip(1024); // 1024 free sections - QueueIntFlip mediumMessageBufferFreeBlocks = new QueueIntFlip(128); // 128 free sections - QueueIntFlip largeMessageBufferFreeBlocks = new QueueIntFlip(16); // 16 free sections + QueueIntFlip smallMessageBufferFreeBlocks = new QueueIntFlip(1024); // 1024 free sections + QueueIntFlip mediumMessageBufferFreeBlocks = new QueueIntFlip(128); // 128 free sections + QueueIntFlip largeMessageBufferFreeBlocks = new QueueIntFlip(16); // 16 free sections - //todo make all message buffer capacities and block sizes configurable - //todo calculate free block queue sizes based on capacity and block size of buffers. + // TODO: make all message buffer capacities and block sizes configurable + // TODO: calculate free block queue sizes based on capacity and block size of + // buffers. - public MessageBuffer() { - //add all free sections to all free section queues. - for(int i=0; ijava-nio-server
+ * File: MessageWriter.java
+ * Created: 21 Oct 2015
+ * + * @author jjenkov */ public class MessageWriter { - private List writeQueue = new ArrayList<>(); - private Message messageInProgress = null; - private int bytesWritten = 0; + private List writeQueue = new ArrayList<>(); + private Message messageInProgress; + private int bytesWritten; - public MessageWriter() { - } + public void enqueue(Message message) { + if (messageInProgress == null) messageInProgress = message; + else writeQueue.add(message); + } - public void enqueue(Message message) { - if(this.messageInProgress == null){ - this.messageInProgress = message; - } else { - this.writeQueue.add(message); - } - } + public void write(Socket socket, ByteBuffer byteBuffer) throws IOException { + byteBuffer.put(messageInProgress.sharedArray, messageInProgress.offset + bytesWritten, messageInProgress.length - bytesWritten); + byteBuffer.flip(); - public void write(Socket socket, ByteBuffer byteBuffer) throws IOException { - byteBuffer.put(this.messageInProgress.sharedArray, this.messageInProgress.offset + this.bytesWritten, this.messageInProgress.length - this.bytesWritten); - byteBuffer.flip(); + bytesWritten += socket.write(byteBuffer); + byteBuffer.clear(); - this.bytesWritten += socket.write(byteBuffer); - byteBuffer.clear(); + if (bytesWritten >= messageInProgress.length) { + if (writeQueue.size() > 0) messageInProgress = writeQueue.remove(0); + else messageInProgress = null; + // TODO: unregister from selector + } + } - if(bytesWritten >= this.messageInProgress.length){ - if(this.writeQueue.size() > 0){ - this.messageInProgress = this.writeQueue.remove(0); - } else { - this.messageInProgress = null; - //todo unregister from selector - } - } - } - - public boolean isEmpty() { - return this.writeQueue.isEmpty() && this.messageInProgress == null; - } - -} + public boolean isEmpty() { return writeQueue.isEmpty() && messageInProgress == null; } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/QueueIntFlip.java b/src/main/java/com/jenkov/nioserver/QueueIntFlip.java index ca1efd7..d1338a8 100644 --- a/src/main/java/com/jenkov/nioserver/QueueIntFlip.java +++ b/src/main/java/com/jenkov/nioserver/QueueIntFlip.java @@ -1,186 +1,158 @@ package com.jenkov.nioserver; /** - * Same as QueueFillCount, except that QueueFlip uses a flip flag to keep track of when the internal writePos has - * "overflowed" (meaning it goes back to 0). Other than that, the two implementations are very similar in functionality. + * Same as QueueFillCount, except that QueueFlip uses a flip flag to keep track + * of when the internal writePos has "overflowed" (meaning it goes back to 0). + * Other than that, the two implementations are very similar in + * functionality.
+ *
+ * One additional difference is that QueueFlip has an available() method, where + * this is a public variable in QueueFillCount.
+ *
+ * Project: java-nio-server
+ * File: QueueIntFlip.java
+ * Created: 18 Oct 2015
* - * One additional difference is that QueueFlip has an available() method, where this is a public variable in - * QueueFillCount. - * - * Created by jjenkov on 18-09-2015. + * @author jjenkov */ public class QueueIntFlip { - public int[] elements = null; + public int[] elements; - public int capacity = 0; - public int writePos = 0; - public int readPos = 0; - public boolean flipped = false; + public int capacity; + public int writePos; + public int readPos; + public boolean flipped; - public QueueIntFlip(int capacity) { - this.capacity = capacity; - this.elements = new int[capacity]; //todo get from TypeAllocator ? - } + public QueueIntFlip(int capacity) { + this.capacity = capacity; + elements = new int[capacity]; // TODO: get from TypeAllocator ? + } - public void reset() { - this.writePos = 0; - this.readPos = 0; - this.flipped = false; - } + public void reset() { + writePos = 0; + readPos = 0; + flipped = false; + } - public int available() { - if(!flipped){ - return writePos - readPos; - } - return capacity - readPos + writePos; - } + public int available() { return flipped ? capacity - readPos + writePos : writePos - readPos; } - public int remainingCapacity() { - if(!flipped){ - return capacity - writePos; - } - return readPos - writePos; - } + public int remainingCapacity() { return flipped ? readPos - writePos : capacity - writePos; } - public boolean put(int element){ - if(!flipped){ - if(writePos == capacity){ - writePos = 0; - flipped = true; + public boolean put(int element) { + if (!flipped) { + if (writePos == capacity) { + writePos = 0; + flipped = true; - if(writePos < readPos){ - elements[writePos++] = element; - return true; - } else { - return false; - } - } else { - elements[writePos++] = element; - return true; - } - } else { - if(writePos < readPos ){ - elements[writePos++] = element; - return true; - } else { - return false; - } - } - } + if (writePos < readPos) { + elements[writePos++] = element; + return true; + } else return false; + } else { + elements[writePos++] = element; + return true; + } + } else { + if (writePos < readPos) { + elements[writePos++] = element; + return true; + } else return false; + } + } - public int put(int[] newElements, int length){ - int newElementsReadPos = 0; - if(!flipped){ - //readPos lower than writePos - free sections are: - //1) from writePos to capacity - //2) from 0 to readPos + public int put(int[] newElements, int length) { + int newElementsReadPos = 0; + if (!flipped) { + // readPos lower than writePos - free sections are: + // 1) from writePos to capacity + // 2) from 0 to readPos - if(length <= capacity - writePos){ - //new elements fit into top of elements array - copy directly - for(; newElementsReadPos < length; newElementsReadPos++){ - this.elements[this.writePos++] = newElements[newElementsReadPos]; - } + if (length <= capacity - writePos) { + // new elements fit into top of elements array - copy directly + for (; newElementsReadPos < length; newElementsReadPos++) + elements[writePos++] = newElements[newElementsReadPos]; - return newElementsReadPos; - } else { - //new elements must be divided between top and bottom of elements array + return newElementsReadPos; + } else { + // new elements must be divided between top and bottom of elements array - //writing to top - for(;this.writePos < capacity; this.writePos++){ - this.elements[this.writePos] = newElements[newElementsReadPos++]; - } + // writing to top + for (; writePos < capacity; writePos++) + elements[writePos] = newElements[newElementsReadPos++]; - //writing to bottom - this.writePos = 0; - this.flipped = true; - int endPos = Math.min(this.readPos, length - newElementsReadPos); - for(; this.writePos < endPos; this.writePos++){ - this.elements[writePos] = newElements[newElementsReadPos++]; - } + // writing to bottom + this.writePos = 0; + this.flipped = true; + int endPos = Math.min(readPos, length - newElementsReadPos); + for (; writePos < endPos; writePos++) + this.elements[writePos] = newElements[newElementsReadPos++]; + return newElementsReadPos; + } - return newElementsReadPos; - } + } else { + // readPos higher than writePos - free sections are: + // 1) from writePos to readPos - } else { - //readPos higher than writePos - free sections are: - //1) from writePos to readPos + int endPos = Math.min(readPos, writePos + length); - int endPos = Math.min(this.readPos, this.writePos + length); + for (; writePos < endPos; writePos++) + elements[writePos] = newElements[newElementsReadPos++]; - for(; this.writePos < endPos; this.writePos++){ - this.elements[this.writePos] = newElements[newElementsReadPos++]; - } + return newElementsReadPos; + } + } - return newElementsReadPos; - } - } + public int take() { + if (!flipped) return readPos < writePos ? elements[readPos++] : -1; + else { + if (readPos == capacity) { + readPos = 0; + flipped = false; + return readPos < writePos ? elements[readPos++] : -1; + } else return elements[readPos++]; + } + } - public int take() { - if(!flipped){ - if(readPos < writePos){ - return elements[readPos++]; - } else { - return -1; - } - } else { - if(readPos == capacity){ - readPos = 0; - flipped = false; + public int take(int[] into, int length) { + int intoWritePos = 0; + if (!flipped) { + // writePos higher than readPos - available section is writePos - readPos - if(readPos < writePos){ - return elements[readPos++]; - } else { - return -1; - } - } else { - return elements[readPos++]; - } - } - } + int endPos = Math.min(writePos, readPos + length); + for (; readPos < endPos; readPos++) + into[intoWritePos++] = elements[readPos]; + return intoWritePos; + } else { + // readPos higher than writePos - available sections are top + bottom of + // elements array - public int take(int[] into, int length){ - int intoWritePos = 0; - if(!flipped){ - //writePos higher than readPos - available section is writePos - readPos + if (length <= capacity - readPos) { + // length is lower than the elements available at the top of the elements array + // - copy directly + for (; intoWritePos < length; intoWritePos++) + into[intoWritePos] = elements[readPos++]; - int endPos = Math.min(this.writePos, this.readPos + length); - for(; this.readPos < endPos; this.readPos++){ - into[intoWritePos++] = this.elements[this.readPos]; - } - return intoWritePos; - } else { - //readPos higher than writePos - available sections are top + bottom of elements array + return intoWritePos; + } else { + // length is higher than elements available at the top of the elements array + // split copy into a copy from both top and bottom of elements array. - if(length <= capacity - readPos){ - //length is lower than the elements available at the top of the elements array - copy directly - for(; intoWritePos < length; intoWritePos++){ - into[intoWritePos] = this.elements[this.readPos++]; - } + // copy from top + for (; readPos < capacity; readPos++) + into[intoWritePos++] = elements[readPos]; - return intoWritePos; - } else { - //length is higher than elements available at the top of the elements array - //split copy into a copy from both top and bottom of elements array. + // copy from bottom + readPos = 0; + flipped = false; + int endPos = Math.min(writePos, length - intoWritePos); + for (; readPos < endPos; readPos++) + into[intoWritePos++] = elements[readPos]; - //copy from top - for(; this.readPos < capacity; this.readPos++){ - into[intoWritePos++] = this.elements[this.readPos]; - } - - //copy from bottom - this.readPos = 0; - this.flipped = false; - int endPos = Math.min(this.writePos, length - intoWritePos); - for(; this.readPos < endPos; this.readPos++){ - into[intoWritePos++] = this.elements[this.readPos]; - } - - return intoWritePos; - } - } - } - -} + return intoWritePos; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/Server.java b/src/main/java/com/jenkov/nioserver/Server.java index 7bf8aac..2926384 100644 --- a/src/main/java/com/jenkov/nioserver/Server.java +++ b/src/main/java/com/jenkov/nioserver/Server.java @@ -3,44 +3,44 @@ package com.jenkov.nioserver; import java.io.IOException; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; /** - * Created by jjenkov on 24-10-2015. + * Project: java-nio-server
+ * File: Server.java
+ * Created: 24 Oct 2015
+ * + * @author jjenkov */ public class Server { - private SocketAccepter socketAccepter = null; - private SocketProcessor socketProcessor = null; + private SocketAcceptor socketAccepter; + private SocketProcessor socketProcessor; - private int tcpPort = 0; - private IMessageReaderFactory messageReaderFactory = null; - private IMessageProcessor messageProcessor = null; + private int tcpPort; + private IMessageReaderFactory messageReaderFactory; + private IMessageProcessor messageProcessor; - public Server(int tcpPort, IMessageReaderFactory messageReaderFactory, IMessageProcessor messageProcessor) { - this.tcpPort = tcpPort; - this.messageReaderFactory = messageReaderFactory; - this.messageProcessor = messageProcessor; - } + public Server(int tcpPort, IMessageReaderFactory messageReaderFactory, IMessageProcessor messageProcessor) { + this.tcpPort = tcpPort; + this.messageReaderFactory = messageReaderFactory; + this.messageProcessor = messageProcessor; + } - public void start() throws IOException { + public void start() throws IOException { - Queue socketQueue = new ArrayBlockingQueue(1024); //move 1024 to ServerConfig + Queue socketQueue = new ArrayBlockingQueue<>(1024); // TODO: move 1024 to ServerConfig - this.socketAccepter = new SocketAccepter(tcpPort, socketQueue); + socketAccepter = new SocketAcceptor(tcpPort, socketQueue); + MessageBuffer readBuffer = new MessageBuffer(); + MessageBuffer writeBuffer = new MessageBuffer(); - MessageBuffer readBuffer = new MessageBuffer(); - MessageBuffer writeBuffer = new MessageBuffer(); + socketProcessor = new SocketProcessor(socketQueue, readBuffer, writeBuffer, this.messageReaderFactory, this.messageProcessor); - this.socketProcessor = new SocketProcessor(socketQueue, readBuffer, writeBuffer, this.messageReaderFactory, this.messageProcessor); + Thread accepterThread = new Thread(socketAccepter); + Thread processorThread = new Thread(socketProcessor); - Thread accepterThread = new Thread(this.socketAccepter); - Thread processorThread = new Thread(this.socketProcessor); - - accepterThread.start(); - processorThread.start(); - } - - -} + accepterThread.start(); + processorThread.start(); + } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/Socket.java b/src/main/java/com/jenkov/nioserver/Socket.java index 298e7bb..9cad6ea 100644 --- a/src/main/java/com/jenkov/nioserver/Socket.java +++ b/src/main/java/com/jenkov/nioserver/Socket.java @@ -5,51 +5,47 @@ import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; /** - * Created by jjenkov on 16-10-2015. + * Project: java-nio-server
+ * File: Socket.java
+ * Created: 16 Oct 2015
+ * + * @author jjenkov */ public class Socket { - public long socketId; + public long socketId; - public SocketChannel socketChannel = null; - public IMessageReader messageReader = null; - public MessageWriter messageWriter = null; + public SocketChannel socketChannel; + public IMessageReader messageReader; + public MessageWriter messageWriter; - public boolean endOfStreamReached = false; + public boolean endOfStreamReached; - public Socket() { - } + public Socket(SocketChannel socketChannel) { this.socketChannel = socketChannel; } - public Socket(SocketChannel socketChannel) { - this.socketChannel = socketChannel; - } + public int read(ByteBuffer byteBuffer) throws IOException { + int bytesRead = socketChannel.read(byteBuffer); + int totalBytesRead = bytesRead; - public int read(ByteBuffer byteBuffer) throws IOException { - int bytesRead = this.socketChannel.read(byteBuffer); - int totalBytesRead = bytesRead; + while (bytesRead > 0) { + bytesRead = socketChannel.read(byteBuffer); + totalBytesRead += bytesRead; + } + if (bytesRead == -1) endOfStreamReached = true; - while(bytesRead > 0){ - bytesRead = this.socketChannel.read(byteBuffer); - totalBytesRead += bytesRead; - } - if(bytesRead == -1){ - this.endOfStreamReached = true; - } + return totalBytesRead; + } - return totalBytesRead; - } + public int write(ByteBuffer byteBuffer) throws IOException { + int bytesWritten = socketChannel.write(byteBuffer); + int totalBytesWritten = bytesWritten; - public int write(ByteBuffer byteBuffer) throws IOException{ - int bytesWritten = this.socketChannel.write(byteBuffer); - int totalBytesWritten = bytesWritten; - - while(bytesWritten > 0 && byteBuffer.hasRemaining()){ - bytesWritten = this.socketChannel.write(byteBuffer); - totalBytesWritten += bytesWritten; - } - - return totalBytesWritten; - } + while (bytesWritten > 0 && byteBuffer.hasRemaining()) { + bytesWritten = socketChannel.write(byteBuffer); + totalBytesWritten += bytesWritten; + } + return totalBytesWritten; + } } diff --git a/src/main/java/com/jenkov/nioserver/SocketAcceptor.java b/src/main/java/com/jenkov/nioserver/SocketAcceptor.java index 138a39f..bde9c7a 100644 --- a/src/main/java/com/jenkov/nioserver/SocketAcceptor.java +++ b/src/main/java/com/jenkov/nioserver/SocketAcceptor.java @@ -2,52 +2,50 @@ package com.jenkov.nioserver; import java.io.IOException; import java.net.InetSocketAddress; -import java.net.ServerSocket; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Queue; /** - * Created by jjenkov on 19-10-2015. + * Project: java-nio-server
+ * File: SocketAcceptor.java
+ * Created: 19 Oct 2015
+ * + * @author jjenkov */ -public class SocketAccepter implements Runnable{ +public class SocketAcceptor implements Runnable { - private int tcpPort = 0; - private ServerSocketChannel serverSocket = null; + private int tcpPort; + private ServerSocketChannel serverSocket; - private Queue socketQueue = null; + private Queue socketQueue; - public SocketAccepter(int tcpPort, Queue socketQueue) { - this.tcpPort = tcpPort; - this.socketQueue = socketQueue; - } + public SocketAcceptor(int tcpPort, Queue socketQueue) { + this.tcpPort = tcpPort; + this.socketQueue = socketQueue; + } + public void run() { + try { + serverSocket = ServerSocketChannel.open(); + serverSocket.bind(new InetSocketAddress(tcpPort)); + } catch (IOException e) { + e.printStackTrace(); + return; + } + while (true) { + try { + SocketChannel socketChannel = serverSocket.accept(); - public void run() { - try{ - this.serverSocket = ServerSocketChannel.open(); - this.serverSocket.bind(new InetSocketAddress(tcpPort)); - } catch(IOException e){ - e.printStackTrace(); - return; - } + System.out.println("Socket accepted: " + socketChannel); + // TODO: check if the queue can even accept more sockets. + this.socketQueue.add(new Socket(socketChannel)); - while(true){ - try{ - SocketChannel socketChannel = this.serverSocket.accept(); - - System.out.println("Socket accepted: " + socketChannel); - - //todo check if the queue can even accept more sockets. - this.socketQueue.add(new Socket(socketChannel)); - - } catch(IOException e){ - e.printStackTrace(); - } - - } - - } -} + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/SocketProcessor.java b/src/main/java/com/jenkov/nioserver/SocketProcessor.java index 251fee6..f05e76b 100644 --- a/src/main/java/com/jenkov/nioserver/SocketProcessor.java +++ b/src/main/java/com/jenkov/nioserver/SocketProcessor.java @@ -5,211 +5,207 @@ import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; /** - * Created by jjenkov on 16-10-2015. + * Project: java-nio-server
+ * File: SocketProcessor.java
+ * Created: 16 Oct 2015
+ * + * @author jjenkov */ public class SocketProcessor implements Runnable { - private Queue inboundSocketQueue = null; + private Queue inboundSocketQueue; - private MessageBuffer readMessageBuffer = null; //todo Not used now - but perhaps will be later - to check for space in the buffer before reading from sockets - private MessageBuffer writeMessageBuffer = null; //todo Not used now - but perhaps will be later - to check for space in the buffer before reading from sockets (space for more to write?) + private MessageBuffer readMessageBuffer; // TODO: Not used now - but perhaps will be later - to check for space in the + // buffer before reading from sockets + @SuppressWarnings("unused") + private MessageBuffer writeMessageBuffer; // TODO: Not used now - but perhaps will be later - to check for space in the + // buffer before reading from sockets (space for more to write?) - private IMessageReaderFactory messageReaderFactory = null; + private IMessageReaderFactory messageReaderFactory; - private Queue outboundMessageQueue = new LinkedList<>(); //todo use a better / faster queue. + private Queue outboundMessageQueue = new LinkedList<>(); // TODO: use a better / faster queue. - private Map socketMap = new HashMap<>(); + private Map socketMap = new HashMap<>(); - private ByteBuffer readByteBuffer = ByteBuffer.allocate(1024 * 1024); - private ByteBuffer writeByteBuffer = ByteBuffer.allocate(1024 * 1024); - private Selector readSelector = null; - private Selector writeSelector = null; + private ByteBuffer readByteBuffer = ByteBuffer.allocate(1024 * 1024); + private ByteBuffer writeByteBuffer = ByteBuffer.allocate(1024 * 1024); + private Selector readSelector; + private Selector writeSelector; - private IMessageProcessor messageProcessor = null; - private WriteProxy writeProxy = null; + private IMessageProcessor messageProcessor; + private WriteProxy writeProxy; - private long nextSocketId = 16 * 1024; //start incoming socket ids from 16K - reserve bottom ids for pre-defined sockets (servers). + private long nextSocketId = 16 * 1024; // start incoming socket ids from 16K - reserve bottom ids for pre-defined + // sockets (servers). - private Set emptyToNonEmptySockets = new HashSet<>(); - private Set nonEmptyToEmptySockets = new HashSet<>(); + private Set emptyToNonEmptySockets = new HashSet<>(); + private Set nonEmptyToEmptySockets = new HashSet<>(); + public SocketProcessor(Queue inboundSocketQueue, MessageBuffer readMessageBuffer, MessageBuffer writeMessageBuffer, + IMessageReaderFactory messageReaderFactory, IMessageProcessor messageProcessor) throws IOException { + this.inboundSocketQueue = inboundSocketQueue; - public SocketProcessor(Queue inboundSocketQueue, MessageBuffer readMessageBuffer, MessageBuffer writeMessageBuffer, IMessageReaderFactory messageReaderFactory, IMessageProcessor messageProcessor) throws IOException { - this.inboundSocketQueue = inboundSocketQueue; + this.readMessageBuffer = readMessageBuffer; + this.writeMessageBuffer = writeMessageBuffer; + writeProxy = new WriteProxy(writeMessageBuffer, this.outboundMessageQueue); - this.readMessageBuffer = readMessageBuffer; - this.writeMessageBuffer = writeMessageBuffer; - this.writeProxy = new WriteProxy(writeMessageBuffer, this.outboundMessageQueue); + this.messageReaderFactory = messageReaderFactory; - this.messageReaderFactory = messageReaderFactory; + this.messageProcessor = messageProcessor; - this.messageProcessor = messageProcessor; + readSelector = Selector.open(); + writeSelector = Selector.open(); + } - this.readSelector = Selector.open(); - this.writeSelector = Selector.open(); - } + public void run() { + while (true) { + try { + executeCycle(); + Thread.sleep(100); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + } - public void run() { - while(true){ - try{ - executeCycle(); - } catch(IOException e){ - e.printStackTrace(); - } + public void executeCycle() throws IOException { + takeNewSockets(); + readFromSockets(); + writeToSockets(); + } - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } + public void takeNewSockets() throws IOException { + Socket newSocket = inboundSocketQueue.poll(); + while (newSocket != null) { + newSocket.socketId = nextSocketId++; + newSocket.socketChannel.configureBlocking(false); - public void executeCycle() throws IOException { - takeNewSockets(); - readFromSockets(); - writeToSockets(); - } + newSocket.messageReader = messageReaderFactory.createMessageReader(); + newSocket.messageReader.init(readMessageBuffer); + newSocket.messageWriter = new MessageWriter(); - public void takeNewSockets() throws IOException { - Socket newSocket = this.inboundSocketQueue.poll(); + socketMap.put(newSocket.socketId, newSocket); - while(newSocket != null){ - newSocket.socketId = this.nextSocketId++; - newSocket.socketChannel.configureBlocking(false); + SelectionKey key = newSocket.socketChannel.register(readSelector, SelectionKey.OP_READ); + key.attach(newSocket); - newSocket.messageReader = this.messageReaderFactory.createMessageReader(); - newSocket.messageReader.init(this.readMessageBuffer); + newSocket = inboundSocketQueue.poll(); + } + } - newSocket.messageWriter = new MessageWriter(); + public void readFromSockets() throws IOException { + int readReady = readSelector.selectNow(); - this.socketMap.put(newSocket.socketId, newSocket); + if (readReady > 0) { + Set selectedKeys = this.readSelector.selectedKeys(); + Iterator keyIterator = selectedKeys.iterator(); - SelectionKey key = newSocket.socketChannel.register(this.readSelector, SelectionKey.OP_READ); - key.attach(newSocket); + while (keyIterator.hasNext()) { + SelectionKey key = keyIterator.next(); + readFromSocket(key); + keyIterator.remove(); + } + selectedKeys.clear(); + } + } - newSocket = this.inboundSocketQueue.poll(); - } - } + private void readFromSocket(SelectionKey key) throws IOException { + Socket socket = (Socket) key.attachment(); + socket.messageReader.read(socket, this.readByteBuffer); + List fullMessages = socket.messageReader.getMessages(); + if (fullMessages.size() > 0) { + for (Message message : fullMessages) { + message.socketId = socket.socketId; + messageProcessor.process(message, writeProxy); // the message processor will eventually push outgoing messages into an + // IMessageWriter for this socket. + } + fullMessages.clear(); + } - public void readFromSockets() throws IOException { - int readReady = this.readSelector.selectNow(); + if (socket.endOfStreamReached) { + System.out.println("Socket closed: " + socket.socketId); + socketMap.remove(socket.socketId); + key.attach(null); + key.cancel(); + key.channel().close(); + } + } - if(readReady > 0){ - Set selectedKeys = this.readSelector.selectedKeys(); - Iterator keyIterator = selectedKeys.iterator(); + public void writeToSockets() throws IOException { - while(keyIterator.hasNext()) { - SelectionKey key = keyIterator.next(); + // Take all new messages from outboundMessageQueue + takeNewOutboundMessages(); - readFromSocket(key); + // Cancel all sockets which have no more data to write. + cancelEmptySockets(); - keyIterator.remove(); - } - selectedKeys.clear(); - } - } + // Register all sockets that *have* data and which are not yet registered. + registerNonEmptySockets(); - private void readFromSocket(SelectionKey key) throws IOException { - Socket socket = (Socket) key.attachment(); - socket.messageReader.read(socket, this.readByteBuffer); + // Select from the Selector. + int writeReady = this.writeSelector.selectNow(); - List fullMessages = socket.messageReader.getMessages(); - if(fullMessages.size() > 0){ - for(Message message : fullMessages){ - message.socketId = socket.socketId; - this.messageProcessor.process(message, this.writeProxy); //the message processor will eventually push outgoing messages into an IMessageWriter for this socket. - } - fullMessages.clear(); - } + if (writeReady > 0) { + Set selectionKeys = this.writeSelector.selectedKeys(); + Iterator keyIterator = selectionKeys.iterator(); - if(socket.endOfStreamReached){ - System.out.println("Socket closed: " + socket.socketId); - this.socketMap.remove(socket.socketId); - key.attach(null); - key.cancel(); - key.channel().close(); - } - } + while (keyIterator.hasNext()) { + SelectionKey key = keyIterator.next(); + Socket socket = (Socket) key.attachment(); - public void writeToSockets() throws IOException { + socket.messageWriter.write(socket, this.writeByteBuffer); - // Take all new messages from outboundMessageQueue - takeNewOutboundMessages(); + if (socket.messageWriter.isEmpty()) { this.nonEmptyToEmptySockets.add(socket); } - // Cancel all sockets which have no more data to write. - cancelEmptySockets(); + keyIterator.remove(); + } + selectionKeys.clear(); + } + } - // Register all sockets that *have* data and which are not yet registered. - registerNonEmptySockets(); + private void registerNonEmptySockets() throws ClosedChannelException { + for (Socket socket : emptyToNonEmptySockets) + socket.socketChannel.register(writeSelector, SelectionKey.OP_WRITE, socket); + emptyToNonEmptySockets.clear(); + } - // Select from the Selector. - int writeReady = this.writeSelector.selectNow(); + private void cancelEmptySockets() { + for (Socket socket : nonEmptyToEmptySockets) { + SelectionKey key = socket.socketChannel.keyFor(this.writeSelector); + key.cancel(); + } + nonEmptyToEmptySockets.clear(); + } - if(writeReady > 0){ - Set selectionKeys = this.writeSelector.selectedKeys(); - Iterator keyIterator = selectionKeys.iterator(); + private void takeNewOutboundMessages() { + Message outMessage = outboundMessageQueue.poll(); + while (outMessage != null) { + Socket socket = socketMap.get(outMessage.socketId); - while(keyIterator.hasNext()){ - SelectionKey key = keyIterator.next(); + if (socket != null) { + MessageWriter messageWriter = socket.messageWriter; + if (messageWriter.isEmpty()) { + messageWriter.enqueue(outMessage); + nonEmptyToEmptySockets.remove(socket); + emptyToNonEmptySockets.add(socket); // not necessary if removed from nonEmptyToEmptySockets in prev. statement. + } else messageWriter.enqueue(outMessage); + } - Socket socket = (Socket) key.attachment(); - - socket.messageWriter.write(socket, this.writeByteBuffer); - - if(socket.messageWriter.isEmpty()){ - this.nonEmptyToEmptySockets.add(socket); - } - - keyIterator.remove(); - } - - selectionKeys.clear(); - - } - } - - private void registerNonEmptySockets() throws ClosedChannelException { - for(Socket socket : emptyToNonEmptySockets){ - socket.socketChannel.register(this.writeSelector, SelectionKey.OP_WRITE, socket); - } - emptyToNonEmptySockets.clear(); - } - - private void cancelEmptySockets() { - for(Socket socket : nonEmptyToEmptySockets){ - SelectionKey key = socket.socketChannel.keyFor(this.writeSelector); - - key.cancel(); - } - nonEmptyToEmptySockets.clear(); - } - - private void takeNewOutboundMessages() { - Message outMessage = this.outboundMessageQueue.poll(); - while(outMessage != null){ - Socket socket = this.socketMap.get(outMessage.socketId); - - if(socket != null){ - MessageWriter messageWriter = socket.messageWriter; - if(messageWriter.isEmpty()){ - messageWriter.enqueue(outMessage); - nonEmptyToEmptySockets.remove(socket); - emptyToNonEmptySockets.add(socket); //not necessary if removed from nonEmptyToEmptySockets in prev. statement. - } else{ - messageWriter.enqueue(outMessage); - } - } - - outMessage = this.outboundMessageQueue.poll(); - } - } - -} + outMessage = outboundMessageQueue.poll(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/WriteProxy.java b/src/main/java/com/jenkov/nioserver/WriteProxy.java index 2c99529..ff4aff0 100644 --- a/src/main/java/com/jenkov/nioserver/WriteProxy.java +++ b/src/main/java/com/jenkov/nioserver/WriteProxy.java @@ -3,24 +3,23 @@ package com.jenkov.nioserver; import java.util.Queue; /** - * Created by jjenkov on 22-10-2015. + * Project: java-nio-server
+ * File: WriteProxy.java
+ * Created: 22 Oct 2015
+ * + * @author jjenkov */ public class WriteProxy { - private MessageBuffer messageBuffer = null; - private Queue writeQueue = null; + private MessageBuffer messageBuffer; + private Queue writeQueue; - public WriteProxy(MessageBuffer messageBuffer, Queue writeQueue) { - this.messageBuffer = messageBuffer; - this.writeQueue = writeQueue; - } + public WriteProxy(MessageBuffer messageBuffer, Queue writeQueue) { + this.messageBuffer = messageBuffer; + this.writeQueue = writeQueue; + } - public Message getMessage(){ - return this.messageBuffer.getMessage(); - } + public Message getMessage() { return messageBuffer.getMessage(); } - public boolean enqueue(Message message){ - return this.writeQueue.offer(message); - } - -} + public boolean enqueue(Message message) { return writeQueue.offer(message); } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/example/Main.java b/src/main/java/com/jenkov/nioserver/example/Main.java index 238c8ea..fb5483c 100644 --- a/src/main/java/com/jenkov/nioserver/example/Main.java +++ b/src/main/java/com/jenkov/nioserver/example/Main.java @@ -1,42 +1,40 @@ package com.jenkov.nioserver.example; -import com.jenkov.nioserver.*; +import java.io.IOException; + +import com.jenkov.nioserver.IMessageProcessor; +import com.jenkov.nioserver.Message; +import com.jenkov.nioserver.Server; import com.jenkov.nioserver.http.HttpMessageReaderFactory; -import java.io.IOException; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; - /** - * Created by jjenkov on 19-10-2015. + * Project: java-nio-server
+ * File: Main.java
+ * Created: 19 Oct 2015
+ * + * @author jjenkov */ public class Main { - public static void main(String[] args) throws IOException { + public static void main(String[] args) throws IOException { - String httpResponse = "HTTP/1.1 200 OK\r\n" + - "Content-Length: 38\r\n" + - "Content-Type: text/html\r\n" + - "\r\n" + - "Hello World!"; + String httpResponse = "HTTP/1.1 200 OK\r\n" + "Content-Length: 38\r\n" + "Content-Type: text/html\r\n" + "\r\n" + + "Hello World!"; - byte[] httpResponseBytes = httpResponse.getBytes("UTF-8"); + byte[] httpResponseBytes = httpResponse.getBytes("UTF-8"); - IMessageProcessor messageProcessor = (request, writeProxy) -> { - System.out.println("Message Received from socket: " + request.socketId); + IMessageProcessor messageProcessor = (request, writeProxy) -> { + System.out.println("Message Received from socket: " + request.socketId); - Message response = writeProxy.getMessage(); - response.socketId = request.socketId; - response.writeToMessage(httpResponseBytes); + Message response = writeProxy.getMessage(); + response.socketId = request.socketId; + response.writeToMessage(httpResponseBytes); - writeProxy.enqueue(response); - }; + writeProxy.enqueue(response); + }; - Server server = new Server(9999, new HttpMessageReaderFactory(), messageProcessor); + Server server = new Server(9999, new HttpMessageReaderFactory(), messageProcessor); - server.start(); - - } - - -} + server.start(); + } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/http/HttpHeaders.java b/src/main/java/com/jenkov/nioserver/http/HttpHeaders.java index 25e5a1b..bd02701 100644 --- a/src/main/java/com/jenkov/nioserver/http/HttpHeaders.java +++ b/src/main/java/com/jenkov/nioserver/http/HttpHeaders.java @@ -1,27 +1,27 @@ package com.jenkov.nioserver.http; /** - * Created by jjenkov on 19-10-2015. + * Project: java-nio-server
+ * File: HttpHeaders.java
+ * Created: 19 Oct 2015
+ * + * @author jjenkov */ public class HttpHeaders { - public static int HTTP_METHOD_GET = 1; - public static int HTTP_METHOD_POST = 2; - public static int HTTP_METHOD_PUT = 3; - public static int HTTP_METHOD_HEAD = 4; - public static int HTTP_METHOD_DELETE = 5; + public static int HTTP_METHOD_GET = 1; + public static int HTTP_METHOD_POST = 2; + public static int HTTP_METHOD_PUT = 3; + public static int HTTP_METHOD_HEAD = 4; + public static int HTTP_METHOD_DELETE = 5; - public int httpMethod = 0; + public int httpMethod = 0; - public int hostStartIndex = 0; - public int hostEndIndex = 0; + public int hostStartIndex = 0; + public int hostEndIndex = 0; - public int contentLength = 0; + public int contentLength = 0; - public int bodyStartIndex = 0; - public int bodyEndIndex = 0; - - - - -} + public int bodyStartIndex = 0; + public int bodyEndIndex = 0; +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/http/HttpMessageReader.java b/src/main/java/com/jenkov/nioserver/http/HttpMessageReader.java index cf3a6e1..3738276 100644 --- a/src/main/java/com/jenkov/nioserver/http/HttpMessageReader.java +++ b/src/main/java/com/jenkov/nioserver/http/HttpMessageReader.java @@ -1,64 +1,64 @@ package com.jenkov.nioserver.http; -import com.jenkov.nioserver.IMessageReader; -import com.jenkov.nioserver.Message; -import com.jenkov.nioserver.MessageBuffer; -import com.jenkov.nioserver.Socket; - import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; +import com.jenkov.nioserver.IMessageReader; +import com.jenkov.nioserver.Message; +import com.jenkov.nioserver.MessageBuffer; +import com.jenkov.nioserver.Socket; + /** - * Created by jjenkov on 18-10-2015. + * Project: java-nio-server
+ * File: HttpMessageReader.java
+ * Created: 18 Oct 2015
+ * + * @author jjenkov */ public class HttpMessageReader implements IMessageReader { - private MessageBuffer messageBuffer = null; + private MessageBuffer messageBuffer; - private List completeMessages = new ArrayList(); - private Message nextMessage = null; + private List completeMessages = new ArrayList<>(); + private Message nextMessage; - public HttpMessageReader() { - } + @Override + public void init(MessageBuffer readMessageBuffer) { + messageBuffer = readMessageBuffer; + nextMessage = messageBuffer.getMessage(); + nextMessage.metaData = new HttpHeaders(); + } - @Override - public void init(MessageBuffer readMessageBuffer) { - this.messageBuffer = readMessageBuffer; - this.nextMessage = messageBuffer.getMessage(); - this.nextMessage.metaData = new HttpHeaders(); - } + @Override + public void read(Socket socket, ByteBuffer byteBuffer) throws IOException { + socket.read(byteBuffer); + byteBuffer.flip(); - @Override - public void read(Socket socket, ByteBuffer byteBuffer) throws IOException { - int bytesRead = socket.read(byteBuffer); - byteBuffer.flip(); + if (byteBuffer.remaining() == 0) { + byteBuffer.clear(); + return; + } - if(byteBuffer.remaining() == 0){ - byteBuffer.clear(); - return; - } + nextMessage.writeToMessage(byteBuffer); - this.nextMessage.writeToMessage(byteBuffer); + int endIndex = HttpUtil.parseHttpRequest(nextMessage.sharedArray, + nextMessage.offset, + nextMessage.offset + nextMessage.length, + (HttpHeaders) nextMessage.metaData); + if (endIndex != -1) { + Message message = messageBuffer.getMessage(); + message.metaData = new HttpHeaders(); - int endIndex = HttpUtil.parseHttpRequest(this.nextMessage.sharedArray, this.nextMessage.offset, this.nextMessage.offset + this.nextMessage.length, (HttpHeaders) this.nextMessage.metaData); - if(endIndex != -1){ - Message message = this.messageBuffer.getMessage(); - message.metaData = new HttpHeaders(); + message.writePartialMessageToMessage(nextMessage, endIndex); - message.writePartialMessageToMessage(nextMessage, endIndex); + completeMessages.add(nextMessage); + nextMessage = message; + } + byteBuffer.clear(); + } - completeMessages.add(nextMessage); - nextMessage = message; - } - byteBuffer.clear(); - } - - - @Override - public List getMessages() { - return this.completeMessages; - } - -} + @Override + public List getMessages() { return completeMessages; } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/http/HttpMessageReaderFactory.java b/src/main/java/com/jenkov/nioserver/http/HttpMessageReaderFactory.java index e855aed..82b4c91 100644 --- a/src/main/java/com/jenkov/nioserver/http/HttpMessageReaderFactory.java +++ b/src/main/java/com/jenkov/nioserver/http/HttpMessageReaderFactory.java @@ -2,18 +2,16 @@ package com.jenkov.nioserver.http; import com.jenkov.nioserver.IMessageReader; import com.jenkov.nioserver.IMessageReaderFactory; -import com.jenkov.nioserver.MessageBuffer; /** - * Created by jjenkov on 18-10-2015. + * Project: java-nio-server
+ * File: HttpMessageReaderFactory.java
+ * Created: 18 Oct 2015
+ * + * @author jjenkov */ public class HttpMessageReaderFactory implements IMessageReaderFactory { - public HttpMessageReaderFactory() { - } - - @Override - public IMessageReader createMessageReader() { - return new HttpMessageReader(); - } -} + @Override + public IMessageReader createMessageReader() { return new HttpMessageReader(); } +} \ No newline at end of file diff --git a/src/main/java/com/jenkov/nioserver/http/HttpUtil.java b/src/main/java/com/jenkov/nioserver/http/HttpUtil.java index ed2904d..16f4543 100644 --- a/src/main/java/com/jenkov/nioserver/http/HttpUtil.java +++ b/src/main/java/com/jenkov/nioserver/http/HttpUtil.java @@ -3,153 +3,143 @@ package com.jenkov.nioserver.http; import java.io.UnsupportedEncodingException; /** - * Created by jjenkov on 19-10-2015. + * Project: java-nio-server
+ * File: HttpUtil.java
+ * Created: 19 Oct 2015
+ * + * @author jjenkov */ public class HttpUtil { - private static final byte[] GET = new byte[]{'G','E','T'}; - private static final byte[] POST = new byte[]{'P','O','S','T'}; - private static final byte[] PUT = new byte[]{'P','U','T'}; - private static final byte[] HEAD = new byte[]{'H','E','A','D'}; - private static final byte[] DELETE = new byte[]{'D','E','L','E','T','E'}; + private static final byte[] GET = new byte[] { 'G', 'E', 'T' }; + private static final byte[] POST = new byte[] { 'P', 'O', 'S', 'T' }; + private static final byte[] PUT = new byte[] { 'P', 'U', 'T' }; + private static final byte[] HEAD = new byte[] { 'H', 'E', 'A', 'D' }; + private static final byte[] DELETE = new byte[] { 'D', 'E', 'L', 'E', 'T', 'E' }; - private static final byte[] HOST = new byte[]{'H','o','s','t'}; - private static final byte[] CONTENT_LENGTH = new byte[]{'C','o','n','t','e','n','t','-','L','e','n','g','t','h'}; + @SuppressWarnings("unused") + private static final byte[] HOST = new byte[] { 'H', 'o', 's', 't' }; + private static final byte[] CONTENT_LENGTH = new byte[] { 'C', 'o', 'n', 't', 'e', 'n', 't', '-', 'L', 'e', 'n', 'g', 't', 'h' }; - public static int parseHttpRequest(byte[] src, int startIndex, int endIndex, HttpHeaders httpHeaders){ + public static int parseHttpRequest(byte[] src, int startIndex, int endIndex, HttpHeaders httpHeaders) { + /* + * int endOfHttpMethod = findNext(src, startIndex, endIndex, (byte) ' '); + * if(endOfHttpMethod == -1) return false; + * resolveHttpMethod(src, startIndex, httpHeaders); + */ - /* - int endOfHttpMethod = findNext(src, startIndex, endIndex, (byte) ' '); - if(endOfHttpMethod == -1) return false; - resolveHttpMethod(src, startIndex, httpHeaders); - */ + // parse HTTP request line + int endOfFirstLine = findNextLineBreak(src, startIndex, endIndex); + if (endOfFirstLine == -1) return -1; - //parse HTTP request line - int endOfFirstLine = findNextLineBreak(src, startIndex, endIndex); - if(endOfFirstLine == -1) return -1; + // parse HTTP headers + int prevEndOfHeader = endOfFirstLine + 1; + int endOfHeader = findNextLineBreak(src, prevEndOfHeader, endIndex); + while (endOfHeader != -1 && endOfHeader != prevEndOfHeader + 1) { // prevEndOfHeader + 1 = end of previous header + 2 (+2 = CR + LF) - //parse HTTP headers - int prevEndOfHeader = endOfFirstLine + 1; - int endOfHeader = findNextLineBreak(src, prevEndOfHeader, endIndex); + if (matches(src, prevEndOfHeader, CONTENT_LENGTH)) { + try { + findContentLength(src, prevEndOfHeader, endIndex, httpHeaders); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } - while(endOfHeader != -1 && endOfHeader != prevEndOfHeader + 1){ //prevEndOfHeader + 1 = end of previous header + 2 (+2 = CR + LF) + prevEndOfHeader = endOfHeader + 1; + endOfHeader = findNextLineBreak(src, prevEndOfHeader, endIndex); + } - if(matches(src, prevEndOfHeader, CONTENT_LENGTH)){ - try { - findContentLength(src, prevEndOfHeader, endIndex, httpHeaders); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - } + if (endOfHeader == -1) { return -1; } - prevEndOfHeader = endOfHeader + 1; - endOfHeader = findNextLineBreak(src, prevEndOfHeader, endIndex); - } + // check that byte array contains full HTTP message. + int bodyStartIndex = endOfHeader + 1; + int bodyEndIndex = bodyStartIndex + httpHeaders.contentLength; - if(endOfHeader == -1){ - return -1; - } + if (bodyEndIndex <= endIndex) { + // byte array contains a full HTTP request + httpHeaders.bodyStartIndex = bodyStartIndex; + httpHeaders.bodyEndIndex = bodyEndIndex; + return bodyEndIndex; + } - //check that byte array contains full HTTP message. - int bodyStartIndex = endOfHeader + 1; - int bodyEndIndex = bodyStartIndex + httpHeaders.contentLength; + return -1; + } - if(bodyEndIndex <= endIndex){ - //byte array contains a full HTTP request - httpHeaders.bodyStartIndex = bodyStartIndex; - httpHeaders.bodyEndIndex = bodyEndIndex; - return bodyEndIndex; - } + private static void findContentLength(byte[] src, int startIndex, int endIndex, HttpHeaders httpHeaders) throws UnsupportedEncodingException { + int indexOfColon = findNext(src, startIndex, endIndex, (byte) ':'); + // skip spaces after colon + int index = indexOfColon + 1; + while (src[index] == ' ') { + index++; + } - return -1; - } + int valueStartIndex = index; + int valueEndIndex = index; + boolean endOfValueFound = false; - private static void findContentLength(byte[] src, int startIndex, int endIndex, HttpHeaders httpHeaders) throws UnsupportedEncodingException { - int indexOfColon = findNext(src, startIndex, endIndex, (byte) ':'); + while (index < endIndex && !endOfValueFound) { + switch (src[index]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + index++; + break; + default: + endOfValueFound = true; + valueEndIndex = index; + } + } + httpHeaders.contentLength = Integer.parseInt(new String(src, valueStartIndex, valueEndIndex - valueStartIndex, "UTF-8")); + } - //skip spaces after colon - int index = indexOfColon +1; - while(src[index] == ' '){ - index++; - } + public static int findNext(byte[] src, int startIndex, int endIndex, byte value) { + for (int index = startIndex; index < endIndex; index++) + if (src[index] == value) return index; + return -1; + } - int valueStartIndex = index; - int valueEndIndex = index; - boolean endOfValueFound = false; + public static int findNextLineBreak(byte[] src, int startIndex, int endIndex) { + for (int index = startIndex; index < endIndex; index++) + if (src[index] == '\n') if (src[index - 1] == '\r') return index; + return -1; + } - while(index < endIndex && !endOfValueFound){ - switch(src[index]){ - case '0' : ; - case '1' : ; - case '2' : ; - case '3' : ; - case '4' : ; - case '5' : ; - case '6' : ; - case '7' : ; - case '8' : ; - case '9' : { index++; break; } + public static void resolveHttpMethod(byte[] src, int startIndex, HttpHeaders httpHeaders) { + if (matches(src, startIndex, GET)) { + httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_GET; + return; + } + if (matches(src, startIndex, POST)) { + httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_POST; + return; + } + if (matches(src, startIndex, PUT)) { + httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_PUT; + return; + } + if (matches(src, startIndex, HEAD)) { + httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_HEAD; + return; + } + if (matches(src, startIndex, DELETE)) { + httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_DELETE; + return; + } + } - default: { - endOfValueFound = true; - valueEndIndex = index; - } - } - } - - httpHeaders.contentLength = Integer.parseInt(new String(src, valueStartIndex, valueEndIndex - valueStartIndex, "UTF-8")); - - } - - - public static int findNext(byte[] src, int startIndex, int endIndex, byte value){ - for(int index = startIndex; index < endIndex; index++){ - if(src[index] == value) return index; - } - return -1; - } - - public static int findNextLineBreak(byte[] src, int startIndex, int endIndex) { - for(int index = startIndex; index < endIndex; index++){ - if(src[index] == '\n'){ - if(src[index - 1] == '\r'){ - return index; - } - }; - } - return -1; - } - - public static void resolveHttpMethod(byte[] src, int startIndex, HttpHeaders httpHeaders){ - if(matches(src, startIndex, GET)) { - httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_GET; - return; - } - if(matches(src, startIndex, POST)){ - httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_POST; - return; - } - if(matches(src, startIndex, PUT)){ - httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_PUT; - return; - } - if(matches(src, startIndex, HEAD)){ - httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_HEAD; - return; - } - if(matches(src, startIndex, DELETE)){ - httpHeaders.httpMethod = HttpHeaders.HTTP_METHOD_DELETE; - return; - } - } - - public static boolean matches(byte[] src, int offset, byte[] value){ - for(int i=offset, n=0; n < value.length; i++, n++){ - if(src[i] != value[n]) return false; - } - return true; - } -} + public static boolean matches(byte[] src, int offset, byte[] value) { + for (int i = offset, n = 0; n < value.length; i++, n++) + if (src[i] != value[n]) return false; + return true; + } +} \ No newline at end of file diff --git a/src/test/java/com/jenkov/nioserver/MessageBufferTest.java b/src/test/java/com/jenkov/nioserver/MessageBufferTest.java index b1e1cda..353e77e 100644 --- a/src/test/java/com/jenkov/nioserver/MessageBufferTest.java +++ b/src/test/java/com/jenkov/nioserver/MessageBufferTest.java @@ -1,80 +1,79 @@ package com.jenkov.nioserver; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; +import org.junit.jupiter.api.Test; /** - * Created by jjenkov on 18-10-2015. + * Project: java-nio-server
+ * File: MessageBufferTest.java
+ * Created: 18 Oct 2015
+ * + * @author jjenkov */ public class MessageBufferTest { - @Test - public void testGetMessage() { + @Test + public void testGetMessage() { - MessageBuffer messageBuffer = new MessageBuffer(); + MessageBuffer messageBuffer = new MessageBuffer(); - Message message = messageBuffer.getMessage(); + Message message = messageBuffer.getMessage(); - assertNotNull(message); - assertEquals(0 , message.offset); - assertEquals(0 , message.length); - assertEquals(4 * 1024, message.capacity); + assertNotNull(message); + assertEquals(0, message.offset); + assertEquals(0, message.length); + assertEquals(4 * 1024, message.capacity); - Message message2 = messageBuffer.getMessage(); + Message message2 = messageBuffer.getMessage(); - assertNotNull(message2); - assertEquals(4096 , message2.offset); - assertEquals(0 , message2.length); - assertEquals(4 * 1024, message2.capacity); + assertNotNull(message2); + assertEquals(4096, message2.offset); + assertEquals(0, message2.length); + assertEquals(4 * 1024, message2.capacity); - //todo test what happens if the small buffer space is depleted of messages. + // TODO: test what happens if the small buffer space is depleted of messages. + } - } + @Test + public void testExpandMessage() { + MessageBuffer messageBuffer = new MessageBuffer(); + Message message = messageBuffer.getMessage(); - @Test - public void testExpandMessage(){ - MessageBuffer messageBuffer = new MessageBuffer(); + byte[] smallSharedArray = message.sharedArray; - Message message = messageBuffer.getMessage(); + assertNotNull(message); + assertEquals(0, message.offset); + assertEquals(0, message.length); + assertEquals(4 * 1024, message.capacity); - byte[] smallSharedArray = message.sharedArray; + messageBuffer.expandMessage(message); + assertEquals(0, message.offset); + assertEquals(0, message.length); + assertEquals(128 * 1024, message.capacity); - assertNotNull(message); - assertEquals(0 , message.offset); - assertEquals(0 , message.length); - assertEquals(4 * 1024, message.capacity); + byte[] mediumSharedArray = message.sharedArray; + assertNotSame(smallSharedArray, mediumSharedArray); - messageBuffer.expandMessage(message); - assertEquals(0 , message.offset); - assertEquals(0 , message.length); - assertEquals(128 * 1024, message.capacity); + messageBuffer.expandMessage(message); + assertEquals(0, message.offset); + assertEquals(0, message.length); + assertEquals(1024 * 1024, message.capacity); - byte[] mediumSharedArray = message.sharedArray; - assertNotSame(smallSharedArray, mediumSharedArray); + byte[] largeSharedArray = message.sharedArray; + assertNotSame(smallSharedArray, largeSharedArray); + assertNotSame(mediumSharedArray, largeSharedArray); - messageBuffer.expandMessage(message); - assertEquals(0 , message.offset); - assertEquals(0 , message.length); - assertEquals(1024 * 1024, message.capacity); - - byte[] largeSharedArray = message.sharedArray; - assertNotSame(smallSharedArray, largeSharedArray); - assertNotSame(mediumSharedArray, largeSharedArray); - - //next expansion should not be possible. - assertFalse(messageBuffer.expandMessage(message)); - assertEquals(0 , message.offset); - assertEquals(0 , message.length); - assertEquals(1024 * 1024, message.capacity); - assertSame(message.sharedArray, largeSharedArray); - - - - } -} + // next expansion should not be possible. + assertFalse(messageBuffer.expandMessage(message)); + assertEquals(0, message.offset); + assertEquals(0, message.length); + assertEquals(1024 * 1024, message.capacity); + assertSame(message.sharedArray, largeSharedArray); + } +} \ No newline at end of file diff --git a/src/test/java/com/jenkov/nioserver/MessageTest.java b/src/test/java/com/jenkov/nioserver/MessageTest.java index dcc070f..4e838dc 100644 --- a/src/test/java/com/jenkov/nioserver/MessageTest.java +++ b/src/test/java/com/jenkov/nioserver/MessageTest.java @@ -1,59 +1,56 @@ package com.jenkov.nioserver; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; import java.nio.ByteBuffer; +import org.junit.jupiter.api.Test; /** - * Created by jjenkov on 18-10-2015. + * Project: java-nio-server
+ * File: MessageTest.java
+ * Created: 18 Oct 2015
+ * + * @author jjenkov */ public class MessageTest { + @Test + public void testWriteToMessage() { + MessageBuffer messageBuffer = new MessageBuffer(); - @Test - public void testWriteToMessage() { - MessageBuffer messageBuffer = new MessageBuffer(); + Message message = messageBuffer.getMessage(); + ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024); - Message message = messageBuffer.getMessage(); - ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024); + fill(byteBuffer, 4096); - fill(byteBuffer, 4096); + int written = message.writeToMessage(byteBuffer); + assertEquals(4096, written); + assertEquals(4096, message.length); + assertSame(messageBuffer.smallMessageBuffer, message.sharedArray); - int written = message.writeToMessage(byteBuffer); - assertEquals(4096, written); - assertEquals(4096, message.length); - assertSame(messageBuffer.smallMessageBuffer, message.sharedArray); + fill(byteBuffer, 124 * 1024); + written = message.writeToMessage(byteBuffer); + assertEquals(124 * 1024, written); + assertEquals(128 * 1024, message.length); + assertSame(messageBuffer.mediumMessageBuffer, message.sharedArray); - fill(byteBuffer, 124 * 1024); - written = message.writeToMessage(byteBuffer); - assertEquals(124 * 1024, written); - assertEquals(128 * 1024, message.length); - assertSame(messageBuffer.mediumMessageBuffer, message.sharedArray); + fill(byteBuffer, (1024 - 128) * 1024); + written = message.writeToMessage(byteBuffer); + assertEquals(896 * 1024, written); + assertEquals(1024 * 1024, message.length); + assertSame(messageBuffer.largeMessageBuffer, message.sharedArray); - fill(byteBuffer, (1024-128) * 1024); - written = message.writeToMessage(byteBuffer); - assertEquals(896 * 1024, written); - assertEquals(1024 * 1024, message.length); - assertSame(messageBuffer.largeMessageBuffer, message.sharedArray); + fill(byteBuffer, 1); + written = message.writeToMessage(byteBuffer); + assertEquals(-1, written); + } - fill(byteBuffer, 1); - written = message.writeToMessage(byteBuffer); - assertEquals(-1, written); - - } - - private void fill(ByteBuffer byteBuffer, int length){ - byteBuffer.clear(); - for(int i=0; ijava-nio-server
+ * File: SelectorTest.java
+ * Created: 21 Oct 2015
+ * + * @author jjenkov */ public class SelectorTest { @@ -27,10 +31,5 @@ public class SelectorTest { SelectionKey key2 = socketChannel.register(selector, SelectionKey.OP_WRITE); key2.cancel(); - - - } - - -} +} \ No newline at end of file diff --git a/src/test/java/com/jenkov/nioserver/http/HttpUtilTest.java b/src/test/java/com/jenkov/nioserver/http/HttpUtilTest.java index 0d1a176..83c0d50 100644 --- a/src/test/java/com/jenkov/nioserver/http/HttpUtilTest.java +++ b/src/test/java/com/jenkov/nioserver/http/HttpUtilTest.java @@ -1,88 +1,67 @@ package com.jenkov.nioserver.http; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.UnsupportedEncodingException; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; +import org.junit.jupiter.api.Test; /** - * Created by jjenkov on 19-10-2015. + * Project: java-nio-server
+ * File: HttpUtilTest.java
+ * Created: 19 Oct 2015
+ * + * @author jjenkov */ public class HttpUtilTest { - @Test - public void testResolveHttpMethod() throws UnsupportedEncodingException { - assertHttpMethod("GET / HTTP/1.1\r\n" , HttpHeaders.HTTP_METHOD_GET); - assertHttpMethod("POST / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_POST); - assertHttpMethod("PUT / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_PUT); - assertHttpMethod("HEAD / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_HEAD); - assertHttpMethod("DELETE / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_DELETE); - } + @Test + public void testResolveHttpMethod() throws UnsupportedEncodingException { + assertHttpMethod("GET / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_GET); + assertHttpMethod("POST / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_POST); + assertHttpMethod("PUT / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_PUT); + assertHttpMethod("HEAD / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_HEAD); + assertHttpMethod("DELETE / HTTP/1.1\r\n", HttpHeaders.HTTP_METHOD_DELETE); + } - private void assertHttpMethod(String httpRequest, int httpMethod) throws UnsupportedEncodingException { - byte[] source = httpRequest.getBytes("UTF-8"); - HttpHeaders httpHeaders = new HttpHeaders(); + private void assertHttpMethod(String httpRequest, int httpMethod) throws UnsupportedEncodingException { + byte[] source = httpRequest.getBytes("UTF-8"); + HttpHeaders httpHeaders = new HttpHeaders(); - HttpUtil.resolveHttpMethod(source, 0, httpHeaders); - assertEquals(httpMethod, httpHeaders.httpMethod); - } + HttpUtil.resolveHttpMethod(source, 0, httpHeaders); + assertEquals(httpMethod, httpHeaders.httpMethod); + } + @Test + public void testParseHttpRequest() throws UnsupportedEncodingException { + String httpRequest = "GET / HTTP/1.1\r\n\r\n"; + byte[] source = httpRequest.getBytes("UTF-8"); + HttpHeaders httpHeaders = new HttpHeaders(); - @Test - public void testParseHttpRequest() throws UnsupportedEncodingException { - String httpRequest = - "GET / HTTP/1.1\r\n\r\n"; + HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders); - byte[] source = httpRequest.getBytes("UTF-8"); - HttpHeaders httpHeaders = new HttpHeaders(); + assertEquals(0, httpHeaders.contentLength); - HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders); + httpRequest = "GET / HTTP/1.1\r\n" + "Content-Length: 5\r\n" + "\r\n1234"; + source = httpRequest.getBytes("UTF-8"); - assertEquals(0, httpHeaders.contentLength); + assertEquals(-1, HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders)); + assertEquals(5, httpHeaders.contentLength); - httpRequest = - "GET / HTTP/1.1\r\n" + - "Content-Length: 5\r\n" + - "\r\n1234"; - source = httpRequest.getBytes("UTF-8"); + httpRequest = "GET / HTTP/1.1\r\n" + "Content-Length: 5\r\n" + "\r\n12345"; + source = httpRequest.getBytes("UTF-8"); - assertEquals(-1, HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders)); - assertEquals(5, httpHeaders.contentLength); + assertEquals(42, HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders)); + assertEquals(5, httpHeaders.contentLength); + httpRequest = "GET / HTTP/1.1\r\n" + "Content-Length: 5\r\n" + "\r\n12345" + "GET / HTTP/1.1\r\n" + "Content-Length: 5\r\n" + "\r\n12345"; - httpRequest = - "GET / HTTP/1.1\r\n" + - "Content-Length: 5\r\n" + - "\r\n12345"; - source = httpRequest.getBytes("UTF-8"); + source = httpRequest.getBytes("UTF-8"); - assertEquals(42, HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders)); - assertEquals(5, httpHeaders.contentLength); - - - httpRequest = - "GET / HTTP/1.1\r\n" + - "Content-Length: 5\r\n" + - "\r\n12345" + - "GET / HTTP/1.1\r\n" + - "Content-Length: 5\r\n" + - "\r\n12345"; - - source = httpRequest.getBytes("UTF-8"); - - assertEquals(42, HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders)); - assertEquals(5, httpHeaders.contentLength); - assertEquals(37, httpHeaders.bodyStartIndex); - assertEquals(42, httpHeaders.bodyEndIndex); - } - - - -} + assertEquals(42, HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders)); + assertEquals(5, httpHeaders.contentLength); + assertEquals(37, httpHeaders.bodyStartIndex); + assertEquals(42, httpHeaders.bodyEndIndex); + } +} \ No newline at end of file