From 1cb731bc40a11a501b40df209f657cbb1548747e Mon Sep 17 00:00:00 2001 From: dhairya Date: Mon, 21 Mar 2022 21:56:15 -0300 Subject: [PATCH 1/2] Refactoring_Assignment --- .../java/io/vlingo/xoom/http/RequestHeader.java | 1 - .../java/io/vlingo/xoom/http/ResponseParser.java | 12 +++++++----- .../vlingo/xoom/http/media/MediaTypeDescriptor.java | 8 ++++---- .../java/io/vlingo/xoom/http/resource/Action.java | 13 ++++++++++--- .../vlingo/xoom/http/resource/DispatcherPool.java | 5 +++++ .../io/vlingo/xoom/http/resource/ServerActor.java | 6 ++---- .../http/resource/agent/AgentDispatcherPool.java | 3 --- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/vlingo/xoom/http/RequestHeader.java b/src/main/java/io/vlingo/xoom/http/RequestHeader.java index b618124..f9a9efc 100644 --- a/src/main/java/io/vlingo/xoom/http/RequestHeader.java +++ b/src/main/java/io/vlingo/xoom/http/RequestHeader.java @@ -59,7 +59,6 @@ public class RequestHeader extends Header { // Common non-standard request header names public static final String XRequestedWith = "X-Requested-With"; public static final String DNT = "DNT"; - public static final String XForwardedFor = "X-Forwarded-For"; public static final String XForwardedHost = "X-Forwarded-Host"; public static final String XForwardedProto = "X-Forwarded-Proto"; public static final String FrontEndHttps = "Front-End-Https"; diff --git a/src/main/java/io/vlingo/xoom/http/ResponseParser.java b/src/main/java/io/vlingo/xoom/http/ResponseParser.java index 4789575..77cd2ac 100644 --- a/src/main/java/io/vlingo/xoom/http/ResponseParser.java +++ b/src/main/java/io/vlingo/xoom/http/ResponseParser.java @@ -76,8 +76,14 @@ private ResponseParser(final ByteBuffer responseContent, final boolean bodyOnly) //========================================= // VirtualStateParser //========================================= + static class Responses{ + protected List fullResponses; + protected ListIterator fullResponsesIterator; + protected Headers headers; + protected Response.Status status; + } - static class VirtualStateParser { + static class VirtualStateParser extends Responses{ private static class OutOfContentException extends RuntimeException { private static final long serialVersionUID = 1L; } private enum Step { NotStarted, StatusLine, Headers, Body, Completed }; @@ -98,12 +104,8 @@ private enum Step { NotStarted, StatusLine, Headers, Body, Completed }; private int contentExtraLength; private boolean continuation; private Step currentStep; - private List fullResponses; - private ListIterator fullResponsesIterator; - private Headers headers; private boolean keepAlive; private long outOfContentTime; - private Response.Status status; private boolean stream; private Version version; diff --git a/src/main/java/io/vlingo/xoom/http/media/MediaTypeDescriptor.java b/src/main/java/io/vlingo/xoom/http/media/MediaTypeDescriptor.java index 67b5391..9235cc0 100644 --- a/src/main/java/io/vlingo/xoom/http/media/MediaTypeDescriptor.java +++ b/src/main/java/io/vlingo/xoom/http/media/MediaTypeDescriptor.java @@ -50,10 +50,10 @@ public String toString() { } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MediaTypeDescriptor that = (MediaTypeDescriptor) o; + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + MediaTypeDescriptor that = (MediaTypeDescriptor) object; return Objects.equals(mimeType, that.mimeType) && Objects.equals(mimeSubType, that.mimeSubType) && Objects.equals(parameters, that.parameters); diff --git a/src/main/java/io/vlingo/xoom/http/resource/Action.java b/src/main/java/io/vlingo/xoom/http/resource/Action.java index 56eaa0e..1f0b1b8 100644 --- a/src/main/java/io/vlingo/xoom/http/resource/Action.java +++ b/src/main/java/io/vlingo/xoom/http/resource/Action.java @@ -619,8 +619,16 @@ private Tuple2> parse(final String to) { final String methodName = to.substring(0, openParen); final String[] rawParameters = to.substring(openParen + 1, closeParen).split(","); - final List parameters = new ArrayList<>(rawParameters.length); + List parameter = new ArrayList<>(rawParameters.length); + + parameter = getTheParameters(rawParameters); + + return Tuple2.from(methodName, parameter); + } + private List getTheParameters(String[] rawParameters) + { + final List parameters = new ArrayList<>(rawParameters.length); for (String rawParameter : rawParameters) { rawParameter = rawParameter.trim(); if (!rawParameter.isEmpty()) { @@ -633,8 +641,7 @@ private Tuple2> parse(final String to) { } } } - - return Tuple2.from(methodName, parameters); + return parameters; } private String qualifiedType(String possiblyUnqualifiedType) { diff --git a/src/main/java/io/vlingo/xoom/http/resource/DispatcherPool.java b/src/main/java/io/vlingo/xoom/http/resource/DispatcherPool.java index c5d0639..731b20e 100644 --- a/src/main/java/io/vlingo/xoom/http/resource/DispatcherPool.java +++ b/src/main/java/io/vlingo/xoom/http/resource/DispatcherPool.java @@ -9,6 +9,8 @@ import io.vlingo.xoom.actors.Stage; +import java.util.concurrent.atomic.AtomicLong; + /** * A pool of {@code Dispatcher} instances. */ @@ -30,6 +32,9 @@ public interface DispatcherPool { static abstract class AbstractDispatcherPool implements DispatcherPool { protected final Dispatcher[] dispatcherPool; + protected AtomicLong dispatcherPoolIndex; + protected long dispatcherPoolSize; + protected AbstractDispatcherPool(final Stage stage, final Resources resources, final int dispatcherPoolSize) { this.dispatcherPool = new Dispatcher[dispatcherPoolSize]; diff --git a/src/main/java/io/vlingo/xoom/http/resource/ServerActor.java b/src/main/java/io/vlingo/xoom/http/resource/ServerActor.java index 163c159..3b122b7 100644 --- a/src/main/java/io/vlingo/xoom/http/resource/ServerActor.java +++ b/src/main/java/io/vlingo/xoom/http/resource/ServerActor.java @@ -7,8 +7,6 @@ package io.vlingo.xoom.http.resource; -import static io.vlingo.xoom.http.RequestHeader.XForwardedFor; - import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; @@ -252,8 +250,6 @@ private void logResourceMappings(final Resources resources) { private static class ServerDispatcherPool extends AbstractDispatcherPool { - private AtomicLong dispatcherPoolIndex; - private int dispatcherPoolSize; ServerDispatcherPool(final Stage stage, final Resources resources, final int dispatcherPoolSize) { super(stage, resources, dispatcherPoolSize); @@ -350,6 +346,8 @@ public void consume(final RequestResponseContext requestResponseContext, fina } } + public static final String XForwardedFor = "X-Forwarded-For"; + private Request enrichRequest(final RequestResponseContext requestResponseContext, final Request request) { try { request.headers.add(RequestHeader.of(XForwardedFor, requestResponseContext.remoteAddress())); diff --git a/src/main/java/io/vlingo/xoom/http/resource/agent/AgentDispatcherPool.java b/src/main/java/io/vlingo/xoom/http/resource/agent/AgentDispatcherPool.java index 4ec1ae5..2a5d78e 100644 --- a/src/main/java/io/vlingo/xoom/http/resource/agent/AgentDispatcherPool.java +++ b/src/main/java/io/vlingo/xoom/http/resource/agent/AgentDispatcherPool.java @@ -16,9 +16,6 @@ public class AgentDispatcherPool extends AbstractDispatcherPool { - private AtomicLong dispatcherPoolIndex; - private long dispatcherPoolSize; - public AgentDispatcherPool(final Stage stage, final Resources resources, final int dispatcherPoolSize) { super(stage, resources, dispatcherPoolSize); From 39935e7e08e648f070fbd1d57ee00e1013ce4def Mon Sep 17 00:00:00 2001 From: dhairya Date: Fri, 25 Mar 2022 14:37:10 -0300 Subject: [PATCH 2/2] refactoringv1 --- src/main/java/io/vlingo/xoom/http/Header.java | 4 +- .../io/vlingo/xoom/http/ResponseParser.java | 381 ----------------- .../vlingo/xoom/http/VirtualStateParser.java | 391 ++++++++++++++++++ .../io/vlingo/xoom/http/resource/Action.java | 4 +- 4 files changed, 395 insertions(+), 385 deletions(-) create mode 100644 src/main/java/io/vlingo/xoom/http/VirtualStateParser.java diff --git a/src/main/java/io/vlingo/xoom/http/Header.java b/src/main/java/io/vlingo/xoom/http/Header.java index f927e80..a02e40e 100644 --- a/src/main/java/io/vlingo/xoom/http/Header.java +++ b/src/main/java/io/vlingo/xoom/http/Header.java @@ -207,8 +207,8 @@ public String toString() { return builder.toString(); } - Headers(final int initialCapactiy) { - super(initialCapactiy); + Headers(final int initialCapacity) { + super(initialCapacity); } } } diff --git a/src/main/java/io/vlingo/xoom/http/ResponseParser.java b/src/main/java/io/vlingo/xoom/http/ResponseParser.java index 77cd2ac..2fd7dd3 100644 --- a/src/main/java/io/vlingo/xoom/http/ResponseParser.java +++ b/src/main/java/io/vlingo/xoom/http/ResponseParser.java @@ -73,386 +73,5 @@ private ResponseParser(final ByteBuffer responseContent, final boolean bodyOnly) this.virtualStateParser = new VirtualStateParser(bodyOnly).includes(responseContent).parse(); } - //========================================= - // VirtualStateParser - //========================================= - static class Responses{ - protected List fullResponses; - protected ListIterator fullResponsesIterator; - protected Headers headers; - protected Response.Status status; - } - - static class VirtualStateParser extends Responses{ - private static class OutOfContentException extends RuntimeException { private static final long serialVersionUID = 1L; } - - private enum Step { NotStarted, StatusLine, Headers, Body, Completed }; - - // DO NOT RESET: (1) contentQueue, (2) position, (3) requestText (4) currentResponseTextLength - - private final Queue contentQueue; - private int position; - private String responseText; - private int currentResponseTextLength; - private boolean transferEncodingChunked; - - // DO NOT RESET: (1) headers, (2) fullResponses - - private Body body; - private boolean bodyOnly; - private int contentLength; - private int contentExtraLength; - private boolean continuation; - private Step currentStep; - private boolean keepAlive; - private long outOfContentTime; - private boolean stream; - private Version version; - - VirtualStateParser() { - this(false); - } - - VirtualStateParser(final boolean bodyOnly) { - this.bodyOnly = bodyOnly; - this.contentQueue = new LinkedList<>(); - this.currentStep = Step.NotStarted; - this.responseText = ""; - this.headers = new Headers<>(2); - this.fullResponses = new ArrayList<>(2); - - this.keepAlive = false; - this.stream = false; - - reset(); - } - - Response fullResponse() { - if (fullResponsesIterator == null) { - fullResponsesIterator = fullResponses.listIterator(); - } - if (fullResponsesIterator.hasNext()) { - final Response fullResponse = fullResponsesIterator.next(); - fullResponsesIterator.remove(); - return fullResponse; - } - fullResponsesIterator = null; - throw new IllegalStateException("Response is not completed."); - } - - boolean hasFullResponse() { - if (fullResponsesIterator != null) { - if (!fullResponsesIterator.hasNext()) { - fullResponsesIterator = null; - return false; - } else { - return true; - } - } - if (fullResponses.isEmpty()) { - fullResponsesIterator = null; - return false; - } - return true; - } - - boolean hasCompleted() { - if (isNotStarted() && position >= currentResponseTextLength && contentQueue.isEmpty()) { - responseText = compact(); - return true; - } - return false; - } - - boolean hasMissingContentTimeExpired(final long timeLimit) { - final long timeOutTime = outOfContentTime + timeLimit; - return timeOutTime < System.currentTimeMillis(); - } - - VirtualStateParser includes(final ByteBuffer responseContent) { - outOfContentTime = 0; - final String responseContentText = Converters.bytesToText(responseContent.array(), 0, responseContent.limit()); - final int utf8ExtraLength = responseContent.remaining() - responseContentText.length(); - if (contentQueue.isEmpty()) { - contentExtraLength += utf8ExtraLength; - responseText = responseText + responseContentText; - currentResponseTextLength = responseText.length(); - } else { - contentQueue.add(new ContentPacket(responseContentText, utf8ExtraLength)); - } - return this; - } - - boolean isKeepAliveConnection() { - return keepAlive; - } - - boolean isMissingContent() { - return outOfContentTime > 0; - } - - boolean isStreamContentType() { - return stream; - } - - VirtualStateParser parse() { - while (!hasCompleted()) { - try { - if (isNotStarted()) { -// System.out.println("NOT STARTED: "); - nextStep(); - } else if (isStatusLineStep()) { -// System.out.println("STATUS LINE"); - parseStatusLine(); - } else if (isHeadersStep()) { -// System.out.println("HEADERS"); - parseHeaders(); - } else if (isBodyStep()) { -// System.out.println("BODY"); - parseBody(); -// System.out.println("BODY: " + body); - } else if (isCompletedStep()) { -// System.out.println("COMPLETED: REMAINING: " + responseText.substring(position)); - continuation = false; - newResponse(); - } - } catch (OutOfContentException e) { - continuation = true; - outOfContentTime = System.currentTimeMillis(); - return this; - } catch (Throwable t) { - throw t; - } - } - - prepareForStream(); - - return this; - } - - private void clearContent() { - responseText = ""; - currentResponseTextLength = 0; - position = 0; - } - - private String compact() { - final String compact = responseText.substring(position); - position = 0; - currentResponseTextLength = compact.length(); - return compact; - } - private String nextLine(final boolean mayBeBlank, final String errorMessage) { - int possibleCarriageReturnIndex = -1; - final int lineBreak = responseText.indexOf("\n", position); - if (lineBreak < 0) { - if (contentQueue.isEmpty()) { - responseText = compact(); - throw new OutOfContentException(); - } - final ContentPacket packet = contentQueue.poll(); - contentExtraLength += packet.utf8ExtraLength; - responseText = compact() + packet.content; - return nextLine(mayBeBlank, errorMessage); - } else if (lineBreak == 0) { - possibleCarriageReturnIndex = 0; - } - final int endOfLine = responseText.charAt(lineBreak + possibleCarriageReturnIndex) == '\r' ? lineBreak - 1 : lineBreak; - final String line = responseText.substring(position, endOfLine).trim(); - position = lineBreak + 1; - return line; - } - - private void nextStep() { - if (isNotStarted()) { - currentStep = Step.StatusLine; - } else if (isStatusLineStep()) { - currentStep = Step.Headers; - } else if (isHeadersStep()) { - currentStep = Step.Body; - } else if (isBodyStep()) { - currentStep = Step.Completed; - } else if (isCompletedStep()) { - currentStep = Step.NotStarted; - } - } - - private boolean isBodyStep() { - return currentStep == Step.Body; - } - - private boolean isCompletedStep() { - return currentStep == Step.Completed; - } - - private boolean isHeadersStep() { - return currentStep == Step.Headers; - } - - private boolean isNotStarted() { - return currentStep == Step.NotStarted; - } - - private boolean isStatusLineStep() { - return currentStep == Step.StatusLine; - } - - private void parseBody() { - if (bodyOnly) { - contentLength = responseText.length(); - } - - continuation = false; - if (contentLength > 0) { - final int endIndex = position + contentLength; - if (currentResponseTextLength + contentExtraLength < endIndex) { - if (contentQueue.isEmpty()) { - responseText = compact(); - throw new OutOfContentException(); - } - final ContentPacket packet = contentQueue.poll(); - responseText = compact() + packet.content; - contentExtraLength += packet.utf8ExtraLength; - parseBody(); - return; - } - body = Body.from(responseText.substring(position, endIndex - contentExtraLength)); - position += (contentLength - contentExtraLength); - } else if (transferEncodingChunked) { - body = Body.from(parseChunks()); - } else { - body = Body.empty(); - } - nextStep(); - } - - private String parseBodyChunk(final int chunkLength) { - final int endIndex = position + chunkLength; - if (currentResponseTextLength < endIndex) { - if (contentQueue.isEmpty()) { - responseText = compact(); - throw new OutOfContentException(); - } - responseText = compact() + contentQueue.poll(); - } - final String chunk = responseText.substring(position, endIndex); - position += chunk.length(); -// System.out.println("CHUNK: " + chunk); - return chunk; - } - - private String parseChunks() { - final StringBuilder builder = new StringBuilder(); - - int chunkLength = chunkLength(); -// System.out.println("CHUNK LENGTH: " + chunkLength); - while (chunkLength > 0) { - final String chunk = parseBodyChunk(chunkLength); - builder.append(chunk); - chunkLength = chunkLength(); -// System.out.println("CHUNK LENGTH: " + chunkLength); - } - - clearContent(); - - return builder.toString(); - } - - private void parseHeaders() { - if (bodyOnly) { - nextStep(); - return; - } - if (!continuation) { - headers = new Headers<>(2); - } - continuation = false; - while (true) { - final String maybeHeaderLine = nextLine(true, null); - if (maybeHeaderLine.isEmpty()) { - break; - } - final ResponseHeader header = ResponseHeader.from(maybeHeaderLine); - headers.add(header); - if (contentLength == 0) { - final int maybeContentLength = header.ifContentLength(); - if (maybeContentLength >= 0) { - contentLength = maybeContentLength; - } else if (header.isTransferEncodingChunked()) { - transferEncodingChunked = true; - } - } - if (!keepAlive && header.isKeepAliveConnection()) { - this.keepAlive = true; - } else if (!stream && header.isStreamContentType()) { - this.stream = true; - } - } - nextStep(); - } - - private void parseStatusLine() { - if (bodyOnly) { - version = Version.Http1_1; - status = Response.Status.Ok; - nextStep(); - return; - } - - continuation = false; - final String line = nextLine(false, "Response status line is required."); - final int spaceIndex = line.indexOf(' '); - - try { - version = Version.from(line.substring(0, spaceIndex).trim()); - status = Response.Status.valueOfRawState(line.substring(spaceIndex + 1).trim()); - - nextStep(); - } catch (Throwable e) { - throw new IllegalArgumentException("Response status line parsing exception: " + e.getMessage(), e); - } - } - - private void prepareForStream() { - if (!bodyOnly) { - if (keepAlive && stream) { - bodyOnly = true; - } - } - } - - private void newResponse() { - final Response response = Response.of(version, status, headers, body); - fullResponses.add(response); - reset(); - nextStep(); - } - - private void reset() { - // DO NOT RESET: (1) contentQueue, (2) position, (3) responseText, (4) headers, (5) fullResponses - - this.body = null; - this.contentLength = 0; - this.contentExtraLength = 0; - this.continuation = false; - this.outOfContentTime = 0; - this.status = null; - this.version = null; - this.transferEncodingChunked = false; - } - - private int chunkLength() { - try { - String line = nextLine(false, "Missing chunk length."); - if (line.isEmpty()) { - line = nextLine(false, "Missing chunk length."); - } -// System.out.println("CHUNK LENGTH TEXT: " + line); - return Integer.parseInt(line, 16); - } catch (Exception e) { - return 0; - } - } - } } diff --git a/src/main/java/io/vlingo/xoom/http/VirtualStateParser.java b/src/main/java/io/vlingo/xoom/http/VirtualStateParser.java new file mode 100644 index 0000000..a378066 --- /dev/null +++ b/src/main/java/io/vlingo/xoom/http/VirtualStateParser.java @@ -0,0 +1,391 @@ +package io.vlingo.xoom.http; + +import io.vlingo.xoom.wire.message.Converters; + +import java.nio.ByteBuffer; +import java.util.*; + +class Responses{ + protected List fullResponses; + protected ListIterator fullResponsesIterator; + protected Header.Headers headers; + protected Response.Status status; + protected String responseText; + protected int currentResponseTextLength; + + Response fullResponse() { + if (fullResponsesIterator == null) { + fullResponsesIterator = fullResponses.listIterator(); + } + if (fullResponsesIterator.hasNext()) { + final Response fullResponse = fullResponsesIterator.next(); + fullResponsesIterator.remove(); + return fullResponse; + } + fullResponsesIterator = null; + throw new IllegalStateException("Response is not completed."); + } + + boolean hasFullResponse() { + if (fullResponsesIterator != null) { + if (!fullResponsesIterator.hasNext()) { + fullResponsesIterator = null; + return false; + } else { + return true; + } + } + if (fullResponses.isEmpty()) { + fullResponsesIterator = null; + return false; + } + return true; + } + +} + +//========================================= +// VirtualStateParser +//========================================= +public class VirtualStateParser extends Responses{ + private static class OutOfContentException extends RuntimeException { private static final long serialVersionUID = 1L; } + + private enum Step { NotStarted, StatusLine, Headers, Body, Completed }; + + // DO NOT RESET: (1) contentQueue, (2) position, (3) requestText (4) currentResponseTextLength + + private final Queue contentQueue; + private int position; + + private boolean transferEncodingChunked; + + // DO NOT RESET: (1) headers, (2) fullResponses + + private Body body; + private boolean bodyOnly; + private int contentLength; + private int contentExtraLength; + private boolean continuation; + private Step currentStep; + private boolean keepAlive; + private long outOfContentTime; + private boolean stream; + private Version version; + + VirtualStateParser() { + this(false); + } + + VirtualStateParser(final boolean bodyOnly) { + this.bodyOnly = bodyOnly; + this.contentQueue = new LinkedList<>(); + this.currentStep = Step.NotStarted; + this.responseText = ""; + this.headers = new Header.Headers<>(2); + this.fullResponses = new ArrayList<>(2); + + this.keepAlive = false; + this.stream = false; + + reset(); + } + + boolean hasCompleted() { + if (isNotStarted() && position >= currentResponseTextLength && contentQueue.isEmpty()) { + responseText = compact(); + return true; + } + return false; + } + + boolean hasMissingContentTimeExpired(final long timeLimit) { + final long timeOutTime = outOfContentTime + timeLimit; + return timeOutTime < System.currentTimeMillis(); + } + + VirtualStateParser includes(final ByteBuffer responseContent) { + outOfContentTime = 0; + final String responseContentText = Converters.bytesToText(responseContent.array(), 0, responseContent.limit()); + final int utf8ExtraLength = responseContent.remaining() - responseContentText.length(); + if (contentQueue.isEmpty()) { + contentExtraLength += utf8ExtraLength; + responseText = responseText + responseContentText; + currentResponseTextLength = responseText.length(); + } else { + contentQueue.add(new ContentPacket(responseContentText, utf8ExtraLength)); + } + return this; + } + + boolean isKeepAliveConnection() { + return keepAlive; + } + + boolean isMissingContent() { + return outOfContentTime > 0; + } + + boolean isStreamContentType() { + return stream; + } + + VirtualStateParser parse() { + while (!hasCompleted()) { + try { + if (isNotStarted()) { +// System.out.println("NOT STARTED: "); + nextStep(); + } else if (isStatusLineStep()) { +// System.out.println("STATUS LINE"); + parseStatusLine(); + } else if (isHeadersStep()) { +// System.out.println("HEADERS"); + parseHeaders(); + } else if (isBodyStep()) { +// System.out.println("BODY"); + parseBody(); +// System.out.println("BODY: " + body); + } else if (isCompletedStep()) { +// System.out.println("COMPLETED: REMAINING: " + responseText.substring(position)); + continuation = false; + newResponse(); + } + } catch (OutOfContentException e) { + continuation = true; + outOfContentTime = System.currentTimeMillis(); + return this; + } catch (Throwable t) { + throw t; + } + } + + prepareForStream(); + + return this; + } + + private void clearContent() { + responseText = ""; + currentResponseTextLength = 0; + position = 0; + } + + private String compact() { + final String compact = responseText.substring(position); + position = 0; + currentResponseTextLength = compact.length(); + return compact; + } + + private String nextLine(final boolean mayBeBlank, final String errorMessage) { + int possibleCarriageReturnIndex = -1; + final int lineBreak = responseText.indexOf("\n", position); + if (lineBreak < 0) { + if (contentQueue.isEmpty()) { + responseText = compact(); + throw new OutOfContentException(); + } + final ContentPacket packet = contentQueue.poll(); + contentExtraLength += packet.utf8ExtraLength; + responseText = compact() + packet.content; + return nextLine(mayBeBlank, errorMessage); + } else if (lineBreak == 0) { + possibleCarriageReturnIndex = 0; + } + final int endOfLine = responseText.charAt(lineBreak + possibleCarriageReturnIndex) == '\r' ? lineBreak - 1 : lineBreak; + final String line = responseText.substring(position, endOfLine).trim(); + position = lineBreak + 1; + return line; + } + + private void nextStep() { + if (isNotStarted()) { + currentStep = Step.StatusLine; + } else if (isStatusLineStep()) { + currentStep = Step.Headers; + } else if (isHeadersStep()) { + currentStep = Step.Body; + } else if (isBodyStep()) { + currentStep = Step.Completed; + } else if (isCompletedStep()) { + currentStep = Step.NotStarted; + } + } + + private boolean isBodyStep() { + return currentStep == Step.Body; + } + + private boolean isCompletedStep() { + return currentStep == Step.Completed; + } + + private boolean isHeadersStep() { + return currentStep == Step.Headers; + } + + private boolean isNotStarted() { + return currentStep == Step.NotStarted; + } + + private boolean isStatusLineStep() { + return currentStep == Step.StatusLine; + } + + private void parseBody() { + if (bodyOnly) { + contentLength = responseText.length(); + } + + continuation = false; + if (contentLength > 0) { + final int endIndex = position + contentLength; + if (currentResponseTextLength + contentExtraLength < endIndex) { + if (contentQueue.isEmpty()) { + responseText = compact(); + throw new OutOfContentException(); + } + final ContentPacket packet = contentQueue.poll(); + responseText = compact() + packet.content; + contentExtraLength += packet.utf8ExtraLength; + parseBody(); + return; + } + body = Body.from(responseText.substring(position, endIndex - contentExtraLength)); + position += (contentLength - contentExtraLength); + } else if (transferEncodingChunked) { + body = Body.from(parseChunks()); + } else { + body = Body.empty(); + } + nextStep(); + } + + private String parseBodyChunk(final int chunkLength) { + final int endIndex = position + chunkLength; + if (currentResponseTextLength < endIndex) { + if (contentQueue.isEmpty()) { + responseText = compact(); + throw new OutOfContentException(); + } + responseText = compact() + contentQueue.poll(); + } + final String chunk = responseText.substring(position, endIndex); + position += chunk.length(); +// System.out.println("CHUNK: " + chunk); + return chunk; + } + + private String parseChunks() { + final StringBuilder builder = new StringBuilder(); + + int chunkLength = chunkLength(); +// System.out.println("CHUNK LENGTH: " + chunkLength); + while (chunkLength > 0) { + final String chunk = parseBodyChunk(chunkLength); + builder.append(chunk); + chunkLength = chunkLength(); +// System.out.println("CHUNK LENGTH: " + chunkLength); + } + + clearContent(); + + return builder.toString(); + } + + private void parseHeaders() { + if (bodyOnly) { + nextStep(); + return; + } + if (!continuation) { + headers = new Header.Headers<>(2); + } + continuation = false; + while (true) { + final String maybeHeaderLine = nextLine(true, null); + if (maybeHeaderLine.isEmpty()) { + break; + } + final ResponseHeader header = ResponseHeader.from(maybeHeaderLine); + headers.add(header); + if (contentLength == 0) { + final int maybeContentLength = header.ifContentLength(); + if (maybeContentLength >= 0) { + contentLength = maybeContentLength; + } else if (header.isTransferEncodingChunked()) { + transferEncodingChunked = true; + } + } + if (!keepAlive && header.isKeepAliveConnection()) { + this.keepAlive = true; + } else if (!stream && header.isStreamContentType()) { + this.stream = true; + } + } + nextStep(); + } + + private void parseStatusLine() { + if (bodyOnly) { + version = Version.Http1_1; + status = Response.Status.Ok; + nextStep(); + return; + } + + continuation = false; + final String line = nextLine(false, "Response status line is required."); + final int spaceIndex = line.indexOf(' '); + + try { + version = Version.from(line.substring(0, spaceIndex).trim()); + status = Response.Status.valueOfRawState(line.substring(spaceIndex + 1).trim()); + + nextStep(); + } catch (Throwable e) { + throw new IllegalArgumentException("Response status line parsing exception: " + e.getMessage(), e); + } + } + + private void prepareForStream() { + if (!bodyOnly) { + if (keepAlive && stream) { + bodyOnly = true; + } + } + } + + private void newResponse() { + final Response response = Response.of(version, status, headers, body); + fullResponses.add(response); + reset(); + nextStep(); + } + + private void reset() { + // DO NOT RESET: (1) contentQueue, (2) position, (3) responseText, (4) headers, (5) fullResponses + + this.body = null; + this.contentLength = 0; + this.contentExtraLength = 0; + this.continuation = false; + this.outOfContentTime = 0; + this.status = null; + this.version = null; + this.transferEncodingChunked = false; + } + + private int chunkLength() { + try { + String line = nextLine(false, "Missing chunk length."); + if (line.isEmpty()) { + line = nextLine(false, "Missing chunk length."); + } +// System.out.println("CHUNK LENGTH TEXT: " + line); + return Integer.parseInt(line, 16); + } catch (Exception e) { + return 0; + } + } +} diff --git a/src/main/java/io/vlingo/xoom/http/resource/Action.java b/src/main/java/io/vlingo/xoom/http/resource/Action.java index 1f0b1b8..2cd8ebd 100644 --- a/src/main/java/io/vlingo/xoom/http/resource/Action.java +++ b/src/main/java/io/vlingo/xoom/http/resource/Action.java @@ -619,9 +619,9 @@ private Tuple2> parse(final String to) { final String methodName = to.substring(0, openParen); final String[] rawParameters = to.substring(openParen + 1, closeParen).split(","); - List parameter = new ArrayList<>(rawParameters.length); + //List parameter = new ArrayList<>(rawParameters.length); - parameter = getTheParameters(rawParameters); + List parameter = getTheParameters(rawParameters); return Tuple2.from(methodName, parameter); }