org.zodiac.sdk.nio.http.common.HTTPResponse Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zodiac-sdk-nio Show documentation
Show all versions of zodiac-sdk-nio Show documentation
Zodiac SDK NIO2(New Non-Blocking IO)
package org.zodiac.sdk.nio.http.common;
import io.vavr.control.Either;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.zodiac.sdk.nio.http.NioHttpConstants;
import org.zodiac.sdk.toolkit.util.StringUtil;
//@Builder(toBuilder = true)
public final class HTTPResponse {
private static final String STATUS_LINE_REGEX = "^HTTP/\\d.\\d (\\d+) (.*)\r?$";
private HTTPRequest request;
private String statusCode;
private String statusMessage;
private HttpHeaders headers;
private String body;
public HTTPResponse() {
super();
}
public HTTPResponse(final HTTPRequest request, final String messageHeader, final String messageBody) {
this.request = request;
body = messageBody;
/*
final Stream statusLine = Arrays.stream(messageHeader.split("\n"))
.filter(e -> e.matches(STATUS_LINE_REGEX))
.map(e -> Pattern.compile(STATUS_LINE_REGEX).matcher(e).results().map(ee -> ee.group(0)).findFirst().orElse(null))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
statusCode = Pattern.compile(STATUS_LINE_REGEX)
.matcher(Objects.requireNonNullElse(statusLine, ""))
.results()
.map(e -> e.group(1))
.map(String::trim)
.findFirst()
.orElse(null);
statusMessage = Pattern.compile(STATUS_LINE_REGEX)
.matcher(Objects.requireNonNullElse(statusLine, ""))
.results()
.map(e -> e.group(2))
.map(String::trim)
.findFirst()
.orElse(null);
headers = Arrays.stream(messageHeader.split("\n"))
.filter(e -> e.contains(":"))
.map(e -> e.split(": "))
.collect(Collectors.toMap(e -> e[0], e -> e[1], (value1, value2) -> {
System.err.println("Duplicate key found. Discarding value: " + value1);
return value2;
}));
*/
final String statusLine = Arrays.stream(messageHeader.split("\n"))
.filter(e -> e.matches(STATUS_LINE_REGEX))
.map(e -> {
Matcher matcher = Pattern.compile(STATUS_LINE_REGEX).matcher(e);
return matcher;
})
.map(m -> m.group(0))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
statusCode = Arrays.stream(new String[] {Pattern.compile(STATUS_LINE_REGEX)
.matcher(Optional.ofNullable(statusLine).orElse("")).group(1)})
.map(String::trim)
.findFirst()
.orElse(null);
statusMessage = Arrays.stream(new String[] {Pattern.compile(STATUS_LINE_REGEX)
.matcher(Optional.ofNullable(statusLine).orElse("")).group(2)})
.map(String::trim)
.findFirst()
.orElse(null);
headers = new HttpHeaders(Arrays.stream(messageHeader.split("\n"))
.filter(e -> e.contains(":"))
.map(e -> e.split(": "))
.collect(Collectors.toMap(e -> e[0], e -> Arrays.asList(e[1]), (value1, value2) -> {
System.err.println("Duplicate key found. Discarding value: " + value1);
return value2;
})));
}
public HTTPResponse(HTTPRequest request, String statusCode, String statusMessage, HttpHeaders headers,
String body) {
this.request = request;
this.statusCode = statusCode;
this.statusMessage = statusMessage;
this.headers = headers;
this.body = body;
}
public HTTPRequest getRequest() {
return request;
}
public String getStatusCode() {
return statusCode;
}
public String getStatusMessage() {
return statusMessage;
}
public HttpHeaders getHeaders() {
return headers;
}
public String getBody() {
return body;
}
public HTTPResponse setRequest(HTTPRequest request) {
this.request = request;
return this;
}
public HTTPResponse setStatusCode(HttpResponseStatusEnum httpResponseStatus) {
if (null != httpResponseStatus)
this.statusCode = String.valueOf(httpResponseStatus.value());
return this;
}
public HTTPResponse setStatusMessage(HttpResponseStatusEnum httpResponseStatus) {
if (null != httpResponseStatus)
this.statusMessage = httpResponseStatus.getReasonPhrase();
return this;
}
public HTTPResponse setHeaders(HttpHeaders headers) {
this.headers = headers;
return this;
}
public HTTPResponse setBody(String body) {
this.body = body;
return this;
}
public Either valid() {
if (statusCode == null) {
return Either.right("statusCode was null");
}
if (statusMessage == null) {
return Either.right("statusMessage was null");
}
if (headers == null) {
return Either.right("headers were null");
}
return validBody();
}
private Either validBody() {
if (headers != null) {
final String contentLength = headers.getFirstOrDefault(HttpHeaders.CONTENT_LENGTH, "0");
final String bodyLength = String.valueOf(body != null ? body.length() : 0);
if (!contentLength.equals(bodyLength)) {
return Either.right(String.format(
"Content-Length header value (%s) did not match body's length parsed (%s)",
contentLength,
bodyLength));
}
}
return Either.left(true);
}
public String statusLine() {
return HttpProtocolVersion.HTTP_1_0.getText() + " " + statusCode + " " + statusMessage;
}
public String messageHeader() {
if (headers == null) {
return "";
}
return statusLine() + NioHttpConstants.CRLF + headers.entrySet()
.stream()
.map(e -> e.getKey() + ": " + e.getValue())
.collect(Collectors.joining("\n"));
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
boolean contentLengthAdded = false;
sb.append(String.format("%s %s %s%s", HttpProtocolVersion.HTTP_1_1.getText(), statusCode, statusMessage, NioHttpConstants.CRLF));
for (final Entry> header : headers.entrySet()) {
sb.append(String.format("%s: %s%s", header.getKey(), StringUtil.collectionToString(header.getValue()), NioHttpConstants.CRLF));
if (header.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
contentLengthAdded = true;
}
}
if (body != null) {
if (!contentLengthAdded) {
sb.append(String.format("%s: %s%s", NioHttpConstants.Headers.CONTENT_LENGTH, body.length(), NioHttpConstants.CRLF));
}
sb.append(NioHttpConstants.CRLF);
sb.append(body);
} else {
sb.append(NioHttpConstants.CRLF);
}
return sb.toString();
}
public static Either of(final HTTPRequest request, final String spec) {
try {
final List lines = new ArrayList<>(Arrays.asList(spec.split(NioHttpConstants.CRLF)));
final int lastLineOfHeaderIndex = indexOfBlankLine(spec);
if (lastLineOfHeaderIndex == -1) {
return Either.right("could not parse blank line in HTTP response");
}
final List messageHeader = lines.subList(0, lastLineOfHeaderIndex);
final List messageBody = lines.subList(lastLineOfHeaderIndex + 1, lines.size());
final HTTPResponse response = new HTTPResponse(request, String.join("\n", messageHeader), String.join("\n", messageBody));
final Either isValid = response.valid();
return isValid.isLeft() ? Either.left(response) : Either.right(isValid.get());
} catch (final Exception e) {
return Either.right("received IOException: " + e.getMessage());
}
}
private static int indexOfBlankLine(final String text) {
final List lines = new ArrayList<>(Arrays.asList(text.split("\n")));
for (int i = 0; i < lines.size(); i++) {
if (lines.get(i).equals("") || lines.get(i).equals("\r")) {
return i;
}
}
return -1;
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private HTTPRequest request;
private String statusCode;
private String statusMessage;
private HttpHeaders headers;
private String body;
public Builder() {
super();
}
public Builder request(HTTPRequest request) {
this.request = request;
return this;
}
public Builder statusCode(HttpResponseStatusEnum httpResponseStatus) {
this.statusCode = String.valueOf(httpResponseStatus.value());
return this;
}
public Builder statusMessage(HttpResponseStatusEnum httpResponseStatus) {
this.statusMessage = httpResponseStatus.getReasonPhrase();
return this;
}
public Builder headers(HttpHeaders headers) {
this.headers = headers;
return this;
}
public Builder body(String body) {
this.body = body;
return this;
}
public HTTPResponse build() {
return new HTTPResponse(request, statusCode, statusMessage, headers, body);
}
}
}