HttpRequest.java
/*
* Copyright 2016 The Lannister Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.anyflow.lannister.http;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.cookie.ClientCookieEncoder;
import io.netty.handler.codec.http.cookie.Cookie;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import io.netty.util.CharsetUtil;
/**
* @author anyflow
*/
public class HttpRequest extends DefaultFullHttpRequest {
private static final Logger logger = LoggerFactory.getLogger(HttpRequest.class);
private final Map<String, List<String>> parameters;
private final URI uri;
private final Set<Cookie> cookies;
private Map<String, String> pathParameters;
protected HttpRequest(FullHttpRequest fullHttpRequest) throws URISyntaxException {
super(fullHttpRequest.protocolVersion(), fullHttpRequest.method(), fullHttpRequest.uri(),
fullHttpRequest.content().copy());
this.headers().set(fullHttpRequest.headers());
this.trailingHeaders().set(fullHttpRequest.trailingHeaders());
this.setDecoderResult(fullHttpRequest.decoderResult());
this.uri = createUriWithNormalizing(fullHttpRequest.uri());
this.parameters = parameters();
this.cookies = cookies();
this.pathParameters = Maps.newHashMap();
}
private URI createUriWithNormalizing(String uri) throws URISyntaxException {
URI temp = new URI(uri);
String scheme = temp.getScheme();
if (scheme == null) {
scheme = "http";
}
int port = temp.getPort();
if (port == -1) {
if (scheme.equalsIgnoreCase("http")) {
port = 80;
}
else if (scheme.equalsIgnoreCase("https")) {
port = 443;
}
else {
throw new URISyntaxException(uri, "Invalid protocol.");
}
}
return new URI(scheme, temp.getUserInfo(), temp.getHost(), port, temp.getPath(), temp.getQuery(),
temp.getFragment());
}
public Set<Cookie> cookies() {
if (cookies != null) { return cookies; }
String cookie = headers().get(HttpHeaderNames.COOKIE);
if (cookie == null || "".equals(cookie)) { return new HashSet<Cookie>(); }
Set<Cookie> ret = ServerCookieDecoder.STRICT.decode(cookie);
return ret == null || ret.isEmpty() ? new HashSet<Cookie>() : ret;
}
public Map<String, String> pathParameters() {
return pathParameters;
}
public HttpRequest pathParameters(Map<String, String> pathParameters) {
this.pathParameters = pathParameters;
return this;
}
public String pathParameter(String name) {
return pathParameters.get(name);
}
public Map<String, List<String>> parameters() {
if (parameters != null) { return parameters; }
Map<String, List<String>> ret = Maps.newHashMap();
if (HttpMethod.GET.equals(method()) || HttpMethod.DELETE.equals(method())) {
ret.putAll(new QueryStringDecoder(uri()).parameters());
return ret;
}
else if (headers().contains(HttpHeaderNames.CONTENT_TYPE)
&& headers().get(HttpHeaderNames.CONTENT_TYPE)
.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString())
&& (HttpMethod.POST.equals(method()) || HttpMethod.PUT.equals(method()))) {
ret.putAll(new QueryStringDecoder("/dummy?" + content().toString(CharsetUtil.UTF_8)).parameters());
}
return ret;
}
/**
* Get single parameter. In case of multiple values, the method returns the
* first.
*
* @param name
* parameter name.
* @return The first value of the parameter name. If it does not exist, it
* returns null.
*/
public String parameter(String name) {
if (!parameters().containsKey(name) || parameters.get(name).size() <= 0) { return null; }
return parameters().get(name).get(0);
}
public HttpRequest addParameter(String name, String value) {
List<String> values = parameters().get(name);
if (values == null) {
values = new ArrayList<String>();
values.add(value);
parameters().put(name, values);
}
else {
values.clear();
values.add(value);
}
return this;
}
@Override
public boolean equals(Object o) {
return toString().equals(o.toString()); // Just for removing FindBugs
// issue
}
@Override
public int hashCode() {
return toString().hashCode(); // Just for removing FindBugs issue
}
public String toString(Date incomingTime) {
StringBuilder buf = new StringBuilder();
buf.append("\r\n");
buf.append("Request URI: ").append(this.uri()).append("\r\n");
buf.append("HTTP METHOD: ").append(this.method().toString()).append("\r\n");
buf.append("Requested Time: ").append(incomingTime.toString()).append("\r\n");
buf.append("Version: ").append(this.protocolVersion()).append("\r\n");
buf.append("Request Headers:").append("\r\n");
List<Entry<String, String>> headers = this.headers().entries();
if (!headers.isEmpty()) {
for (Entry<String, String> h : this.headers().entries()) {
String key = h.getKey();
String value = h.getValue();
buf.append(" ").append(key).append(" = ").append(value).append("\r\n");
}
}
Map<String, List<String>> params = parameters();
buf.append("Query String Parameters: ");
if (params.isEmpty()) {
buf.append("NONE\r\n");
}
else {
for (Entry<String, List<String>> p : params.entrySet()) {
String key = p.getKey();
List<String> vals = p.getValue();
for (String val : vals) {
buf.append("\r\n ").append(key).append(" = ").append(val).append("\r\n");
}
}
}
buf.append("Content: ");
if (this.content().isReadable()) {
buf.append("\r\n ").append(content().toString(CharsetUtil.UTF_8));
}
else {
buf.append("UNREADABLE CONTENT or NONE");
}
DecoderResult result = this.decoderResult();
if (!result.isSuccess()) {
buf.append("\r\n").append(".. WITH DECODER FAILURE:");
buf.append("\r\n ").append(result.cause());
}
return buf.toString();
}
@Override
public String toString() {
return toString(new Date());
}
public void setContent(String content) {
byte[] contentByte = Strings.nullToEmpty(content).getBytes(CharsetUtil.UTF_8);
headers().set(HttpHeaderNames.CONTENT_LENGTH, contentByte.length);
content().writeBytes(contentByte);
logger.debug(content().toString(CharsetUtil.UTF_8));
}
public URI uriObject() {
return uri;
}
protected void normalize() {
normalizeParameters();
String encoded = ClientCookieEncoder.STRICT.encode(cookies);
if (encoded == null) { return; }
headers().set(HttpHeaderNames.COOKIE, encoded);
}
private String convertParametersToString() {
StringBuilder builder = new StringBuilder();
for (String name : parameters().keySet()) {
for (String value : parameters().get(name)) {
builder = builder.append(name).append("=").append(value).append("&");
}
}
String ret = builder.toString();
if (ret.length() <= 0) { return ""; }
if (ret.charAt(ret.length() - 1) == '&') {
return ret.substring(0, ret.length() - 1);
}
else {
return ret;
}
}
private void normalizeParameters() {
String address = new StringBuilder().append(uriObject().getScheme()).append("://")
.append(uriObject().getAuthority()).append(uriObject().getPath()).toString();
if (HttpMethod.GET.equals(method()) || HttpMethod.DELETE.equals(method())) {
String parameters = convertParametersToString();
address += Strings.isNullOrEmpty(parameters) ? "" : "?" + parameters;
}
else if ((HttpMethod.POST.equals(method()) || HttpMethod.PUT.equals(method()))
&& (!headers().contains(HttpHeaderNames.CONTENT_TYPE) || headers().get(HttpHeaderNames.CONTENT_TYPE)
.startsWith(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString()))) {
ByteBuf content = Unpooled.copiedBuffer(convertParametersToString(), CharsetUtil.UTF_8);
headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
content().clear();
content().writeBytes(content);
}
setUri(address);
}
}