/*
* JLibs: Common Utilities for Java
* Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package jlibs.nio.http.msg;
import jlibs.core.io.IOUtil;
import jlibs.nio.http.expr.UnresolvedException;
import jlibs.nio.http.util.*;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.function.Function;
import static jlibs.nio.http.util.USAscii.*;
/**
* @author Santhosh Kumar Tekuri
*/
public class Request extends Message{
public Method method = Method.GET;
public String uri = "/";
@Override
public void putLineInto(ByteBuffer buffer){
method.putInto(buffer);
USAscii.append(buffer, uri);
buffer.put(SP);
version.putInto(buffer);
buffer.put(CR);
buffer.put(LF);
}
@Override
public Status badMessageStatus(){
return Status.BAD_REQUEST;
}
@Override
public Status timeoutStatus(){
return Status.REQUEST_TIMEOUT;
}
@Override
public String toString(){
StringBuilder builder = new StringBuilder();
builder.append(method.name).append(' ')
.append(uri).append(' ')
.append(version)
.append("\r\n")
.append(headers);
return builder.toString();
}
/*-------------------------------------------------[ Bean ]---------------------------------------------------*/
@Override
@SuppressWarnings("StringEquality")
public Object getField(String name) throws UnresolvedException{
if(name=="method")
return method==null ? null : method.toString();
else if(name=="uri")
return uri;
else if(name=="line")
return method+" "+uri+' '+version;
else if(name=="query_string"){
if(uri==null)
return null;
int question = uri.indexOf('?');
return question==-1 ? "" : uri.substring(question+1);
}else if(name=="cookies")
return getCookies();
else
return super.getField(name);
}
/*-------------------------------------------------[ Host ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.23
public static final AsciiString HOST = new AsciiString("Host");
public HostPort getHost(){
return headers.getSingleValue(HOST, HostPort::valueOf);
}
public void setHost(HostPort host){
headers.setSingleValue(HOST, host, null);
}
/*-------------------------------------------------[ Accept-Encoding ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
public static final AsciiString ACCEPT_ENCODING = new AsciiString("Accept-Encoding");
public List<Encoding> getAcceptEncodings(){
return headers.getListValue(ACCEPT_ENCODING, encodingDelegate, true);
}
public void setAcceptEncodings(Collection<Encoding> encodings){
headers.setListValue(ACCEPT_ENCODING, encodings, null, true);
}
/*-------------------------------------------------[ X-Forwarded-For ]---------------------------------------------------*/
// http://en.wikipedia.org/wiki/X-Forwarded-For
public static final AsciiString X_FORWARDED_FOR = new AsciiString("X-Forwarded-For");
public List<String> getXForwardedFor(){
return headers.getListValue(X_FORWARDED_FOR, Parser.LVALUE_FUNCTION, true);
}
public void setXForwardedFor(Collection<String> clients){
headers.setListValue(X_FORWARDED_FOR, clients, null, true);
}
/*-------------------------------------------------[ Expect ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.20
public static final AsciiString EXPECT = new AsciiString("Expect");
public Expect getExpectation(){
return headers.getSingleValue(EXPECT, Expect::valueOf);
}
public void setExpectation(Expect expect){
headers.setSingleValue(EXPECT, expect, null);
}
/*-------------------------------------------------[ Authorization ]---------------------------------------------------*/
public static final AsciiString AUTHORIZATION = new AsciiString("Authorization");
public Credentials getCredentials(){
return Credentials.parse(headers.value(AUTHORIZATION));
}
public void setCredentials(Credentials credentials){
headers.setSingleValue(AUTHORIZATION, credentials, null);
}
/*-------------------------------------------------[ Proxy-Authorization ]---------------------------------------------------*/
public static final AsciiString PROXY_AUTHORIZATION = new AsciiString("Proxy-Authorization");
public Credentials getProxyCredentials(){
return Credentials.parse(headers.value(PROXY_AUTHORIZATION));
}
public void setProxyCredentials(Credentials credentials){
headers.setSingleValue(PROXY_AUTHORIZATION, credentials, null);
}
/*-------------------------------------------------[ Credentials ]---------------------------------------------------*/
public Credentials getCredentials(boolean proxy){
return proxy ? getProxyCredentials() : getCredentials();
}
public void setCredentials(Credentials credentials, boolean proxy){
if(proxy)
setProxyCredentials(credentials);
else
setCredentials(credentials);
}
/*-------------------------------------------------[ Accept ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
public static final AsciiString ACCEPT = new AsciiString("Accept");
private static final Function<Parser, QualityItem<MediaType>> ACCEPT_PARSER = parser -> {
String name = parser.lvalue();
int slash = name.indexOf('/');
String type = name.substring(0, slash);
String subType = name.substring(slash+1);
parser.rvalue();
double quality = 1;
Map<String, String> params = null;
while(true){
String paramName = parser.lvalue();
if(paramName==null)
break;
if(QualityItem.QUALITY.equals(paramName))
quality = Double.parseDouble(parser.rvalue());
else{
if(params==null)
params = new HashMap<>();
params.put(paramName, parser.rvalue());
}
}
return new QualityItem<>(new MediaType(type, subType, params), quality);
};
private static final Comparator<QualityItem<MediaType>> ACCEPT_COMPARATOR = new Comparator<QualityItem<MediaType>>(){
private int score(MediaType mt){
if("*".equals(mt.type))
return 0;
if("*".equals(mt.subType))
return 1;
else
return 2;
}
@Override
public int compare(QualityItem<MediaType> o1, QualityItem<MediaType> o2){
int score1 = score(o1.item);
int score2 = score(o2.item);
if(score1==score2){
if(o1.item.type.equals(o2.item.type) && o1.item.subType.equals(o2.item.subType)){
int paramScore1 = o1.item.params.size();
int paramScore2 = o2.item.params.size();
if(paramScore1==paramScore2)
return Double.compare(o2.quality, o1.quality);
else
return Integer.compare(paramScore2, paramScore1);
}else
return -1;
}else
return Integer.compare(score2, score1);
}
};
public List<QualityItem<MediaType>> getAcceptableMediaTypes(){
List<QualityItem<MediaType>> list = headers.getListValue(ACCEPT, ACCEPT_PARSER, true);
list.sort(ACCEPT_COMPARATOR);
return list;
}
public static double getQuality(MediaType mt, List<QualityItem<MediaType>> accept){
for(QualityItem<MediaType> qualityItem: accept){
if(mt.isCompatible(qualityItem.item)){
boolean paramsMatched = true;
for(Map.Entry<String, String> param: qualityItem.item.params.entrySet()){
String value = mt.params.get(param.getKey());
if(!param.getValue().equals(value)){
paramsMatched = false;
break;
}
}
if(paramsMatched)
return qualityItem.quality;
}
}
return 0;
}
public MediaType getAcceptableMediaType(Iterable<MediaType> mediaTypes){
List<QualityItem<MediaType>> acceptable = getAcceptableMediaTypes();
for(MediaType mediaType: mediaTypes){
if(getQuality(mediaType, acceptable)>0)
return mediaType;
}
return null;
}
public void setAcceptableMediaTypes(Collection<QualityItem<MediaType>> acceptable){
headers.setListValue(ACCEPT, acceptable, null, true);
}
/*-------------------------------------------------[ Accept-Charset ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.2
public static final AsciiString ACCEPT_CHARSET = new AsciiString("Accept-Charset");
private static final Function<Parser, QualityItem<String>> QUALITY_ITEM_PARSER = parser -> {
String item = parser.lvalue();
parser.rvalue();
double quality = 1;
while(true){
String paramName = parser.lvalue();
if(paramName==null)
break;
if(QualityItem.QUALITY.equals(paramName))
quality = Double.parseDouble(parser.rvalue());
}
return new QualityItem<>(item, quality);
};
public List<QualityItem<String>> getAcceptableCharsets(){
return headers.getListValue(ACCEPT_CHARSET, QUALITY_ITEM_PARSER, true);
}
public static double getCharsetQuality(String charset, List<QualityItem<String>> acceptableCharsets){
if(acceptableCharsets==null || acceptableCharsets.isEmpty())
return 1.0;
double defaultQuality = 0;
for(QualityItem<String> qualityItem: acceptableCharsets){
if("*".equals(qualityItem.item))
defaultQuality = qualityItem.quality;
else if(qualityItem.item.equalsIgnoreCase(charset))
return qualityItem.quality;
}
return IOUtil.ISO_8859_1.name().equalsIgnoreCase(charset) ? 1 : defaultQuality;
}
public String getAcceptableCharset(Iterable<String> charsets){
List<QualityItem<String>> acceptable = getAcceptableCharsets();
for(String charset: charsets){
if(getCharsetQuality(charset, acceptable)>0)
return charset;
}
return null;
}
public void setAcceptableCharsets(Collection<QualityItem<String>> acceptable){
headers.setListValue(ACCEPT_CHARSET, acceptable, null, true);
}
/*-------------------------------------------------[ If-Modified-Since ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
public static final AsciiString IF_MODIFIED_SINCE = new AsciiString("If-Modified-Since");
public Date getIfModifiedSince(){
return headers.getSingleValue(IF_MODIFIED_SINCE, HTTPDate.getInstance()::parse);
}
public void setIfModifiedSince(Date date){
headers.setSingleValue(IF_MODIFIED_SINCE, date, HTTPDate.getInstance()::format);
}
/*-------------------------------------------------[ If-Unmodified-Since ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.28
public static final AsciiString IF_UNMODIFIED_SINCE = new AsciiString("If-Unmodified-Since");
public Date getIfUnmodifiedSince(){
return headers.getSingleValue(IF_UNMODIFIED_SINCE, HTTPDate.getInstance()::parse);
}
public void setIfUnmodifiedSince(Date date){
headers.setSingleValue(IF_UNMODIFIED_SINCE, date, HTTPDate.getInstance()::format);
}
/*-------------------------------------------------[ SOAPAction ]---------------------------------------------------*/
// http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528
public static final AsciiString SOAP_ACTION = new AsciiString("SOAPAction");
public String getSOAPAction(){
return headers.getSingleValue(SOAP_ACTION, String::valueOf);
}
public void setSOAPAction(String soapAction){
headers.set(SOAP_ACTION, soapAction);
}
/*-------------------------------------------------[ Referer ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.36
public static final AsciiString REFERER = new AsciiString("Referer");
public String getReferer(){
return headers.getSingleValue(REFERER, String::valueOf);
}
public void setReferer(String referer){
headers.set(REFERER, referer);
}
/*-------------------------------------------------[ Cookie ]---------------------------------------------------*/
// http://tools.ietf.org/html/rfc6265#section-4.2
public static final AsciiString COOKIE = new AsciiString("Cookie");
public Map<String, Cookie> getCookies(){
return headers.getMapValue(COOKIE, Cookie::new, cookie -> cookie.name, true);
}
public void setCookies(Collection<Cookie> cookies){
headers.setListValue(COOKIE, cookies, null, true);
}
/*-------------------------------------------------[ User-Agent ]---------------------------------------------------*/
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
public static final AsciiString USER_AGENT = new AsciiString("User-Agent");
public String getUserAgent(){
return headers.getSingleValue(USER_AGENT, String::valueOf);
}
public void setUserAgent(String userAgent){
headers.set(USER_AGENT, userAgent);
}
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
public static final AsciiString ACCEPT_LANGUAGE = new AsciiString("Accept-Language");
/*-------------------------------------------------[ Origin ]---------------------------------------------------*/
// http://tools.ietf.org/html/rfc6454#section-7
public static final AsciiString ORIGIN = new AsciiString("Origin");
public Origins getOrigins(){
return headers.getSingleValue(ORIGIN, Origins::valueOf);
}
public void setOrigins(Origins origins){
headers.setSingleValue(ORIGIN, origins, null);
}
/*-------------------------------------------------[ Access-Control-Request-Method ]---------------------------------------------------*/
// http://www.w3.org/TR/cors/#access-control-request-method-request-header
public static final AsciiString ACCESS_CONTROL_REQUEST_METHOD = new AsciiString("Access-Control-Request-Method");
public Method getAccessControlRequestMethod(){
return headers.getSingleValue(ACCESS_CONTROL_REQUEST_METHOD, Method::valueOf);
}
public void setAccessControlRequestMethod(Method method){
headers.setSingleValue(ACCESS_CONTROL_REQUEST_METHOD, method, null);
}
}