diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..0b0766f
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..09e3bc9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/bin/
+/target/
diff --git a/.project b/.project
new file mode 100644
index 0000000..16957d7
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ java-nio-server
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..943e578
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
+org.eclipse.jdt.core.compiler.compliance=1.8
+org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
+org.eclipse.jdt.core.compiler.release=enabled
+org.eclipse.jdt.core.compiler.source=1.8
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..e3b3192
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,29 @@
+
+ 4.0.0
+ informatik-ag-ngl
+ java-nio-server
+ 0.0.1-SNAPSHOT
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.6.0-M1
+ test
+
+
+
+
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+ 8
+
+
+
+
+
\ No newline at end of file
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/SocketAccepter.java b/src/main/java/com/jenkov/nioserver/SocketAccepter.java
deleted file mode 100644
index 138a39f..0000000
--- a/src/main/java/com/jenkov/nioserver/SocketAccepter.java
+++ /dev/null
@@ -1,53 +0,0 @@
-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.
- */
-public class SocketAccepter implements Runnable{
-
- private int tcpPort = 0;
- private ServerSocketChannel serverSocket = null;
-
- private Queue socketQueue = null;
-
- public SocketAccepter(int tcpPort, Queue socketQueue) {
- this.tcpPort = tcpPort;
- this.socketQueue = socketQueue;
- }
-
-
-
- public void run() {
- try{
- this.serverSocket = ServerSocketChannel.open();
- this.serverSocket.bind(new InetSocketAddress(tcpPort));
- } catch(IOException e){
- e.printStackTrace();
- return;
- }
-
-
- 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();
- }
-
- }
-
- }
-}
diff --git a/src/main/java/com/jenkov/nioserver/SocketAcceptor.java b/src/main/java/com/jenkov/nioserver/SocketAcceptor.java
new file mode 100644
index 0000000..bde9c7a
--- /dev/null
+++ b/src/main/java/com/jenkov/nioserver/SocketAcceptor.java
@@ -0,0 +1,51 @@
+package com.jenkov.nioserver;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Queue;
+
+/**
+ * Project: java-nio-server
+ * File: SocketAcceptor.java
+ * Created: 19 Oct 2015
+ *
+ * @author jjenkov
+ */
+public class SocketAcceptor implements Runnable {
+
+ private int tcpPort;
+ private ServerSocketChannel serverSocket;
+
+ private Queue 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();
+
+ 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();
+ }
+ }
+ }
+}
\ 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
deleted file mode 100644
index b1e1cda..0000000
--- a/src/test/java/com.jenkov.nioserver/MessageBufferTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-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;
-
-/**
- * Created by jjenkov on 18-10-2015.
- */
-public class MessageBufferTest {
-
- @Test
- public void testGetMessage() {
-
- MessageBuffer messageBuffer = new MessageBuffer();
-
- Message message = messageBuffer.getMessage();
-
- assertNotNull(message);
- assertEquals(0 , message.offset);
- assertEquals(0 , message.length);
- assertEquals(4 * 1024, message.capacity);
-
- Message message2 = messageBuffer.getMessage();
-
- 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.
-
- }
-
-
- @Test
- public void testExpandMessage(){
- MessageBuffer messageBuffer = new MessageBuffer();
-
- Message message = messageBuffer.getMessage();
-
- byte[] smallSharedArray = message.sharedArray;
-
- assertNotNull(message);
- assertEquals(0 , message.offset);
- assertEquals(0 , message.length);
- assertEquals(4 * 1024, message.capacity);
-
- messageBuffer.expandMessage(message);
- assertEquals(0 , message.offset);
- assertEquals(0 , message.length);
- assertEquals(128 * 1024, message.capacity);
-
- byte[] mediumSharedArray = message.sharedArray;
- assertNotSame(smallSharedArray, mediumSharedArray);
-
- 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);
-
-
-
- }
-}
diff --git a/src/test/java/com.jenkov.nioserver/MessageTest.java b/src/test/java/com.jenkov.nioserver/MessageTest.java
deleted file mode 100644
index dcc070f..0000000
--- a/src/test/java/com.jenkov.nioserver/MessageTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-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 java.nio.ByteBuffer;
-
-
-/**
- * Created by jjenkov on 18-10-2015.
- */
-public class MessageTest {
-
-
- @Test
- public void testWriteToMessage() {
- MessageBuffer messageBuffer = new MessageBuffer();
-
- Message message = messageBuffer.getMessage();
- ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);
-
- fill(byteBuffer, 4096);
-
- 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, (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);
-
- }
-
- private void fill(ByteBuffer byteBuffer, int length){
- byteBuffer.clear();
- for(int i=0; ijava-nio-server
+ * File: MessageBufferTest.java
+ * Created: 18 Oct 2015
+ *
+ * @author jjenkov
+ */
+public class MessageBufferTest {
+
+ @Test
+ public void testGetMessage() {
+
+ MessageBuffer messageBuffer = new MessageBuffer();
+
+ Message message = messageBuffer.getMessage();
+
+ assertNotNull(message);
+ assertEquals(0, message.offset);
+ assertEquals(0, message.length);
+ assertEquals(4 * 1024, message.capacity);
+
+ Message message2 = messageBuffer.getMessage();
+
+ 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.
+ }
+
+ @Test
+ public void testExpandMessage() {
+ MessageBuffer messageBuffer = new MessageBuffer();
+
+ Message message = messageBuffer.getMessage();
+
+ byte[] smallSharedArray = message.sharedArray;
+
+ assertNotNull(message);
+ assertEquals(0, message.offset);
+ assertEquals(0, message.length);
+ assertEquals(4 * 1024, message.capacity);
+
+ messageBuffer.expandMessage(message);
+ assertEquals(0, message.offset);
+ assertEquals(0, message.length);
+ assertEquals(128 * 1024, message.capacity);
+
+ byte[] mediumSharedArray = message.sharedArray;
+ assertNotSame(smallSharedArray, mediumSharedArray);
+
+ 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);
+ }
+}
\ 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
new file mode 100644
index 0000000..4e838dc
--- /dev/null
+++ b/src/test/java/com/jenkov/nioserver/MessageTest.java
@@ -0,0 +1,56 @@
+package com.jenkov.nioserver;
+
+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;
+
+/**
+ * Project: java-nio-server
+ * File: MessageTest.java
+ * Created: 18 Oct 2015
+ *
+ * @author jjenkov
+ */
+public class MessageTest {
+
+ @Test
+ public void testWriteToMessage() {
+ MessageBuffer messageBuffer = new MessageBuffer();
+
+ Message message = messageBuffer.getMessage();
+ ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);
+
+ fill(byteBuffer, 4096);
+
+ 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, (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);
+ }
+
+ private void fill(ByteBuffer byteBuffer, int length) {
+ byteBuffer.clear();
+ for (int i = 0; i < length; i++)
+ byteBuffer.put((byte) (i % 128));
+ byteBuffer.flip();
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com.jenkov.nioserver/SelectorTest.java b/src/test/java/com/jenkov/nioserver/SelectorTest.java
similarity index 78%
rename from src/test/java/com.jenkov.nioserver/SelectorTest.java
rename to src/test/java/com/jenkov/nioserver/SelectorTest.java
index 497f5d5..e8bc589 100644
--- a/src/test/java/com.jenkov.nioserver/SelectorTest.java
+++ b/src/test/java/com/jenkov/nioserver/SelectorTest.java
@@ -1,15 +1,19 @@
package com.jenkov.nioserver;
-import org.junit.Test;
-
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
+import org.junit.jupiter.api.Test;
+
/**
- * Created by jjenkov on 21-10-2015.
+ * Project: java-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
new file mode 100644
index 0000000..83c0d50
--- /dev/null
+++ b/src/test/java/com/jenkov/nioserver/http/HttpUtilTest.java
@@ -0,0 +1,67 @@
+package com.jenkov.nioserver.http;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.io.UnsupportedEncodingException;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * 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);
+ }
+
+ 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);
+ }
+
+ @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();
+
+ HttpUtil.parseHttpRequest(source, 0, source.length, httpHeaders);
+
+ assertEquals(0, httpHeaders.contentLength);
+
+ httpRequest = "GET / HTTP/1.1\r\n" + "Content-Length: 5\r\n" + "\r\n1234";
+ source = httpRequest.getBytes("UTF-8");
+
+ 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\n12345";
+ 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);
+ }
+}
\ No newline at end of file