org.zodiac.sdk.nio.http.common.HTTPRequest 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.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.zodiac.sdk.nio.common.InetLocation;
import org.zodiac.sdk.nio.http.NioHttpConstants;
//@Builder(toBuilder = true)
public class HTTPRequest {
private final HTTPMethod method;
private final InetLocation inetLocation;
private final InetLocation routerAddress;
private final HttpHeaders headers;
private final String body;
private final File in;
private final File out;
private final String path;
private HTTPRequest(HTTPMethod method, InetLocation inetLocation, InetLocation routerAddress,
HttpHeaders headers, String body, File in, File out, String path) {
super();
this.method = method;
this.inetLocation = inetLocation;
this.routerAddress = routerAddress;
this.headers = headers;
this.body = body;
this.in = in;
this.out = out;
this.path = path;
}
public HTTPMethod method() {
return method;
}
public InetLocation url() {
return inetLocation;
}
public InetSocketAddress socketAddress() {
return inetLocation.getSocketAddress();
}
public InetSocketAddress routerAddress() {
return routerAddress.getSocketAddress();
}
public String host() {
return inetLocation.getHost();
}
public String path() {
if (path == null) {
return inetLocation.getPath() + (inetLocation.getQuery() == null ? "" : "?" + inetLocation.getQuery());
} else {
final String tryPath = inetLocation.getPath() + (inetLocation.getQuery() == null ? "" : "?" + inetLocation.getQuery());
if (!tryPath.equals("")) {
return inetLocation.getPath() + (inetLocation.getQuery() == null ? "" : "?" + inetLocation.getQuery());
} else {
return path;
}
}
}
public HttpHeaders headers() {
return headers;
}
public String body() {
return body;
}
public File in() {
return in;
}
public File out() {
return out;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append(String.format("%s %s %s%s", method().name(), path().equals("") ? "/" : path(), HttpProtocolVersion.HTTP_1_0.getText(), NioHttpConstants.CRLF));
sb.append(String.format("Host: %s%s", host(), NioHttpConstants.CRLF));
if (headers() != null) {
for (final Entry entry : headers().firstValueEntrySet()) {
sb.append(String.format("%s: %s%s", entry.getKey(), entry.getValue(), NioHttpConstants.CRLF));
}
}
addHeaderIfAbsent(sb, NioHttpConstants.Headers.CONTENT_TYPE, NioHttpConstants.Headers.APPLICATION_JSON);
if (body() != null) {
addHeaderIfAbsent(sb, NioHttpConstants.Headers.CONTENT_LENGTH, body().length());
}
if (body() != null) {
sb.append(NioHttpConstants.CRLF);
sb.append(String.format("%s", body()));
}
sb.append(NioHttpConstants.CRLF);
return sb.toString();
}
public static Either of(final String spec) throws RequestError {
final Builder requestBuilder = HTTPRequest.builder();
requestBuilder.spec(spec);
int lineBreakCount = 0;
int lineCount = 1;
String host;
String path = "";
final List headers = new ArrayList<>();
for (final String line : spec.split(NioHttpConstants.CRLF)) {
if (lineCount == 1) {
final String[] lexemes = line.split("\\s+");
if (lexemes.length != 3) {
return Either.right("First line of HTTP request did not contain 3 space delimited lexemes: " + line);
}
requestBuilder.method(HTTPMethod.of(lexemes[0]));
path = lexemes[1];
} else if (lineCount == 2) {
if (!line.contains("Host: ")) {
return Either.right("Second line of HTTP request did not contain 'Host: ..' information");
}
//host = Pattern.compile("Host: (\\S+)").matcher(line).results().map(ee -> ee.group(1)).findFirst().orElse(null);
host = Optional.ofNullable(Pattern.compile("Host: (\\S+)").matcher(line).group(1)).orElse(null);
requestBuilder.url("http://" + host + path);
} else if (!line.trim().equalsIgnoreCase("") && lineBreakCount == 0) {
headers.add(line.trim());
}
if (line.equalsIgnoreCase("")) {
lineBreakCount += 1;
} else if (lineBreakCount == 1) {
requestBuilder.body(line);
}
lineCount += 1;
}
if (!headers.isEmpty()) {
requestBuilder.headers(headers);
}
try {
final HTTPRequest request = requestBuilder.build();
final Either isValid = request.valid();
return isValid.isLeft() ? Either.left(request) : Either.right(isValid.get());
} catch (final IOException e) {
return Either.right("received IOException: " + e.getMessage());
}
}
public Either valid() {
if (url().getPath() == null) {
return Either.right("url.path was null");
}
if (method == null) {
return Either.right("method was null");
}
if (headers == null) {
return Either.right("headers were null");
}
return validBody();
}
private Either validBody() {
if (method() == HTTPMethod.POST && headers != null) {
final String contentLength = headers.getFirstOrDefault("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);
}
private void addHeaderIfAbsent(final StringBuilder sb, final String headerKey, final String headerValue) {
if (headers() != null && !headers.containsKey(headerKey)) {
sb.append(String.format("%s: %s%s", headerKey, headerValue, NioHttpConstants.CRLF));
}
}
private void addHeaderIfAbsent(final StringBuilder sb, final String headerKey, final int headerValue) {
addHeaderIfAbsent(sb, headerKey, Integer.toString(headerValue));
}
public Builder toBuilder() {;
return builder().body(body).headers(headers.collectHeaders()).in(in.getPath()).method(method).out(out.getPath())
.routerAddress(routerAddress.getUrl().getPath())
.url(inetLocation.getUrl().getPath());
}
public static class RequestError extends Exception {
private static final long serialVersionUID = 6419423377670554409L;
public RequestError(final String message) {
super(message);
}
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private HTTPMethod method = null;
private String url = null;
private String routerAddress = null;
private List headers = null;
private String body = null;
private String in = null;
private String out = null;
private String spec = null;
private String path = null;
public Builder() {
super();
}
public Builder path(final String path) {
this.path = path;
return this;
}
public Builder spec(final String spec) {
this.spec = spec;
return this;
}
public Builder method(final HTTPMethod method) {
this.method = method;
return this;
}
public Builder url(final String url) {
this.url = url;
return this;
}
public Builder routerAddress(final String address) {
routerAddress = address;
return this;
}
public Builder headers(final List headers) {
this.headers = headers;
return this;
}
public Builder headers(final Map headers) {
this.headers = headers.entrySet().stream().map(e -> e.getKey() + ": " + e.getValue()).collect(Collectors.toList());
return this;
}
public Builder body(final String body) {
this.body = body;
return this;
}
public Builder in(final String in) {
this.in = in;
return this;
}
public Builder out(final String out) {
this.out = out;
return this;
}
public Builder inetLocation(final InetLocation inetLocation) {
this.url = inetLocation.getUrl().getPath();
return this;
}
public HTTPRequest build() throws RequestError, IOException {
if (method == HTTPMethod.POST && body != null && in != null) {
throw new RequestError("Fields body and in cannot be both specified.");
}
if (in != null) {
body = new String(Files.readAllBytes(Paths.get(in)));
}
final HttpHeaders mappedHeaders = new HttpHeaders();
if (headers != null) {
for (final String header : headers) {
final String[] matches = header.split(":", 2);
if (matches.length != 2) {
throw new RequestError("Error occurred while parsing the header: " + header + ". Should of been delimited by a `:` such as `key:value`, but was not.");
}
final String key = matches[0].trim();
final String value = matches[1].trim();
if (mappedHeaders.containsKey(key)) {
throw new RequestError("Error occurred while parsing the header: " + header + ". A duplicate key was found: " + key + ".");
}
mappedHeaders.set(key, value);
}
}
return new HTTPRequest(
method,
InetLocation.fromSpec(url),
InetLocation.fromSpec(NioHttpConstants.DEFAULT_ROUTER_ADDRESS),
new HttpHeaders(mappedHeaders),
body,
in != null ? new File(in) : null,
out != null ? new File(out) : null,
path);
}
}
}