package com.limegroup.gnutella.http; import java.io.IOException; import java.net.URLEncoder; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.apache.http.Header; import org.apache.http.HttpResponse; import org.limewire.util.StringUtils; /** * This class supplies general facilities for handling HTTP, such as writing * headers, extracting header values, etc.. */ public final class HTTPUtils { /** * Constant for the carriage-return linefeed sequence that marks the end of * an HTTP header */ private static final String CRLF = "\r\n"; /** * Cached colon followed by a space to avoid excessive allocations. */ private static final String COLON_SPACE = ": "; /** * Cached colon to avoid excessive allocations. */ public static final String COLON = ":"; /** * Cached slash to avoid excessive allocations. */ private static final String SLASH = "/"; /** * Private constructor to ensure that this class cannot be constructed */ private HTTPUtils() { } public static String createHeader(String name, String value) { StringBuilder sb = new StringBuilder(name.length() + value.length() + 4); return sb.append(name).append(COLON_SPACE).append(value).append(CRLF).toString(); } /** * Utility method for extracting the version from a feature token. */ public static float parseFeatureToken(String token) throws ProblemReadingHeaderException { int slashIndex = token.indexOf(SLASH); if (slashIndex == -1 || slashIndex >= token.length() - 1) throw new ProblemReadingHeaderException("invalid feature token"); String versionS = token.substring(slashIndex + 1); try { return Float.parseFloat(versionS); } catch (NumberFormatException bad) { throw new ProblemReadingHeaderException(bad); } } /** Utility for extracting a value from a K=V string. */ public static String parseValue(String keyValuePair) throws IOException { int equalIndex = keyValuePair.indexOf("="); if (equalIndex == -1 || equalIndex >= keyValuePair.length() - 1) throw new IOException("invalid keyValuePair: " + keyValuePair); return keyValuePair.substring(equalIndex + 1).trim(); } /** * Utility method for getting the date value for the "Date" header in * standard format. * * @return the current date as a standardized date string -- see RFC 2616 * section 14.18 */ public static String getDateValue() { DateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss z", Locale.US); df.setTimeZone(TimeZone.getTimeZone("GMT")); return df.format(new Date()); } /** * Encodes a name using URLEncoder, using %20 instead of + for spaces. */ public static String encode(String name, String encoding) throws IOException { return StringUtils.replace(URLEncoder.encode(name, encoding), "+", "%20"); } /** * Returns the offset at which content starts in the HttpResponse, * based on the 'Content-Range' header. */ public static long getStartPoint(HttpResponse response) throws IOException { // LOG.debugf("Looking at response of {0}, {1}", response.getStatusLine(), Arrays.asList(response.getAllHeaders())); Header header = response.getFirstHeader(HTTPHeaderName.CONTENT_RANGE.httpStringValue()); if(header == null) { return 0; } else { // Content-Range = "Content-Range" ":" content-range-spec // // content-range-spec = byte-content-range-spec // byte-content-range-spec = bytes-unit SP // byte-range-resp-spec "/" // ( instance-length | "*" ) // // byte-range-resp-spec = (first-byte-pos "-" last-byte-pos) // | "*" // instance-length = 1*DIGIT String value = header.getValue(); // Skip past the 'bytes ' prefix. if(!value.startsWith("bytes ")) { throw new IOException("invalid content range: " + value); } value = value.substring("bytes ".length()).trim(); if(value.startsWith("*")) { // They're returning everything! return 0; } else { // There's got to be a dash in there, we want before the dash. int dashIdx = value.indexOf('-'); if(dashIdx < 0) { throw new IOException("invalid content range: " + value); } else { value = value.substring(0, dashIdx).trim(); try { long start = Long.parseLong(value); if(start < 0) { throw new IOException("invalid start: " + start); } return start; } catch(NumberFormatException nfe) { throw new IOException("invalid content range", nfe); } } } } } }