blob: 3e6654b35fc5f0301a1d7ce91b128203bd35c39f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.asyncweb.common;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.CharacterCodingException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TimeZone;
import org.apache.asyncweb.common.codec.HttpCodecUtils;
import org.apache.mina.core.buffer.IoBuffer;
/**
* A default implementation of {@link MutableHttpRequest}.
*
* @author The Apache MINA Project (dev@mina.apache.org)
*/
public class DefaultHttpRequest extends DefaultHttpMessage implements
MutableHttpRequest {
private static final long serialVersionUID = 3044997961372568928L;
private HttpMethod method = HttpMethod.GET;
private URI requestUri;
private Map<String, List<String>> parameters = new HashMap<String, List<String>>();
/**
* Creates a new instance.
*/
public DefaultHttpRequest() {
}
public void setCookies(String headerValue) {
clearCookies();
int version = -1; // -1 means version is not parsed yet.
int fieldIdx = 0;
MutableCookie currentCookie = null;
StringTokenizer tk = new StringTokenizer(headerValue, ";,");
while (tk.hasMoreTokens()) {
String pair = tk.nextToken();
String key;
String value;
int equalsPos = pair.indexOf('=');
if (equalsPos >= 0) {
key = pair.substring(0, equalsPos).trim();
value = pair.substring(equalsPos + 1).trim();
} else {
key = pair.trim();
value = "";
}
if (version < 0) {
if (!key.equalsIgnoreCase("$Version")) {
// $Version is not specified. Use the default (0).
version = 0;
} else {
version = Integer.parseInt(value);
if (version != 0 && version != 1) {
throw new IllegalArgumentException("Invalid version: "
+ version + " (" + headerValue + ")");
}
}
}
if (version >= 0) {
try {
switch (fieldIdx) {
case 1:
if (key.equalsIgnoreCase("$Path")) {
currentCookie.setPath(value);
fieldIdx++;
} else {
fieldIdx = 0;
}
break;
case 2:
if (key.equalsIgnoreCase("$Domain")) {
currentCookie.setDomain(value);
fieldIdx++;
} else {
fieldIdx = 0;
}
break;
case 3:
if (key.equalsIgnoreCase("$Port")) {
// Ignoring for now
fieldIdx++;
} else {
fieldIdx = 0;
}
break;
}
} catch (NullPointerException e) {
throw new IllegalArgumentException(
"Cookie key-value pair not found (" + headerValue
+ ")");
}
if (fieldIdx == 0) {
currentCookie = new DefaultCookie(key);
currentCookie.setVersion(version);
currentCookie.setValue(value);
addCookie(currentCookie);
fieldIdx++;
}
}
}
}
public URI getRequestUri() {
return requestUri;
}
public void setRequestUri(URI requestUri) {
if (requestUri == null) {
throw new NullPointerException("requestUri");
}
this.requestUri = requestUri;
}
public boolean requiresContinuationResponse() {
if (getProtocolVersion() == HttpVersion.HTTP_1_1) {
String expectations = getHeader(HttpHeaderConstants.KEY_EXPECT);
if (expectations != null) {
return expectations
.indexOf(HttpHeaderConstants.VALUE_CONTINUE_EXPECTATION) >= 0;
}
}
return false;
}
public HttpMethod getMethod() {
return method;
}
public void setMethod(HttpMethod method) {
if (method == null) {
throw new NullPointerException("method");
}
this.method = method;
}
public void addParameter(String name, String value) {
List<String> values = parameters.get(name);
if (values == null) {
values = new ArrayList<String>();
parameters.put(name, values);
}
values.add(value);
}
public boolean removeParameter(String name) {
return parameters.remove(name) != null;
}
public void setParameter(String name, String value) {
List<String> values = new ArrayList<String>();
values.add(value);
parameters.put(name, values);
}
public void setParameters(Map<String, List<String>> parameters) {
clearParameters();
for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
for (String value : entry.getValue()) {
if (value == null) {
throw new NullPointerException("Parameter '"
+ entry.getKey() + "' contains null.");
}
}
if (entry.getValue().size() > 0) {
this.parameters.put(entry.getKey(), entry.getValue());
}
}
}
public void setParameters(String queryString) {
try {
this.setParameters(queryString, HttpCodecUtils.DEFAULT_CHARSET_NAME);
} catch (UnsupportedEncodingException e) {
throw new InternalError(
HttpCodecUtils.DEFAULT_CHARSET_NAME +
" decoder must be provided by JDK.");
}
}
public void setParameters(String queryString, String encoding)
throws UnsupportedEncodingException {
clearParameters();
if (queryString == null || queryString.length() == 0) {
return;
}
int pos = 0;
while (pos < queryString.length()) {
int ampPos = queryString.indexOf('&', pos);
String value;
if (ampPos < 0) {
value = queryString.substring(pos);
ampPos = queryString.length();
} else {
value = queryString.substring(pos, ampPos);
}
int equalPos = value.indexOf('=');
if (equalPos < 0) {
this.addParameter(URLDecoder.decode(value, encoding), "");
} else {
this.addParameter(URLDecoder.decode(value
.substring(0, equalPos), encoding), URLDecoder.decode(
value.substring(equalPos + 1), encoding));
}
pos = ampPos + 1;
}
}
public void clearParameters() {
this.parameters.clear();
}
public boolean containsParameter(String name) {
return parameters.containsKey(name);
}
public String getParameter(String name) {
List<String> values = parameters.get(name);
if (values == null) {
return null;
}
return values.get(0);
}
public Map<String, List<String>> getParameters() {
return Collections.unmodifiableMap(parameters);
}
@Override
public void setContent(IoBuffer content) {
if (content == null) {
throw new NullPointerException("content");
}
String ct = getContentType();
if (ct != null && ct.toLowerCase().startsWith(HttpHeaderConstants.VALUE_URLENCODED_FORM.toLowerCase())) {
content.mark();
try {
setParameters(content.getString(
HttpCodecUtils.DEFAULT_CHARSET.newDecoder()));
} catch (CharacterCodingException e) {
throw new IllegalArgumentException(
"Failed to decode the url-encoded content.", e);
} finally {
content.reset();
}
}
super.setContent(content);
}
/**
* Thread-local DateFormat for old-style cookies
*/
private static final ThreadLocal<DateFormat> EXPIRY_FORMAT_LOACAL = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
SimpleDateFormat format = new SimpleDateFormat(
"EEE, dd-MMM-yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone(
HttpCodecUtils.DEFAULT_TIME_ZONE_NAME));
return format;
}
};
/**
* A date long long ago, formatted in the old style cookie expire format
*/
private static final String EXPIRED_DATE = getFormattedExpiry(0);
private static String getFormattedExpiry(long time) {
DateFormat format = EXPIRY_FORMAT_LOACAL.get();
return format.format(new Date(time));
}
public void normalize() {
// Encode parameters.
Map<String, List<String>> params = getParameters();
if (!params.isEmpty()) {
try {
boolean first = true;
StringBuilder buf = new StringBuilder();
for (Map.Entry<String, List<String>> e: params.entrySet()) {
if (e.getValue().isEmpty()) {
continue;
}
for (String v: e.getValue()) {
if (!first) {
buf.append('&');
}
buf.append(URLEncoder.encode(
e.getKey(), HttpCodecUtils.DEFAULT_CHARSET_NAME));
buf.append('=');
buf.append(URLEncoder.encode(
v, HttpCodecUtils.DEFAULT_CHARSET_NAME));
first = false;
}
}
if (buf.length() > 0) {
String uri = getRequestUri().toString();
int queryIndex = uri.indexOf('?');
switch (getMethod()) {
case POST:
if (queryIndex >= 0) {
setRequestUri(new URI(uri.substring(0, queryIndex)));
}
IoBuffer content = IoBuffer.allocate(buf.length());
content.put(buf.toString().getBytes(HttpCodecUtils.US_ASCII_CHARSET_NAME));
content.flip();
setContent(content);
setHeader(
HttpHeaderConstants.KEY_CONTENT_TYPE,
HttpHeaderConstants.VALUE_URLENCODED_FORM);
break;
default:
if (queryIndex >= 0) {
setRequestUri(new URI(
uri.substring(0, queryIndex + 1) + buf));
} else {
setRequestUri(new URI(
uri + '?' + buf));
}
}
}
} catch (Exception e) {
throw (InternalError) new InternalError(
"Unexpected exception.").initCause(e);
}
}
// Encode Cookies
Set<Cookie> cookies = getCookies();
if (!cookies.isEmpty()) {
// Clear previous values.
removeHeader(HttpHeaderConstants.KEY_COOKIE);
// And encode.
for (Cookie c: cookies) {
StringBuilder buf = new StringBuilder();
buf.append(c.getName());
buf.append('=');
buf.append(c.getValue());
if (c.getVersion() > 0) {
buf.append("; version=");
buf.append(c.getVersion());
}
if (c.getPath() != null) {
buf.append("; path=");
buf.append(c.getPath());
}
long expiry = c.getMaxAge();
int version = c.getVersion();
if (expiry >= 0) {
if (version == 0) {
String expires = expiry == 0 ? EXPIRED_DATE
: getFormattedExpiry(System.currentTimeMillis()
+ 1000 * expiry);
buf.append("; Expires=");
buf.append(expires);
} else {
buf.append("; max-age=");
buf.append(c.getMaxAge());
}
}
if (c.isSecure()) {
buf.append("; secure");
}
if (c.isHttpOnly()) {
buf.append("; HTTPOnly");
}
buf.append(';');
addHeader(HttpHeaderConstants.KEY_SET_COOKIE, buf.toString());
}
}
// Add the Host header.
if (!containsHeader(HttpHeaderConstants.KEY_HOST)) {
URI uri = getRequestUri();
String host = uri.getHost();
if (host != null) {
if ((uri.getScheme().equalsIgnoreCase("http") &&
uri.getPort() != 80 && uri.getPort() > 0) ||
(uri.getScheme().equalsIgnoreCase("https") &&
uri.getPort() != 443 && uri.getPort() > 0)) {
setHeader(HttpHeaderConstants.KEY_HOST, host + ':' + uri.getPort());
} else {
setHeader(HttpHeaderConstants.KEY_HOST, host);
}
}
}
// Set Content-Length.
if (!containsHeader(HttpHeaderConstants.KEY_TRANSFER_CODING)) {
IoBuffer content = getContent();
int contentLength = content == null? 0 : content.remaining();
setHeader(
HttpHeaderConstants.KEY_CONTENT_LENGTH,
String.valueOf(contentLength));
}
}
}