From 4831a8dc6577f762e0f21fd15dd9700fe824740d Mon Sep 17 00:00:00 2001 From: jwilson Date: Tue, 31 Dec 2013 01:57:47 -0500 Subject: [PATCH] Rename RawHeaders to Headers. Now that Request and Response self-describe, I think the word Headers is sufficient. And it's a lot less ugly than RawHeaders. Also move header serialization code to HttpTransport and SpdyTransport. --- .../com/squareup/okhttp/internal/Util.java | 12 ++-- .../java/com/squareup/okhttp/Connection.java | 2 +- .../squareup/okhttp/HttpResponseCache.java | 16 ++--- .../java/com/squareup/okhttp/Request.java | 18 ++--- .../java/com/squareup/okhttp/Response.java | 26 +++---- .../http/{RawHeaders.java => Headers.java} | 68 ++----------------- .../internal/http/HttpAuthenticator.java | 4 +- .../okhttp/internal/http/HttpEngine.java | 39 +++-------- .../okhttp/internal/http/HttpTransport.java | 28 ++++++-- .../internal/http/HttpURLConnectionImpl.java | 18 ++--- .../internal/http/HttpsURLConnectionImpl.java | 2 +- .../okhttp/internal/http/SpdyTransport.java | 53 +++++++++++++-- .../{RawHeadersTest.java => HeadersTest.java} | 40 +++++------ 13 files changed, 149 insertions(+), 177 deletions(-) rename okhttp/src/main/java/com/squareup/okhttp/internal/http/{RawHeaders.java => Headers.java} (79%) rename okhttp/src/test/java/com/squareup/okhttp/internal/http/{RawHeadersTest.java => HeadersTest.java} (63%) diff --git a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java index dbe84d140..f2adbf58f 100644 --- a/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java +++ b/okhttp-protocols/src/main/java/com/squareup/okhttp/internal/Util.java @@ -72,14 +72,10 @@ public final class Util { return specifiedPort != -1 ? specifiedPort : getDefaultPort(scheme); } - public static int getDefaultPort(String scheme) { - if ("http".equalsIgnoreCase(scheme)) { - return 80; - } else if ("https".equalsIgnoreCase(scheme)) { - return 443; - } else { - return -1; - } + public static int getDefaultPort(String protocol) { + if ("http".equals(protocol)) return 80; + if ("https".equals(protocol)) return 443; + return -1; } public static void checkOffsetAndCount(int arrayLength, int offset, int count) { diff --git a/okhttp/src/main/java/com/squareup/okhttp/Connection.java b/okhttp/src/main/java/com/squareup/okhttp/Connection.java index 121a78d96..8f585628e 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Connection.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Connection.java @@ -310,7 +310,7 @@ public final class Connection implements Closeable { Request request = tunnelRequest.getRequest(); String requestLine = tunnelRequest.requestLine(); while (true) { - out.write(request.rawHeaders().toBytes(requestLine)); + HttpTransport.writeRequest(out, request.headers(), requestLine); Response response = HttpTransport.readResponse(request, in).build(); switch (response.code()) { diff --git a/okhttp/src/main/java/com/squareup/okhttp/HttpResponseCache.java b/okhttp/src/main/java/com/squareup/okhttp/HttpResponseCache.java index bb39385e5..ebed3e5cf 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/HttpResponseCache.java +++ b/okhttp/src/main/java/com/squareup/okhttp/HttpResponseCache.java @@ -20,7 +20,7 @@ import com.squareup.okhttp.internal.Base64; import com.squareup.okhttp.internal.DiskLruCache; import com.squareup.okhttp.internal.StrictLineReader; import com.squareup.okhttp.internal.Util; -import com.squareup.okhttp.internal.http.RawHeaders; +import com.squareup.okhttp.internal.http.Headers; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.File; @@ -356,10 +356,10 @@ public final class HttpResponseCache extends ResponseCache implements OkResponse private static final class Entry { private final String url; - private final RawHeaders varyHeaders; + private final Headers varyHeaders; private final String requestMethod; private final String statusLine; - private final RawHeaders responseHeaders; + private final Headers responseHeaders; private final Handshake handshake; /** @@ -416,7 +416,7 @@ public final class HttpResponseCache extends ResponseCache implements OkResponse StrictLineReader reader = new StrictLineReader(in, US_ASCII); url = reader.readLine(); requestMethod = reader.readLine(); - RawHeaders.Builder varyHeadersBuilder = new RawHeaders.Builder(); + Headers.Builder varyHeadersBuilder = new Headers.Builder(); int varyRequestHeaderLineCount = reader.readInt(); for (int i = 0; i < varyRequestHeaderLineCount; i++) { varyHeadersBuilder.addLine(reader.readLine()); @@ -424,7 +424,7 @@ public final class HttpResponseCache extends ResponseCache implements OkResponse varyHeaders = varyHeadersBuilder.build(); statusLine = reader.readLine(); - RawHeaders.Builder responseHeadersBuilder = new RawHeaders.Builder(); + Headers.Builder responseHeadersBuilder = new Headers.Builder(); int responseHeaderLineCount = reader.readInt(); for (int i = 0; i < responseHeaderLineCount; i++) { responseHeadersBuilder.addLine(reader.readLine()); @@ -450,10 +450,10 @@ public final class HttpResponseCache extends ResponseCache implements OkResponse public Entry(Response response) { this.url = response.request().urlString(); - this.varyHeaders = response.request().rawHeaders().getAll(response.getVaryFields()); + this.varyHeaders = response.request().headers().getAll(response.getVaryFields()); this.requestMethod = response.request().method(); this.statusLine = response.statusLine(); - this.responseHeaders = response.rawHeaders(); + this.responseHeaders = response.headers(); this.handshake = response.handshake(); } @@ -529,7 +529,7 @@ public final class HttpResponseCache extends ResponseCache implements OkResponse String contentLength = responseHeaders.get("Content-Length"); return new Response.Builder(request) .statusLine(statusLine) - .rawHeaders(responseHeaders) + .headers(responseHeaders) .body(new CacheResponseBody(snapshot, contentType, contentLength)) .handshake(handshake) .build(); diff --git a/okhttp/src/main/java/com/squareup/okhttp/Request.java b/okhttp/src/main/java/com/squareup/okhttp/Request.java index 57d0d0f67..7cd16170f 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Request.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Request.java @@ -18,8 +18,8 @@ package com.squareup.okhttp; import com.squareup.okhttp.internal.Platform; import com.squareup.okhttp.internal.Util; import com.squareup.okhttp.internal.http.HeaderParser; +import com.squareup.okhttp.internal.http.Headers; import com.squareup.okhttp.internal.http.HttpDate; -import com.squareup.okhttp.internal.http.RawHeaders; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -45,7 +45,7 @@ import java.util.Set; public final class Request { private final URL url; private final String method; - private final RawHeaders headers; + private final Headers headers; private final Body body; private final Object tag; @@ -93,7 +93,7 @@ public final class Request { return headers.names(); } - RawHeaders rawHeaders() { + Headers headers() { return headers; } @@ -120,7 +120,7 @@ public final class Request { public Builder newBuilder() { return new Builder(url) .method(method, body) - .rawHeaders(headers) + .headers(headers) .tag(tag); } @@ -132,7 +132,7 @@ public final class Request { return "close".equalsIgnoreCase(parsedHeaders().connection); } - public RawHeaders getHeaders() { + public Headers getHeaders() { return headers; } @@ -255,7 +255,7 @@ public final class Request { private String ifNoneMatch; private String proxyAuthorization; - public ParsedHeaders(RawHeaders headers) { + public ParsedHeaders(Headers headers) { HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() { @Override public void handle(String directive, String parameter) { if ("no-cache".equalsIgnoreCase(directive)) { @@ -398,7 +398,7 @@ public final class Request { public static class Builder { private URL url; private String method = "GET"; - private RawHeaders.Builder headers = new RawHeaders.Builder(); + private Headers.Builder headers = new Headers.Builder(); private Body body; private Object tag; @@ -444,8 +444,8 @@ public final class Request { } // TODO: this shouldn't be public. - public Builder rawHeaders(RawHeaders rawHeaders) { - headers = rawHeaders.newBuilder(); + public Builder headers(Headers headers) { + this.headers = headers.newBuilder(); return this; } diff --git a/okhttp/src/main/java/com/squareup/okhttp/Response.java b/okhttp/src/main/java/com/squareup/okhttp/Response.java index e8cb46de9..36b444c01 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/Response.java +++ b/okhttp/src/main/java/com/squareup/okhttp/Response.java @@ -18,8 +18,8 @@ package com.squareup.okhttp; import com.squareup.okhttp.internal.Platform; import com.squareup.okhttp.internal.Util; import com.squareup.okhttp.internal.http.HeaderParser; +import com.squareup.okhttp.internal.http.Headers; import com.squareup.okhttp.internal.http.HttpDate; -import com.squareup.okhttp.internal.http.RawHeaders; import com.squareup.okhttp.internal.http.StatusLine; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -65,7 +65,7 @@ public final class Response { private final Request request; private final StatusLine statusLine; private final Handshake handshake; - private final RawHeaders headers; + private final Headers headers; private final Body body; private final Response redirectedBy; @@ -146,7 +146,7 @@ public final class Response { } // TODO: this shouldn't be public. - public RawHeaders rawHeaders() { + public Headers headers() { return headers; } @@ -162,7 +162,7 @@ public final class Response { return new Builder(request) .statusLine(statusLine) .handshake(handshake) - .rawHeaders(headers) + .headers(headers) .body(body) .redirectedBy(redirectedBy); } @@ -189,10 +189,6 @@ public final class Response { return "close".equalsIgnoreCase(parsedHeaders().connection); } - public RawHeaders getHeaders() { - return headers; - } - public Date getServedDate() { return parsedHeaders().servedDate; } @@ -267,7 +263,7 @@ public final class Response { * Returns true if none of the Vary headers on this response have changed * between {@code cachedRequest} and {@code newRequest}. */ - public boolean varyMatches(RawHeaders varyHeaders, Request newRequest) { + public boolean varyMatches(Headers varyHeaders, Request newRequest) { for (String field : parsedHeaders().varyFields) { if (!equal(varyHeaders.values(field), newRequest.headers(field))) return false; } @@ -301,7 +297,7 @@ public final class Response { * 13.5.3. */ public Response combine(Response network) throws IOException { - RawHeaders.Builder result = new RawHeaders.Builder(); + Headers.Builder result = new Headers.Builder(); for (int i = 0; i < headers.length(); i++) { String fieldName = headers.getFieldName(i); @@ -321,7 +317,7 @@ public final class Response { } } - return newBuilder().rawHeaders(result.build()).build(); + return newBuilder().headers(result.build()).build(); } /** @@ -506,7 +502,7 @@ public final class Response { private String connection; private String contentType; - private ParsedHeaders(RawHeaders headers) { + private ParsedHeaders(Headers headers) { HeaderParser.CacheControlHandler handler = new HeaderParser.CacheControlHandler() { @Override public void handle(String directive, String parameter) { if ("no-cache".equalsIgnoreCase(directive)) { @@ -619,7 +615,7 @@ public final class Response { private final Request request; private StatusLine statusLine; private Handshake handshake; - private RawHeaders.Builder headers = new RawHeaders.Builder(); + private Headers.Builder headers = new Headers.Builder(); private Body body; private Response redirectedBy; @@ -666,8 +662,8 @@ public final class Response { } // TODO: this shouldn't be public. - public Builder rawHeaders(RawHeaders rawHeaders) { - headers = rawHeaders.newBuilder(); + public Builder headers(Headers headers) { + this.headers = headers.newBuilder(); return this; } diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/RawHeaders.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/Headers.java similarity index 79% rename from okhttp/src/main/java/com/squareup/okhttp/internal/http/RawHeaders.java rename to okhttp/src/main/java/com/squareup/okhttp/internal/http/Headers.java index 93cab49d6..de964cf87 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/RawHeaders.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/Headers.java @@ -20,13 +20,10 @@ package com.squareup.okhttp.internal.http; import com.squareup.okhttp.internal.Util; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -48,7 +45,7 @@ import java.util.TreeSet; *

This class trims whitespace from values. It never returns values with * leading or trailing whitespace. */ -public final class RawHeaders { +public final class Headers { private static final Comparator FIELD_NAME_COMPARATOR = new Comparator() { // @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ") @Override public int compare(String a, String b) { @@ -66,7 +63,7 @@ public final class RawHeaders { private final List namesAndValues; - private RawHeaders(Builder builder) { + private Headers(Builder builder) { this.namesAndValues = Util.immutableList(builder.namesAndValues); } @@ -122,7 +119,7 @@ public final class RawHeaders { } /** @param fieldNames a case-insensitive set of HTTP header field names. */ - public RawHeaders getAll(Set fieldNames) { + public Headers getAll(Set fieldNames) { Builder result = new Builder(); for (int i = 0; i < namesAndValues.size(); i += 2) { String fieldName = namesAndValues.get(i); @@ -133,20 +130,6 @@ public final class RawHeaders { return result.build(); } - /** Returns bytes of a request header for sending on an HTTP transport. */ - public byte[] toBytes(String requestLine) throws UnsupportedEncodingException { - StringBuilder result = new StringBuilder(256); - result.append(requestLine).append("\r\n"); - for (int i = 0; i < namesAndValues.size(); i += 2) { - result.append(namesAndValues.get(i)) - .append(": ") - .append(namesAndValues.get(i + 1)) - .append("\r\n"); - } - result.append("\r\n"); - return result.toString().getBytes("ISO-8859-1"); - } - /** * Returns an immutable map containing each field to its list of values. * @@ -173,45 +156,6 @@ public final class RawHeaders { return Collections.unmodifiableMap(result); } - /** - * Returns a list of alternating names and values. Names are all lower case. - * No names are repeated. If any name has multiple values, they are - * concatenated using "\0" as a delimiter. - */ - public List toNameValueBlock() { - Set names = new HashSet(); - List result = new ArrayList(); - for (int i = 0; i < namesAndValues.size(); i += 2) { - String name = namesAndValues.get(i).toLowerCase(Locale.US); - String value = namesAndValues.get(i + 1); - - // Drop headers that are forbidden when layering HTTP over SPDY. - if (name.equals("connection") - || name.equals("host") - || name.equals("keep-alive") - || name.equals("proxy-connection") - || name.equals("transfer-encoding")) { - continue; - } - - // If we haven't seen this name before, add the pair to the end of the list... - if (names.add(name)) { - result.add(name); - result.add(value); - continue; - } - - // ...otherwise concatenate the existing values and this value. - for (int j = 0; j < result.size(); j += 2) { - if (name.equals(result.get(j))) { - result.set(j + 1, result.get(j + 1) + "\0" + value); - break; - } - } - } - return result; - } - public Builder newBuilder() { Builder result = new Builder(); result.namesAndValues.addAll(namesAndValues); @@ -232,7 +176,7 @@ public final class RawHeaders { /** Equivalent to {@code build().get(fieldName)}, but potentially faster. */ public String get(String fieldName) { - return RawHeaders.get(namesAndValues, fieldName); + return Headers.get(namesAndValues, fieldName); } /** @@ -300,8 +244,8 @@ public final class RawHeaders { return this; } - public RawHeaders build() { - return new RawHeaders(this); + public Headers build() { + return new Headers(this); } } } diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpAuthenticator.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpAuthenticator.java index d8ffcba58..cc8d6a8b0 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpAuthenticator.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpAuthenticator.java @@ -102,7 +102,7 @@ public final class HttpAuthenticator { } else { throw new IllegalArgumentException(); // TODO: ProtocolException? } - List challenges = parseChallenges(response.rawHeaders(), responseField); + List challenges = parseChallenges(response.headers(), responseField); if (challenges.isEmpty()) return null; // Could not find a challenge so end the request cycle. Request request = response.request(); @@ -119,7 +119,7 @@ public final class HttpAuthenticator { * Parse RFC 2617 challenges. This API is only interested in the scheme * name and realm. */ - private static List parseChallenges(RawHeaders responseHeaders, + private static List parseChallenges(Headers responseHeaders, String challengeHeader) { // auth-scheme = token // auth-param = token "=" ( token | quoted-string ) diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java index 33b698e69..99d690da4 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpEngine.java @@ -19,7 +19,6 @@ package com.squareup.okhttp.internal.http; import com.squareup.okhttp.Address; import com.squareup.okhttp.Connection; -import com.squareup.okhttp.Handshake; import com.squareup.okhttp.MediaType; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkResponseCache; @@ -336,17 +335,8 @@ public class HttpEngine { return response; } - public final int getResponseCode() { - if (response == null) { - throw new IllegalStateException(); - } - return response.code(); - } - public final InputStream getResponseBody() { - if (response == null) { - throw new IllegalStateException(); - } + if (response == null) throw new IllegalStateException(); return responseBodyIn; } @@ -434,13 +424,12 @@ public class HttpEngine { * See RFC 2616 section 4.3. */ public final boolean hasResponseBody() { - int responseCode = response.code(); - // HEAD requests never yield a body regardless of the response headers. if (request.method().equals("HEAD")) { return false; } + int responseCode = response.code(); if ((responseCode < HTTP_CONTINUE || responseCode >= 200) && responseCode != HTTP_NO_CONTENT && responseCode != HTTP_NOT_MODIFIED) { @@ -471,7 +460,7 @@ public class HttpEngine { } if (request.getHost() == null) { - result.setHost(getOriginAddress(request.url())); + result.setHost(getHostHeader(request.url())); } if ((connection == null || connection.getHttpMinorVersion() != 0) @@ -496,26 +485,15 @@ public class HttpEngine { request = result.build(); } - /** - * Returns the TLS handshake created when this engine connected, or null if - * no TLS connection was made. - */ - public Handshake getHandshake() { - return response.handshake(); - } - public static String getDefaultUserAgent() { String agent = System.getProperty("http.agent"); return agent != null ? agent : ("Java" + System.getProperty("java.version")); } - public static String getOriginAddress(URL url) { - int port = url.getPort(); - String result = url.getHost(); - if (port > 0 && port != getDefaultPort(url.getProtocol())) { - result = result + ":" + port; - } - return result; + public static String getHostHeader(URL url) { + return getEffectivePort(url) != getDefaultPort(url.getProtocol()) + ? url.getHost() + ":" + url.getPort() + : url.getHost(); } /** @@ -524,6 +502,7 @@ public class HttpEngine { */ public final void readResponse() throws IOException { if (hasResponse()) { + // TODO: this doesn't make much sense. response = response.newBuilder().setResponseSource(responseSource).build(); return; } @@ -599,7 +578,7 @@ public class HttpEngine { request.getProxyAuthorization()); } - public void receiveHeaders(RawHeaders headers) throws IOException { + public void receiveHeaders(Headers headers) throws IOException { CookieHandler cookieHandler = client.getCookieHandler(); if (cookieHandler != null) { cookieHandler.put(request.uri(), headers.toMultimap(null)); diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpTransport.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpTransport.java index 6cdbaf512..64ed3c1ee 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpTransport.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpTransport.java @@ -135,12 +135,11 @@ public final class HttpTransport implements Transport { */ public void writeRequestHeaders() throws IOException { httpEngine.writingRequestHeaders(); - RawHeaders headersToSend = httpEngine.getRequest().getHeaders(); + Headers headersToSend = httpEngine.getRequest().getHeaders(); String requestLine = RequestLine.get(httpEngine.getRequest(), httpEngine.connection.getRoute().getProxy().type(), httpEngine.connection.getHttpMinorVersion()); - byte[] bytes = headersToSend.toBytes(requestLine); - requestOut.write(bytes); + writeRequest(requestOut, headersToSend, requestLine); } @Override public Response readResponseHeaders() throws IOException { @@ -148,10 +147,25 @@ public final class HttpTransport implements Transport { .handshake(httpEngine.connection.getHandshake()) .build(); httpEngine.connection.setHttpMinorVersion(response.httpMinorVersion()); - httpEngine.receiveHeaders(response.rawHeaders()); + httpEngine.receiveHeaders(response.headers()); return response; } + /** Returns bytes of a request header for sending on an HTTP transport. */ + public static void writeRequest(OutputStream out, Headers headers, String requestLine) + throws IOException { + StringBuilder result = new StringBuilder(256); + result.append(requestLine).append("\r\n"); + for (int i = 0; i < headers.length(); i ++) { + result.append(headers.getFieldName(i)) + .append(": ") + .append(headers.getValue(i)) + .append("\r\n"); + } + result.append("\r\n"); + out.write(result.toString().getBytes("ISO-8859-1")); + } + /** Parses bytes of a response header from an HTTP transport. */ public static Response.Builder readResponse(Request request, InputStream in) throws IOException { while (true) { @@ -162,9 +176,9 @@ public final class HttpTransport implements Transport { responseBuilder.statusLine(statusLine); responseBuilder.header(Response.SELECTED_TRANSPORT, "http/1.1"); - RawHeaders.Builder headersBuilder = new RawHeaders.Builder(); + Headers.Builder headersBuilder = new Headers.Builder(); headersBuilder.readHeaders(in); - responseBuilder.rawHeaders(headersBuilder.build()); + responseBuilder.headers(headersBuilder.build()); if (statusLine.code() != HTTP_CONTINUE) return responseBuilder; } @@ -498,7 +512,7 @@ public final class HttpTransport implements Transport { } if (bytesRemainingInChunk == 0) { hasMoreChunks = false; - RawHeaders trailers = new RawHeaders.Builder() + Headers trailers = new Headers.Builder() .readHeaders(transport.socketIn) .build(); httpEngine.receiveHeaders(trailers); diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java index a0de0eeaf..6b2565f05 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpURLConnectionImpl.java @@ -69,7 +69,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { final OkHttpClient client; - private RawHeaders.Builder requestHeaders = new RawHeaders.Builder(); + private Headers.Builder requestHeaders = new Headers.Builder(); /** Like the superclass field of the same name, but a long and available on all platforms. */ private long fixedContentLength = -1; @@ -113,7 +113,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { @Override public final InputStream getErrorStream() { try { HttpEngine response = getResponse(); - if (response.hasResponseBody() && response.getResponseCode() >= HTTP_BAD_REQUEST) { + if (response.hasResponseBody() && response.getResponse().code() >= HTTP_BAD_REQUEST) { return response.getResponseBody(); } return null; @@ -128,7 +128,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { */ @Override public final String getHeaderField(int position) { try { - return getResponse().getResponse().getHeaders().getValue(position); + return getResponse().getResponse().headers().getValue(position); } catch (IOException e) { return null; } @@ -142,7 +142,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { @Override public final String getHeaderField(String fieldName) { try { Response response = getResponse().getResponse(); - return fieldName == null ? response.statusLine() : response.getHeaders().get(fieldName); + return fieldName == null ? response.statusLine() : response.headers().get(fieldName); } catch (IOException e) { return null; } @@ -150,7 +150,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { @Override public final String getHeaderFieldKey(int position) { try { - return getResponse().getResponse().getHeaders().getFieldName(position); + return getResponse().getResponse().headers().getFieldName(position); } catch (IOException e) { return null; } @@ -159,7 +159,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { @Override public final Map> getHeaderFields() { try { Response response = getResponse().getResponse(); - return response.getHeaders().toMultimap(response.statusLine()); + return response.headers().toMultimap(response.statusLine()); } catch (IOException e) { return Collections.emptyMap(); } @@ -273,7 +273,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { RetryableOutputStream requestBody) throws IOException { Request request = new Request.Builder(getURL()) .method(method, null) // No body: that's provided later! - .rawHeaders(requestHeaders.build()) + .headers(requestHeaders.build()) .build(); // If we're currently not using caches, make sure the engine's client doesn't have one. @@ -315,7 +315,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { // Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM // redirect should keep the same method, Chrome, Firefox and the // RI all issue GETs when following any redirect. - int responseCode = httpEngine.getResponseCode(); + int responseCode = httpEngine.getResponse().code(); if (responseCode == HTTP_MULT_CHOICE || responseCode == HTTP_MOVED_PERM || responseCode == HTTP_MOVED_TEMP @@ -501,7 +501,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy { } @Override public final int getResponseCode() throws IOException { - return getResponse().getResponseCode(); + return getResponse().getResponse().code(); } @Override public final void setRequestProperty(String field, String newValue) { diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java index 594240f39..5586ddac1 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/HttpsURLConnectionImpl.java @@ -76,7 +76,7 @@ public final class HttpsURLConnectionImpl extends HttpsURLConnection { if (delegate.httpEngine == null) { throw new IllegalStateException("Connection has not yet been established"); } - return delegate.httpEngine.getHandshake(); + return delegate.httpEngine.getResponse().handshake(); } @Override public void disconnect() { diff --git a/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java b/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java index 07b6adc4d..5ed951090 100644 --- a/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java +++ b/okhttp/src/main/java/com/squareup/okhttp/internal/http/SpdyTransport.java @@ -27,7 +27,11 @@ import java.io.OutputStream; import java.net.CacheRequest; import java.net.ProtocolException; import java.net.URL; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Locale; +import java.util.Set; public final class SpdyTransport implements Transport { private final HttpEngine httpEngine; @@ -45,7 +49,7 @@ public final class SpdyTransport implements Transport { String version = RequestLine.version(httpEngine.connection.getHttpMinorVersion()); URL url = request.url(); builder.addSpdyRequestHeaders(request.method(), RequestLine.requestPath(url), version, - HttpEngine.getOriginAddress(url), httpEngine.getRequest().url().getProtocol()); + HttpEngine.getHostHeader(url), httpEngine.getRequest().url().getProtocol()); if (httpEngine.hasRequestBody()) { long fixedContentLength = httpEngine.policy.getFixedContentLength(); @@ -69,7 +73,7 @@ public final class SpdyTransport implements Transport { httpEngine.writingRequestHeaders(); boolean hasRequestBody = httpEngine.hasRequestBody(); boolean hasResponseBody = true; - stream = spdyConnection.newStream(httpEngine.getRequest().getHeaders().toNameValueBlock(), + stream = spdyConnection.newStream(writeNameValueBlock(httpEngine.getRequest().getHeaders()), hasRequestBody, hasResponseBody); stream.setReadTimeout(httpEngine.client.getReadTimeout()); } @@ -88,10 +92,49 @@ public final class SpdyTransport implements Transport { .handshake(httpEngine.connection.getHandshake()) .build(); httpEngine.connection.setHttpMinorVersion(response.httpMinorVersion()); - httpEngine.receiveHeaders(response.rawHeaders()); + httpEngine.receiveHeaders(response.headers()); return response; } + /** + * Returns a list of alternating names and values containing a SPDY request. + * Names are all lower case. No names are repeated. If any name has multiple + * values, they are concatenated using "\0" as a delimiter. + */ + public static List writeNameValueBlock(Headers headers) { + Set names = new HashSet(); + List result = new ArrayList(); + for (int i = 0; i < headers.length(); i++) { + String name = headers.getFieldName(i).toLowerCase(Locale.US); + String value = headers.getValue(i); + + // Drop headers that are forbidden when layering HTTP over SPDY. + if (name.equals("connection") + || name.equals("host") + || name.equals("keep-alive") + || name.equals("proxy-connection") + || name.equals("transfer-encoding")) { + continue; + } + + // If we haven't seen this name before, add the pair to the end of the list... + if (names.add(name)) { + result.add(name); + result.add(value); + continue; + } + + // ...otherwise concatenate the existing values and this value. + for (int j = 0; j < result.size(); j += 2) { + if (name.equals(result.get(j))) { + result.set(j + 1, result.get(j + 1) + "\0" + value); + break; + } + } + } + return result; + } + /** Returns headers for a name value block containing a SPDY response. */ public static Response.Builder readNameValueBlock(Request request, List nameValueBlock) throws IOException { @@ -101,7 +144,7 @@ public final class SpdyTransport implements Transport { String status = null; String version = null; - RawHeaders.Builder headersBuilder = new RawHeaders.Builder(); + Headers.Builder headersBuilder = new Headers.Builder(); headersBuilder.set(Response.SELECTED_TRANSPORT, "spdy/3"); for (int i = 0; i < nameValueBlock.size(); i += 2) { String name = nameValueBlock.get(i); @@ -127,7 +170,7 @@ public final class SpdyTransport implements Transport { return new Response.Builder(request) .statusLine(new StatusLine(version + " " + status)) - .rawHeaders(headersBuilder.build()); + .headers(headersBuilder.build()); } @Override public InputStream getTransferStream(CacheRequest cacheRequest) throws IOException { diff --git a/okhttp/src/test/java/com/squareup/okhttp/internal/http/RawHeadersTest.java b/okhttp/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java similarity index 63% rename from okhttp/src/test/java/com/squareup/okhttp/internal/http/RawHeadersTest.java rename to okhttp/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java index d109e5428..d815d431d 100644 --- a/okhttp/src/test/java/com/squareup/okhttp/internal/http/RawHeadersTest.java +++ b/okhttp/src/test/java/com/squareup/okhttp/internal/http/HeadersTest.java @@ -25,7 +25,7 @@ import org.junit.Test; import static junit.framework.Assert.assertNull; import static org.junit.Assert.assertEquals; -public final class RawHeadersTest { +public final class HeadersTest { @Test public void parseNameValueBlock() throws IOException { List nameValueBlock = Arrays.asList( "cache-control", "no-cache, no-store", @@ -34,31 +34,31 @@ public final class RawHeadersTest { ":version", "HTTP/1.1"); Request request = new Request.Builder("http://square.com/").build(); Response response = SpdyTransport.readNameValueBlock(request, nameValueBlock).build(); - RawHeaders rawHeaders = response.rawHeaders(); - assertEquals(4, rawHeaders.length()); + Headers headers = response.headers(); + assertEquals(4, headers.length()); assertEquals("HTTP/1.1 200 OK", response.statusLine()); - assertEquals("no-cache, no-store", rawHeaders.get("cache-control")); - assertEquals("Cookie2", rawHeaders.get("set-cookie")); - assertEquals("spdy/3", rawHeaders.get(Response.SELECTED_TRANSPORT)); - assertEquals(Response.SELECTED_TRANSPORT, rawHeaders.getFieldName(0)); - assertEquals("spdy/3", rawHeaders.getValue(0)); - assertEquals("cache-control", rawHeaders.getFieldName(1)); - assertEquals("no-cache, no-store", rawHeaders.getValue(1)); - assertEquals("set-cookie", rawHeaders.getFieldName(2)); - assertEquals("Cookie1", rawHeaders.getValue(2)); - assertEquals("set-cookie", rawHeaders.getFieldName(3)); - assertEquals("Cookie2", rawHeaders.getValue(3)); - assertNull(rawHeaders.get(":status")); - assertNull(rawHeaders.get(":version")); + assertEquals("no-cache, no-store", headers.get("cache-control")); + assertEquals("Cookie2", headers.get("set-cookie")); + assertEquals("spdy/3", headers.get(Response.SELECTED_TRANSPORT)); + assertEquals(Response.SELECTED_TRANSPORT, headers.getFieldName(0)); + assertEquals("spdy/3", headers.getValue(0)); + assertEquals("cache-control", headers.getFieldName(1)); + assertEquals("no-cache, no-store", headers.getValue(1)); + assertEquals("set-cookie", headers.getFieldName(2)); + assertEquals("Cookie1", headers.getValue(2)); + assertEquals("set-cookie", headers.getFieldName(3)); + assertEquals("Cookie2", headers.getValue(3)); + assertNull(headers.get(":status")); + assertNull(headers.get(":version")); } @Test public void toNameValueBlock() { - RawHeaders.Builder builder = new RawHeaders.Builder(); + Headers.Builder builder = new Headers.Builder(); builder.add("cache-control", "no-cache, no-store"); builder.add("set-cookie", "Cookie1"); builder.add("set-cookie", "Cookie2"); builder.add(":status", "200 OK"); - List nameValueBlock = builder.build().toNameValueBlock(); + List nameValueBlock = SpdyTransport.writeNameValueBlock(builder.build()); List expected = Arrays.asList( "cache-control", "no-cache, no-store", "set-cookie", "Cookie1\u0000Cookie2", @@ -67,9 +67,9 @@ public final class RawHeadersTest { } @Test public void toNameValueBlockDropsForbiddenHeaders() { - RawHeaders.Builder builder = new RawHeaders.Builder(); + Headers.Builder builder = new Headers.Builder(); builder.add("Connection", "close"); builder.add("Transfer-Encoding", "chunked"); - assertEquals(Arrays.asList(), builder.build().toNameValueBlock()); + assertEquals(Arrays.asList(), SpdyTransport.writeNameValueBlock(builder.build())); } }