//
// Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s).
// All rights reserved.
//
package openadk.library.tools;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import openadk.library.AgentProperties;
import openadk.library.TransportProperties;
/**
* This class shall contain stateless, static helper methods related to HTTP and may also include final static constants or similar resources.
*
* @author ujensza
*/
public class HTTPUtil {
public static class QualifiedToken implements Comparable<QualifiedToken> {
private String token;
private BigDecimal quality = BigDecimal.ONE;
private String repr;
public QualifiedToken(String _token, String _quality) throws NumberFormatException {
token = _token;
if (_quality != null) quality = new BigDecimal(_quality);
}
public QualifiedToken(String _token, BigDecimal _quality) {
token = _token;
quality = _quality;
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof QualifiedToken)) return false;
QualifiedToken qt = (QualifiedToken)obj;
return token.equals(qt.token);
}
@Override
public int hashCode() {
return token.hashCode();
}
public String getToken() {
return token;
}
@Override
public String toString() {
if (repr == null) {
StringBuilder sb = new StringBuilder();
sb.append(token);
if (quality != null) {
sb.append(";q=");
NumberFormat numf = NumberFormat.getNumberInstance(Locale.US);
numf.setMinimumIntegerDigits(0);
numf.setMaximumFractionDigits(1);
numf.setMinimumFractionDigits(1);
sb.append(numf.format(quality));
}
}
return repr;
}
public int compareTo(QualifiedToken o) {
return -1 * quality.compareTo(o.quality);
}
}
/**
* Parses the acceptEncoding string into a list of QualifiedToken, sorts, then returns just the token strings.
* @param acceptEncoding
* @return list of token strings sorted by priority based on quality specified in the header
*/
public static List<String> derivePreferredCodingFrom(String acceptEncoding) {
if (acceptEncoding == null) return Collections.emptyList();
acceptEncoding = acceptEncoding.trim();
List<QualifiedToken> tokens = getSortedQualifiedTokensFrom(acceptEncoding);
List<String> results = new ArrayList<String>();
for (QualifiedToken token : tokens) {
results.add(token.getToken());
}
return results;
}
public static Map<String, String> parseHTTPHeaderParams(String params) {
params = params.trim();
if (params.length() < 1) return Collections.emptyMap();
Map<String, String> results = new HashMap<String, String>();
for (String param : params.split(";")) {
param = param.trim();
if (param.length() < 2) continue;
int equalIndex = param.indexOf('=');
String key = param.substring(0, equalIndex).trim();
String val = param.substring(equalIndex+1).trim();
results.put(key, val);
}
return results;
}
public static List<QualifiedToken> getSortedQualifiedTokensFrom(String tokenString) {
if (tokenString == null) return Collections.singletonList(new QualifiedToken("identity", (String)null));
boolean foundNonZeroAsterisk = false;
boolean foundIdentity = false;
String asteriskQuality = null;
List<QualifiedToken> results = new ArrayList<QualifiedToken>();
String[] commaDelimitedTokens = tokenString.split(",");
for (String fullToken : commaDelimitedTokens) {
fullToken = fullToken.trim();
String token;
String quality = null;
if (fullToken.length() > 0) {
int splitIndex = fullToken.indexOf(';');
if (splitIndex > -1) {
token = fullToken.substring(0, splitIndex);
String rawParams = fullToken.substring(splitIndex+1);
Map<String, String> params = parseHTTPHeaderParams(rawParams);
quality = params.get("q");
} else {
token = fullToken;
}
if (quality == null) quality = "1.0";
boolean zeroQ = quality.trim().matches("[\\.0]*");
if (!"*".equals(token)) {
if ("identity".equals(token)) foundIdentity = true;
results.add(new QualifiedToken(token, quality));
} else if (!zeroQ) {
foundNonZeroAsterisk = true;
asteriskQuality = quality;
}
}
}
if (foundNonZeroAsterisk && !foundIdentity) {
results.add(new QualifiedToken("identity", asteriskQuality));
}
if (results.size() < 1) {
results.add(new QualifiedToken("identity", (String)null));
}
Collections.sort(results);
if (foundNonZeroAsterisk) {
results.add(new QualifiedToken("*", asteriskQuality));
}
return results;
}
/**
* This ensures the proper timeouts are set on the connection.
*
* It will:
* 1. Try to use the agent.cfg value if they specified it.
* 2. Fall back to using the system property if they specified it
* 3. Fall back on a default value
*
*
* @param properties - the transport properties from which this function will read the timeout properties
* @param conn - the URLConnection on which to set the properties
*/
public static void setTimeoutsOnConnection(TransportProperties properties, HttpURLConnection conn) {
Object readTimeout = properties.get(AgentProperties.PROP_PULL_MODE_READ_TIMEOUT);
Object connectTimeout = properties.get(AgentProperties.PROP_PULL_MODE_CONNECT_TIMEOUT);
Properties systemProperties = System.getProperties();
if ( readTimeout != null ) { //try agent.cfg
int readTimeoutValue = Integer.parseInt(String.valueOf(readTimeout));
conn.setReadTimeout(readTimeoutValue);
} else if ( systemProperties.containsKey("sun.net.client.defaultReadTimeout") ) { //try system properties
//use system property (happens automatically)
} else { //use default
conn.setReadTimeout(AgentProperties.DEFAULT_PULL_READ_TIMEOUT);
}
if ( connectTimeout != null ) { //try agent.cfg
int connectTimeoutValue = Integer.parseInt(String.valueOf(connectTimeout));
conn.setConnectTimeout(connectTimeoutValue);
} else if (systemProperties.containsKey("sun.net.client.defaultConnectTimeout")) { //try system properties
//use system property (happens automatically)
} else { //use default
conn.setConnectTimeout(AgentProperties.DEFAULT_PULL_CONNECT_TIMEOUT);
}
}
public static void main(String... argv) {
String[] testInputs = new String[] {
"gzip",
"",
"*",
"gzip;q=1.0",
"gzip;q=0.5, identity;q=0.7, *;q=0.0",
"*; q= 0.0, compress",
"gzip;q=1.0, *;q=0.0",
"gzip;q=1.0, identity;q=0.5, *;q=0.1"
};
for (String test : testInputs) {
System.out.printf("%s :: %s\n", test, derivePreferredCodingFrom(test));
}
}
}