mirror of
https://github.com/square/okhttp.git
synced 2026-01-25 16:01:38 +03:00
Merge pull request #394 from square/jwilson_0102_headers_apis
Another round of header APIs cleanup.
This commit is contained in:
@@ -38,9 +38,9 @@ import static java.net.HttpURLConnection.HTTP_OK;
|
||||
import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
|
||||
|
||||
/**
|
||||
* Holds the sockets and streams of an HTTP, HTTPS, or HTTPS+SPDY connection,
|
||||
* which may be used for multiple HTTP request/response exchanges. Connections
|
||||
* may be direct to the origin server or via a proxy.
|
||||
* The sockets and streams of an HTTP, HTTPS, or HTTPS+SPDY connection. May be
|
||||
* used for multiple HTTP request/response exchanges. Connections may be direct
|
||||
* to the origin server or via a proxy.
|
||||
*
|
||||
* <p>Typically instances of this class are created, connected and exercised
|
||||
* automatically by the HTTP client. Applications may use this class to monitor
|
||||
@@ -53,10 +53,10 @@ import static java.net.HttpURLConnection.HTTP_PROXY_AUTH;
|
||||
* There are tradeoffs when selecting which options to include when negotiating
|
||||
* a secure connection to a remote host. Newer TLS options are quite useful:
|
||||
* <ul>
|
||||
* <li>Server Name Indication (SNI) enables one IP address to negotiate secure
|
||||
* connections for multiple domain names.
|
||||
* <li>Next Protocol Negotiation (NPN) enables the HTTPS port (443) to be used
|
||||
* for both HTTP and SPDY transports.
|
||||
* <li>Server Name Indication (SNI) enables one IP address to negotiate secure
|
||||
* connections for multiple domain names.
|
||||
* <li>Next Protocol Negotiation (NPN) enables the HTTPS port (443) to be used
|
||||
* for both HTTP and SPDY transports.
|
||||
* </ul>
|
||||
* Unfortunately, older HTTPS servers refuse to connect when such options are
|
||||
* presented. Rather than avoiding these options entirely, this class allows a
|
||||
@@ -223,9 +223,7 @@ public final class Connection implements Closeable {
|
||||
}
|
||||
|
||||
public void resetIdleStartTime() {
|
||||
if (spdyConnection != null) {
|
||||
throw new IllegalStateException("spdyConnection != null");
|
||||
}
|
||||
if (spdyConnection != null) throw new IllegalStateException("spdyConnection != null");
|
||||
this.idleStartTimeNs = System.nanoTime();
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package com.squareup.okhttp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -55,31 +53,4 @@ final class Dispatcher {
|
||||
List<Job> jobs = enqueuedJobs.get(job.tag());
|
||||
if (jobs != null) jobs.remove(job);
|
||||
}
|
||||
|
||||
static class RealResponseBody extends Response.Body {
|
||||
private final Response response;
|
||||
private final InputStream in;
|
||||
|
||||
RealResponseBody(Response response, InputStream in) {
|
||||
this.response = response;
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
@Override public boolean ready() throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public MediaType contentType() {
|
||||
String contentType = response.getContentType();
|
||||
return contentType != null ? MediaType.parse(contentType) : null;
|
||||
}
|
||||
|
||||
@Override public long contentLength() {
|
||||
return response.getContentLength();
|
||||
}
|
||||
|
||||
@Override public InputStream byteStream() {
|
||||
return in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,27 +15,21 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.squareup.okhttp.internal.http;
|
||||
package com.squareup.okhttp;
|
||||
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* The HTTP status and unparsed header fields of a single HTTP message. Values
|
||||
* are represented as uninterpreted strings; use {@code Request} and
|
||||
* {@code Response} for interpreted headers. This class maintains the
|
||||
* order of the header fields within the HTTP message.
|
||||
* The header fields of a single HTTP message. Values are uninterpreted strings;
|
||||
* use {@code Request} and {@code Response} for interpreted headers. This class
|
||||
* maintains the order of the header fields within the HTTP message.
|
||||
*
|
||||
* <p>This class tracks fields line-by-line. A field with multiple comma-
|
||||
* <p>This class tracks header values line-by-line. A field with multiple comma-
|
||||
* separated values on the same line will be treated as a field with a single
|
||||
* value by this class. It is the caller's responsibility to detect and split
|
||||
* on commas if their field permits multiple values. This simplifies use of
|
||||
@@ -44,29 +38,22 @@ import java.util.TreeSet;
|
||||
*
|
||||
* <p>This class trims whitespace from values. It never returns values with
|
||||
* leading or trailing whitespace.
|
||||
*
|
||||
* <p>Instances of this class are immutable. Use {@link Builder} to create
|
||||
* instances.
|
||||
*/
|
||||
public final class Headers {
|
||||
private static final Comparator<String> FIELD_NAME_COMPARATOR = new Comparator<String>() {
|
||||
// @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
|
||||
@Override public int compare(String a, String b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
} else if (a == null) {
|
||||
return -1;
|
||||
} else if (b == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(a, b);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final List<String> namesAndValues;
|
||||
|
||||
private Headers(Builder builder) {
|
||||
this.namesAndValues = Util.immutableList(builder.namesAndValues);
|
||||
}
|
||||
|
||||
/** Returns the last value corresponding to the specified field, or null. */
|
||||
public String get(String fieldName) {
|
||||
return get(namesAndValues, fieldName);
|
||||
}
|
||||
|
||||
/** Returns the number of field values. */
|
||||
public int size() {
|
||||
return namesAndValues.size() / 2;
|
||||
@@ -81,15 +68,6 @@ public final class Headers {
|
||||
return namesAndValues.get(fieldNameIndex);
|
||||
}
|
||||
|
||||
/** Returns an immutable case-insensitive set of header names. */
|
||||
public Set<String> names() {
|
||||
TreeSet<String> result = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (int i = 0; i < size(); i++) {
|
||||
result.add(name(i));
|
||||
}
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
/** Returns the value at {@code index} or null if that is out of range. */
|
||||
public String value(int index) {
|
||||
int valueIndex = index * 2 + 1;
|
||||
@@ -99,9 +77,13 @@ public final class Headers {
|
||||
return namesAndValues.get(valueIndex);
|
||||
}
|
||||
|
||||
/** Returns the last value corresponding to the specified field, or null. */
|
||||
public String get(String fieldName) {
|
||||
return get(namesAndValues, fieldName);
|
||||
/** Returns an immutable case-insensitive set of header names. */
|
||||
public Set<String> names() {
|
||||
TreeSet<String> result = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
|
||||
for (int i = 0; i < size(); i++) {
|
||||
result.add(name(i));
|
||||
}
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
/** Returns an immutable list of the header values for {@code name}. */
|
||||
@@ -119,6 +101,7 @@ public final class Headers {
|
||||
}
|
||||
|
||||
/** @param fieldNames a case-insensitive set of HTTP header field names. */
|
||||
// TODO: it is very weird to request a case-insensitive set as a parameter.
|
||||
public Headers getAll(Set<String> fieldNames) {
|
||||
Builder result = new Builder();
|
||||
for (int i = 0; i < namesAndValues.size(); i += 2) {
|
||||
@@ -130,32 +113,6 @@ public final class Headers {
|
||||
return result.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable map containing each field to its list of values.
|
||||
*
|
||||
* @param valueForNullKey the request line for requests, or the status line
|
||||
* for responses. If non-null, this value is mapped to the null key.
|
||||
*/
|
||||
public Map<String, List<String>> toMultimap(String valueForNullKey) {
|
||||
Map<String, List<String>> result = new TreeMap<String, List<String>>(FIELD_NAME_COMPARATOR);
|
||||
for (int i = 0; i < namesAndValues.size(); i += 2) {
|
||||
String fieldName = namesAndValues.get(i);
|
||||
String value = namesAndValues.get(i + 1);
|
||||
|
||||
List<String> allValues = new ArrayList<String>();
|
||||
List<String> otherValues = result.get(fieldName);
|
||||
if (otherValues != null) {
|
||||
allValues.addAll(otherValues);
|
||||
}
|
||||
allValues.add(value);
|
||||
result.put(fieldName, Collections.unmodifiableList(allValues));
|
||||
}
|
||||
if (valueForNullKey != null) {
|
||||
result.put(null, Collections.unmodifiableList(Collections.singletonList(valueForNullKey)));
|
||||
}
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
public Builder newBuilder() {
|
||||
Builder result = new Builder();
|
||||
result.namesAndValues.addAll(namesAndValues);
|
||||
@@ -174,21 +131,14 @@ public final class Headers {
|
||||
public static class Builder {
|
||||
private final List<String> namesAndValues = new ArrayList<String>(20);
|
||||
|
||||
/** Equivalent to {@code build().get(fieldName)}, but potentially faster. */
|
||||
public String get(String fieldName) {
|
||||
return Headers.get(namesAndValues, fieldName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an HTTP header line containing a field name, a literal colon, and a
|
||||
* value. This works around empty header names and header names that start
|
||||
* with a colon (created by old broken SPDY versions of the response cache).
|
||||
*/
|
||||
/** Add an header line containing a field name, a literal colon, and a value. */
|
||||
public Builder addLine(String line) {
|
||||
int index = line.indexOf(":", 1);
|
||||
if (index != -1) {
|
||||
return addLenient(line.substring(0, index), line.substring(index + 1));
|
||||
} else if (line.startsWith(":")) {
|
||||
// Work around empty header names and header names that start with a
|
||||
// colon (created by old broken SPDY versions of the response cache).
|
||||
return addLenient("", line.substring(1)); // Empty header name.
|
||||
} else {
|
||||
return addLenient("", line); // No header name.
|
||||
@@ -235,13 +185,9 @@ public final class Headers {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Reads headers or trailers into {@code out}. */
|
||||
public Builder readHeaders(InputStream in) throws IOException {
|
||||
// parse the result headers until the first blank line
|
||||
for (String line; (line = Util.readAsciiLine(in)).length() != 0; ) {
|
||||
addLine(line);
|
||||
}
|
||||
return this;
|
||||
/** Equivalent to {@code build().get(fieldName)}, but potentially faster. */
|
||||
public String get(String fieldName) {
|
||||
return Headers.get(namesAndValues, fieldName);
|
||||
}
|
||||
|
||||
public Headers build() {
|
||||
@@ -20,7 +20,6 @@ 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.Headers;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
|
||||
@@ -17,7 +17,9 @@ package com.squareup.okhttp;
|
||||
|
||||
import com.squareup.okhttp.internal.http.HttpAuthenticator;
|
||||
import com.squareup.okhttp.internal.http.HttpEngine;
|
||||
import com.squareup.okhttp.internal.http.OkHeaders;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
@@ -82,7 +84,7 @@ final class Job implements Runnable {
|
||||
|
||||
long contentLength = body.contentLength();
|
||||
if (contentLength != -1) {
|
||||
requestBuilder.setContentLength(contentLength);
|
||||
requestBuilder.header("Content-Length", Long.toString(contentLength));
|
||||
requestBuilder.removeHeader("Transfer-Encoding");
|
||||
} else {
|
||||
requestBuilder.header("Transfer-Encoding", "chunked");
|
||||
@@ -107,7 +109,7 @@ final class Job implements Runnable {
|
||||
if (redirect == null) {
|
||||
engine.automaticallyReleaseConnectionToPool();
|
||||
return response.newBuilder()
|
||||
.body(new Dispatcher.RealResponseBody(response, engine.getResponseBody()))
|
||||
.body(new RealResponseBody(response, engine.getResponseBody()))
|
||||
.redirectedBy(redirectedBy)
|
||||
.build();
|
||||
}
|
||||
@@ -183,4 +185,31 @@ final class Job implements Runnable {
|
||||
&& getEffectivePort(a.url()) == getEffectivePort(b.url())
|
||||
&& a.url().getProtocol().equals(b.url().getProtocol());
|
||||
}
|
||||
|
||||
static class RealResponseBody extends Response.Body {
|
||||
private final Response response;
|
||||
private final InputStream in;
|
||||
|
||||
RealResponseBody(Response response, InputStream in) {
|
||||
this.response = response;
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
@Override public boolean ready() throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public MediaType contentType() {
|
||||
String contentType = response.header("Content-Type");
|
||||
return contentType != null ? MediaType.parse(contentType) : null;
|
||||
}
|
||||
|
||||
@Override public long contentLength() {
|
||||
return OkHeaders.contentLength(response);
|
||||
}
|
||||
|
||||
@Override public InputStream byteStream() {
|
||||
return in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ 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 java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -32,8 +31,6 @@ import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An HTTP request. Instances of this class are immutable if their {@link #body}
|
||||
@@ -81,6 +78,10 @@ public final class Request {
|
||||
return method;
|
||||
}
|
||||
|
||||
public Headers headers() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public String header(String name) {
|
||||
return headers.get(name);
|
||||
}
|
||||
@@ -89,26 +90,6 @@ public final class Request {
|
||||
return headers.values(name);
|
||||
}
|
||||
|
||||
public Set<String> headerNames() {
|
||||
return headers.names();
|
||||
}
|
||||
|
||||
Headers headers() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public int headerCount() {
|
||||
return headers.size();
|
||||
}
|
||||
|
||||
public String headerName(int index) {
|
||||
return headers.name(index);
|
||||
}
|
||||
|
||||
public String headerValue(int index) {
|
||||
return headers.value(index);
|
||||
}
|
||||
|
||||
public Body body() {
|
||||
return body;
|
||||
}
|
||||
@@ -145,20 +126,10 @@ public final class Request {
|
||||
return parsedHeaders().onlyIfCached;
|
||||
}
|
||||
|
||||
// TODO: Make non-public. This conflicts with the Body's content length!
|
||||
public long getContentLength() {
|
||||
return parsedHeaders().contentLength;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return parsedHeaders().userAgent;
|
||||
}
|
||||
|
||||
// TODO: Make non-public. This conflicts with the Body's content type!
|
||||
public String getContentType() {
|
||||
return parsedHeaders().contentType;
|
||||
}
|
||||
|
||||
public String getProxyAuthorization() {
|
||||
return parsedHeaders().proxyAuthorization;
|
||||
}
|
||||
@@ -189,9 +160,7 @@ public final class Request {
|
||||
*/
|
||||
private boolean onlyIfCached;
|
||||
|
||||
private long contentLength = -1;
|
||||
private String userAgent;
|
||||
private String contentType;
|
||||
private String proxyAuthorization;
|
||||
|
||||
public ParsedHeaders(Headers headers) {
|
||||
@@ -220,15 +189,8 @@ public final class Request {
|
||||
if ("no-cache".equalsIgnoreCase(value)) {
|
||||
noCache = true;
|
||||
}
|
||||
} else if ("Content-Length".equalsIgnoreCase(fieldName)) {
|
||||
try {
|
||||
contentLength = Long.parseLong(value);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
} else if ("User-Agent".equalsIgnoreCase(fieldName)) {
|
||||
userAgent = value;
|
||||
} else if ("Content-Type".equalsIgnoreCase(fieldName)) {
|
||||
contentType = value;
|
||||
} else if ("Proxy-Authorization".equalsIgnoreCase(fieldName)) {
|
||||
proxyAuthorization = value;
|
||||
}
|
||||
@@ -323,7 +285,7 @@ public final class Request {
|
||||
public static class Builder {
|
||||
private URL url;
|
||||
private String method;
|
||||
private final Headers.Builder headers;
|
||||
private Headers.Builder headers;
|
||||
private Body body;
|
||||
private Object tag;
|
||||
|
||||
@@ -377,51 +339,22 @@ public final class Request {
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: conflict's with the body's content type.
|
||||
public Builder setContentLength(long contentLength) {
|
||||
headers.set("Content-Length", Long.toString(contentLength));
|
||||
/** Removes all headers on this builder and adds {@code headers}. */
|
||||
public Builder headers(Headers headers) {
|
||||
this.headers = headers.newBuilder();
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setUserAgent(String userAgent) {
|
||||
headers.set("User-Agent", userAgent);
|
||||
public Builder setUserAgent(String userAgent) {
|
||||
return header("User-Agent", userAgent);
|
||||
}
|
||||
|
||||
// TODO: conflict's with the body's content type.
|
||||
public void setContentType(String contentType) {
|
||||
headers.set("Content-Type", contentType);
|
||||
public Builder setIfModifiedSince(Date date) {
|
||||
return header("If-Modified-Since", HttpDate.format(date));
|
||||
}
|
||||
|
||||
public void setIfModifiedSince(Date date) {
|
||||
headers.set("If-Modified-Since", HttpDate.format(date));
|
||||
}
|
||||
|
||||
public void setIfNoneMatch(String ifNoneMatch) {
|
||||
headers.set("If-None-Match", ifNoneMatch);
|
||||
}
|
||||
|
||||
public void addCookies(Map<String, List<String>> cookieHeaders) {
|
||||
for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key))
|
||||
&& !entry.getValue().isEmpty()) {
|
||||
headers.add(key, buildCookieHeader(entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all cookies in one big header, as recommended by
|
||||
* <a href="http://tools.ietf.org/html/rfc6265#section-4.2.1">RFC 6265</a>.
|
||||
*/
|
||||
private String buildCookieHeader(List<String> cookies) {
|
||||
if (cookies.size() == 1) return cookies.get(0);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < cookies.size(); i++) {
|
||||
if (i > 0) sb.append("; ");
|
||||
sb.append(cookies.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
public Builder setIfNoneMatch(String ifNoneMatch) {
|
||||
return header("If-None-Match", ifNoneMatch);
|
||||
}
|
||||
|
||||
public Builder get() {
|
||||
|
||||
@@ -17,10 +17,9 @@ package com.squareup.okhttp;
|
||||
|
||||
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.OkHeaders;
|
||||
import com.squareup.okhttp.internal.http.StatusLine;
|
||||
import com.squareup.okhttp.internal.http.SyntheticHeaders;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
@@ -105,6 +104,10 @@ public final class Response {
|
||||
return handshake;
|
||||
}
|
||||
|
||||
public List<String> headers(String name) {
|
||||
return headers.values(name);
|
||||
}
|
||||
|
||||
public String header(String name) {
|
||||
return header(name, null);
|
||||
}
|
||||
@@ -114,31 +117,10 @@ public final class Response {
|
||||
return result != null ? result : defaultValue;
|
||||
}
|
||||
|
||||
public List<String> headers(String name) {
|
||||
return headers.values(name);
|
||||
}
|
||||
|
||||
public Set<String> headerNames() {
|
||||
return headers.names();
|
||||
}
|
||||
|
||||
public int headerCount() {
|
||||
return headers.size();
|
||||
}
|
||||
|
||||
public String headerName(int index) {
|
||||
return headers.name(index);
|
||||
}
|
||||
|
||||
// TODO: this shouldn't be public?
|
||||
public Headers headers() {
|
||||
return headers;
|
||||
}
|
||||
|
||||
public String headerValue(int index) {
|
||||
return headers.value(index);
|
||||
}
|
||||
|
||||
public Body body() {
|
||||
return body;
|
||||
}
|
||||
@@ -201,16 +183,6 @@ public final class Response {
|
||||
return parsedHeaders().varyFields;
|
||||
}
|
||||
|
||||
// TODO: this shouldn't be public.
|
||||
public long getContentLength() {
|
||||
return parsedHeaders().contentLength;
|
||||
}
|
||||
|
||||
// TODO: this shouldn't be public.
|
||||
public String getContentType() {
|
||||
return parsedHeaders().contentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a Vary header contains an asterisk. Such responses cannot
|
||||
* be cached.
|
||||
@@ -471,9 +443,9 @@ public final class Response {
|
||||
}
|
||||
} else if ("Content-Type".equalsIgnoreCase(fieldName)) {
|
||||
contentType = value;
|
||||
} else if (SyntheticHeaders.SENT_MILLIS.equalsIgnoreCase(fieldName)) {
|
||||
} else if (OkHeaders.SENT_MILLIS.equalsIgnoreCase(fieldName)) {
|
||||
sentRequestMillis = Long.parseLong(value);
|
||||
} else if (SyntheticHeaders.RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
|
||||
} else if (OkHeaders.RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
|
||||
receivedResponseMillis = Long.parseLong(value);
|
||||
}
|
||||
}
|
||||
@@ -589,7 +561,7 @@ public final class Response {
|
||||
return this;
|
||||
}
|
||||
|
||||
// TODO: this shouldn't be public?
|
||||
/** Removes all headers on this builder and adds {@code headers}. */
|
||||
public Builder headers(Headers headers) {
|
||||
this.headers = headers.newBuilder();
|
||||
return this;
|
||||
@@ -602,8 +574,7 @@ public final class Response {
|
||||
|
||||
// TODO: this shouldn't be public.
|
||||
public Builder setResponseSource(ResponseSource responseSource) {
|
||||
headers.set(SyntheticHeaders.RESPONSE_SOURCE, responseSource + " " + statusLine.code());
|
||||
return this;
|
||||
return header(OkHeaders.RESPONSE_SOURCE, responseSource + " " + statusLine.code());
|
||||
}
|
||||
|
||||
public Builder redirectedBy(Response redirectedBy) {
|
||||
|
||||
@@ -18,7 +18,21 @@ package com.squareup.okhttp;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
|
||||
/** Represents the route used by a connection to reach an endpoint. */
|
||||
/**
|
||||
* The concrete route used by a connection to reach an abstract origin server.
|
||||
* When creating a connection the client has many options:
|
||||
* <ul>
|
||||
* <li><strong>HTTP proxy:</strong> a proxy server may be explicitly
|
||||
* configured for the client. Otherwise the {@link java.net.ProxySelector
|
||||
* proxy selector} is used. It may return multiple proxies to attempt.
|
||||
* <li><strong>IP address:</strong> whether connecting directly to an origin
|
||||
* server or a proxy, opening a socket requires an IP address. The DNS
|
||||
* server may return multiple IP addresses to attempt.
|
||||
* <li><strong>Modern TLS:</strong> whether to include advanced TLS options
|
||||
* when attempting a HTTPS connection.
|
||||
* </ul>
|
||||
* Each route is a specific selection of these options.
|
||||
*/
|
||||
public class Route {
|
||||
final Address address;
|
||||
final Proxy proxy;
|
||||
@@ -44,11 +58,8 @@ public class Route {
|
||||
/**
|
||||
* Returns the {@link Proxy} of this route.
|
||||
*
|
||||
* <strong>Warning:</strong> This may be different than the proxy returned
|
||||
* by {@link #getAddress}! That is the proxy that the user asked to be
|
||||
* connected to; this returns the proxy that they were actually connected
|
||||
* to. The two may disagree when a proxy selector selects a different proxy
|
||||
* for a connection.
|
||||
* <strong>Warning:</strong> This may disagree with {@link Address#getProxy}
|
||||
* is null. When the address's proxy is null, the proxy selector will be used.
|
||||
*/
|
||||
public Proxy getProxy() {
|
||||
return proxy;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.OkAuthenticator;
|
||||
import com.squareup.okhttp.OkAuthenticator.Challenge;
|
||||
import com.squareup.okhttp.Request;
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Address;
|
||||
import com.squareup.okhttp.Connection;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.OkResponseCache;
|
||||
import com.squareup.okhttp.Request;
|
||||
@@ -35,6 +35,8 @@ import java.net.CacheRequest;
|
||||
import java.net.CookieHandler;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
@@ -206,10 +208,7 @@ public class HttpEngine {
|
||||
private Response cacheableResponse() {
|
||||
// Use an unreadable response body when offering the response to the cache.
|
||||
// The cache isn't allowed to consume the response body bytes!
|
||||
return response.newBuilder()
|
||||
.body(new UnreadableResponseBody(response.getContentType(),
|
||||
response.getContentLength()))
|
||||
.build();
|
||||
return response.newBuilder().body(null).build();
|
||||
}
|
||||
|
||||
/** Connect to the origin server either directly or via a proxy. */
|
||||
@@ -400,7 +399,7 @@ public class HttpEngine {
|
||||
// If the Content-Length or Transfer-Encoding headers disagree with the
|
||||
// response code, the response is malformed. For best compatibility, we
|
||||
// honor the headers.
|
||||
if (response.getContentLength() != -1
|
||||
if (OkHeaders.contentLength(response) != -1
|
||||
|| "chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
|
||||
return true;
|
||||
}
|
||||
@@ -435,13 +434,15 @@ public class HttpEngine {
|
||||
result.header("Accept-Encoding", "gzip");
|
||||
}
|
||||
|
||||
if (hasRequestBody() && request.getContentType() == null) {
|
||||
result.setContentType("application/x-www-form-urlencoded");
|
||||
if (hasRequestBody() && request.header("Content-Type") == null) {
|
||||
result.header("Content-Type", "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
CookieHandler cookieHandler = client.getCookieHandler();
|
||||
if (cookieHandler != null) {
|
||||
result.addCookies(cookieHandler.get(request.uri(), request.getHeaders().toMultimap(null)));
|
||||
Map<String, List<String>> cookies = cookieHandler.get(
|
||||
request.uri(), OkHeaders.toMultimap(request.getHeaders(), null));
|
||||
OkHeaders.addCookies(result, cookies);
|
||||
}
|
||||
|
||||
request = result.build();
|
||||
@@ -468,11 +469,13 @@ public class HttpEngine {
|
||||
if (!responseSource.requiresConnection()) return;
|
||||
|
||||
if (sentRequestMillis == -1) {
|
||||
if (request.getContentLength() == -1
|
||||
if (OkHeaders.contentLength(request) == -1
|
||||
&& requestBodyOut instanceof RetryableOutputStream) {
|
||||
// We might not learn the Content-Length until the request body has been buffered.
|
||||
int contentLength = ((RetryableOutputStream) requestBodyOut).contentLength();
|
||||
request = request.newBuilder().setContentLength(contentLength).build();
|
||||
long contentLength = ((RetryableOutputStream) requestBodyOut).contentLength();
|
||||
request = request.newBuilder()
|
||||
.header("Content-Length", Long.toString(contentLength))
|
||||
.build();
|
||||
}
|
||||
transport.writeRequestHeaders(request);
|
||||
}
|
||||
@@ -489,8 +492,8 @@ public class HttpEngine {
|
||||
response = transport.readResponseHeaders()
|
||||
.request(request)
|
||||
.handshake(connection.getHandshake())
|
||||
.header(SyntheticHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
|
||||
.header(SyntheticHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
|
||||
.header(OkHeaders.SENT_MILLIS, Long.toString(sentRequestMillis))
|
||||
.header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis()))
|
||||
.setResponseSource(responseSource)
|
||||
.build();
|
||||
connection.setHttpMinorVersion(response.httpMinorVersion());
|
||||
@@ -530,9 +533,10 @@ public class HttpEngine {
|
||||
private static Response combine(Response cached, Response network) throws IOException {
|
||||
Headers.Builder result = new Headers.Builder();
|
||||
|
||||
for (int i = 0; i < cached.headerCount(); i++) {
|
||||
String fieldName = cached.headerName(i);
|
||||
String value = cached.headerValue(i);
|
||||
Headers cachedHeaders = cached.headers();
|
||||
for (int i = 0; i < cachedHeaders.size(); i++) {
|
||||
String fieldName = cachedHeaders.name(i);
|
||||
String value = cachedHeaders.value(i);
|
||||
if ("Warning".equals(fieldName) && value.startsWith("1")) {
|
||||
continue; // drop 100-level freshness warnings
|
||||
}
|
||||
@@ -541,10 +545,11 @@ public class HttpEngine {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < network.headerCount(); i++) {
|
||||
String fieldName = network.headerName(i);
|
||||
Headers networkHeaders = network.headers();
|
||||
for (int i = 0; i < networkHeaders.size(); i++) {
|
||||
String fieldName = networkHeaders.name(i);
|
||||
if (isEndToEnd(fieldName)) {
|
||||
result.add(fieldName, network.headerValue(i));
|
||||
result.add(fieldName, networkHeaders.value(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,33 +585,7 @@ public class HttpEngine {
|
||||
public void receiveHeaders(Headers headers) throws IOException {
|
||||
CookieHandler cookieHandler = client.getCookieHandler();
|
||||
if (cookieHandler != null) {
|
||||
cookieHandler.put(request.uri(), headers.toMultimap(null));
|
||||
}
|
||||
}
|
||||
|
||||
static class UnreadableResponseBody extends Response.Body {
|
||||
private final String contentType;
|
||||
private final long contentLength;
|
||||
|
||||
public UnreadableResponseBody(String contentType, long contentLength) {
|
||||
this.contentType = contentType;
|
||||
this.contentLength = contentLength;
|
||||
}
|
||||
|
||||
@Override public boolean ready() throws IOException {
|
||||
throw new IllegalStateException("It is an error to read this response body at this time.");
|
||||
}
|
||||
|
||||
@Override public MediaType contentType() {
|
||||
return contentType != null ? MediaType.parse(contentType) : null;
|
||||
}
|
||||
|
||||
@Override public long contentLength() {
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
@Override public InputStream byteStream() {
|
||||
throw new IllegalStateException("It is an error to read this response body at this time.");
|
||||
cookieHandler.put(request.uri(), OkHeaders.toMultimap(headers, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Connection;
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.internal.AbstractOutputStream;
|
||||
@@ -62,7 +63,7 @@ public final class HttpTransport implements Transport {
|
||||
}
|
||||
|
||||
@Override public OutputStream createRequestBody(Request request) throws IOException {
|
||||
long contentLength = request.getContentLength();
|
||||
long contentLength = OkHeaders.contentLength(request);
|
||||
|
||||
if (httpEngine.bufferRequestBody) {
|
||||
if (contentLength > Integer.MAX_VALUE) {
|
||||
@@ -154,10 +155,10 @@ public final class HttpTransport implements Transport {
|
||||
|
||||
Response.Builder responseBuilder = new Response.Builder()
|
||||
.statusLine(statusLine)
|
||||
.header(SyntheticHeaders.SELECTED_TRANSPORT, "http/1.1");
|
||||
.header(OkHeaders.SELECTED_TRANSPORT, "http/1.1");
|
||||
|
||||
Headers.Builder headersBuilder = new Headers.Builder();
|
||||
headersBuilder.readHeaders(in);
|
||||
OkHeaders.readHeaders(headersBuilder, in);
|
||||
responseBuilder.headers(headersBuilder.build());
|
||||
|
||||
if (statusLine.code() != HTTP_CONTINUE) return responseBuilder;
|
||||
@@ -234,9 +235,9 @@ public final class HttpTransport implements Transport {
|
||||
return new ChunkedInputStream(socketIn, cacheRequest, this);
|
||||
}
|
||||
|
||||
if (httpEngine.getResponse().getContentLength() != -1) {
|
||||
return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine,
|
||||
httpEngine.getResponse().getContentLength());
|
||||
long contentLength = OkHeaders.contentLength(httpEngine.getResponse());
|
||||
if (contentLength != -1) {
|
||||
return new FixedLengthInputStream(socketIn, cacheRequest, httpEngine, contentLength);
|
||||
}
|
||||
|
||||
// Wrap the input stream from the connection (rather than just returning
|
||||
@@ -443,14 +444,12 @@ public final class HttpTransport implements Transport {
|
||||
/** An HTTP body with alternating chunk sizes and chunk bodies. */
|
||||
private static class ChunkedInputStream extends AbstractHttpInputStream {
|
||||
private static final int NO_CHUNK_YET = -1;
|
||||
private final HttpTransport transport;
|
||||
private int bytesRemainingInChunk = NO_CHUNK_YET;
|
||||
private boolean hasMoreChunks = true;
|
||||
|
||||
ChunkedInputStream(InputStream is, CacheRequest cacheRequest, HttpTransport transport)
|
||||
throws IOException {
|
||||
super(is, transport.httpEngine, cacheRequest);
|
||||
this.transport = transport;
|
||||
}
|
||||
|
||||
@Override public int read(byte[] buffer, int offset, int count) throws IOException {
|
||||
@@ -493,10 +492,9 @@ public final class HttpTransport implements Transport {
|
||||
}
|
||||
if (bytesRemainingInChunk == 0) {
|
||||
hasMoreChunks = false;
|
||||
Headers trailers = new Headers.Builder()
|
||||
.readHeaders(transport.socketIn)
|
||||
.build();
|
||||
httpEngine.receiveHeaders(trailers);
|
||||
Headers.Builder trailersBuilder = new Headers.Builder();
|
||||
OkHeaders.readHeaders(trailersBuilder, in);
|
||||
httpEngine.receiveHeaders(trailersBuilder.build());
|
||||
endOfInput();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Connection;
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
@@ -165,7 +166,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
|
||||
@Override public final Map<String, List<String>> getHeaderFields() {
|
||||
try {
|
||||
Response response = getResponse().getResponse();
|
||||
return response.headers().toMultimap(response.statusLine());
|
||||
return OkHeaders.toMultimap(response.headers(), response.statusLine());
|
||||
} catch (IOException e) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
@@ -180,7 +181,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection {
|
||||
// For the request line property assigned to the null key, just use no proxy and HTTP 1.1.
|
||||
Request request = new Request.Builder().url(getURL()).method(method, null).build();
|
||||
String requestLine = RequestLine.get(request, null, 1);
|
||||
return requestHeaders.build().toMultimap(requestLine);
|
||||
return OkHeaders.toMultimap(requestHeaders.build(), requestLine);
|
||||
}
|
||||
|
||||
@Override public final InputStream getInputStream() throws IOException {
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.internal.Platform;
|
||||
import com.squareup.okhttp.internal.Util;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/** Headers and utilities for internal use by OkHttp. */
|
||||
public final class OkHeaders {
|
||||
private static final Comparator<String> FIELD_NAME_COMPARATOR = new Comparator<String>() {
|
||||
// @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
|
||||
@Override public int compare(String a, String b) {
|
||||
if (a == b) {
|
||||
return 0;
|
||||
} else if (a == null) {
|
||||
return -1;
|
||||
} else if (b == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return String.CASE_INSENSITIVE_ORDER.compare(a, b);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static final String PREFIX = Platform.get().getPrefix();
|
||||
|
||||
/**
|
||||
* Synthetic response header: the local time when the request was sent.
|
||||
*/
|
||||
public static final String SENT_MILLIS = PREFIX + "-Sent-Millis";
|
||||
|
||||
/**
|
||||
* Synthetic response header: the local time when the response was received.
|
||||
*/
|
||||
public static final String RECEIVED_MILLIS = PREFIX + "-Received-Millis";
|
||||
|
||||
/**
|
||||
* Synthetic response header: the response source and status code like
|
||||
* "CONDITIONAL_CACHE 304".
|
||||
*/
|
||||
public static final String RESPONSE_SOURCE = PREFIX + "-Response-Source";
|
||||
|
||||
/**
|
||||
* Synthetic response header: the selected transport ("spdy/3", "http/1.1", etc).
|
||||
*/
|
||||
public static final String SELECTED_TRANSPORT = PREFIX + "-Selected-Transport";
|
||||
|
||||
private OkHeaders() {
|
||||
}
|
||||
|
||||
public static long contentLength(Request request) {
|
||||
return stringToLong(request.header("Content-Length"));
|
||||
}
|
||||
|
||||
public static long contentLength(Response response) {
|
||||
return stringToLong(response.header("Content-Length"));
|
||||
}
|
||||
|
||||
private static long stringToLong(String s) {
|
||||
if (s == null) return -1;
|
||||
try {
|
||||
return Long.parseLong(s);
|
||||
} catch (NumberFormatException e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an immutable map containing each field to its list of values.
|
||||
*
|
||||
* @param valueForNullKey the request line for requests, or the status line
|
||||
* for responses. If non-null, this value is mapped to the null key.
|
||||
*/
|
||||
public static Map<String, List<String>> toMultimap(Headers headers, String valueForNullKey) {
|
||||
Map<String, List<String>> result = new TreeMap<String, List<String>>(FIELD_NAME_COMPARATOR);
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
String fieldName = headers.name(i);
|
||||
String value = headers.value(i);
|
||||
|
||||
List<String> allValues = new ArrayList<String>();
|
||||
List<String> otherValues = result.get(fieldName);
|
||||
if (otherValues != null) {
|
||||
allValues.addAll(otherValues);
|
||||
}
|
||||
allValues.add(value);
|
||||
result.put(fieldName, Collections.unmodifiableList(allValues));
|
||||
}
|
||||
if (valueForNullKey != null) {
|
||||
result.put(null, Collections.unmodifiableList(Collections.singletonList(valueForNullKey)));
|
||||
}
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
public static void addCookies(Request.Builder builder, Map<String, List<String>> cookieHeaders) {
|
||||
for (Map.Entry<String, List<String>> entry : cookieHeaders.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key))
|
||||
&& !entry.getValue().isEmpty()) {
|
||||
builder.addHeader(key, buildCookieHeader(entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send all cookies in one big header, as recommended by
|
||||
* <a href="http://tools.ietf.org/html/rfc6265#section-4.2.1">RFC 6265</a>.
|
||||
*/
|
||||
private static String buildCookieHeader(List<String> cookies) {
|
||||
if (cookies.size() == 1) return cookies.get(0);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < cookies.size(); i++) {
|
||||
if (i > 0) sb.append("; ");
|
||||
sb.append(cookies.get(i));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/** Reads headers or trailers into {@code builder}. */
|
||||
public static void readHeaders(Headers.Builder builder, InputStream in) throws IOException {
|
||||
// parse the result headers until the first blank line
|
||||
for (String line; (line = Util.readAsciiLine(in)).length() != 0; ) {
|
||||
builder.addLine(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.internal.spdy.ErrorCode;
|
||||
@@ -78,7 +79,8 @@ public final class SpdyTransport implements Transport {
|
||||
* values, they are concatenated using "\0" as a delimiter.
|
||||
*/
|
||||
public static List<String> writeNameValueBlock(Request request, String version) {
|
||||
List<String> result = new ArrayList<String>(request.headerCount() + 10);
|
||||
Headers headers = request.headers();
|
||||
List<String> result = new ArrayList<String>(headers.size() + 10);
|
||||
result.add(":method");
|
||||
result.add(request.method());
|
||||
result.add(":path");
|
||||
@@ -91,9 +93,9 @@ public final class SpdyTransport implements Transport {
|
||||
result.add(request.url().getProtocol());
|
||||
|
||||
Set<String> names = new LinkedHashSet<String>();
|
||||
for (int i = 0; i < request.headerCount(); i++) {
|
||||
String name = request.headerName(i).toLowerCase(Locale.US);
|
||||
String value = request.headerValue(i);
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
String name = headers.name(i).toLowerCase(Locale.US);
|
||||
String value = headers.value(i);
|
||||
|
||||
// Drop headers that are forbidden when layering HTTP over SPDY.
|
||||
if (name.equals("connection")
|
||||
@@ -141,7 +143,7 @@ public final class SpdyTransport implements Transport {
|
||||
String version = null;
|
||||
|
||||
Headers.Builder headersBuilder = new Headers.Builder();
|
||||
headersBuilder.set(SyntheticHeaders.SELECTED_TRANSPORT, "spdy/3");
|
||||
headersBuilder.set(OkHeaders.SELECTED_TRANSPORT, "spdy/3");
|
||||
for (int i = 0; i < nameValueBlock.size(); i += 2) {
|
||||
String name = nameValueBlock.get(i);
|
||||
String values = nameValueBlock.get(i + 1);
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.internal.Platform;
|
||||
|
||||
/** Headers added to the HTTP response for internal use by OkHttp. */
|
||||
public final class SyntheticHeaders {
|
||||
static final String PREFIX = Platform.get().getPrefix();
|
||||
|
||||
/** The local time when the request was sent. */
|
||||
public static final String SENT_MILLIS = PREFIX + "-Sent-Millis";
|
||||
|
||||
/** The local time when the response was received. */
|
||||
public static final String RECEIVED_MILLIS = PREFIX + "-Received-Millis";
|
||||
|
||||
/** The response source. */
|
||||
public static final String RESPONSE_SOURCE = PREFIX + "-Response-Source";
|
||||
|
||||
/** The selected transport (spdy/3, http/1.1, etc). */
|
||||
public static final String SELECTED_TRANSPORT = PREFIX + "-Selected-Transport";
|
||||
|
||||
private SyntheticHeaders() {
|
||||
}
|
||||
}
|
||||
@@ -47,8 +47,9 @@ public class RecordedResponse {
|
||||
|
||||
public RecordedResponse assertContainsHeaders(String... expectedHeaders) {
|
||||
List<String> actualHeaders = new ArrayList<String>();
|
||||
for (int i = 0; i < response.headerCount(); i++) {
|
||||
actualHeaders.add(response.headerName(i) + ": " + response.headerValue(i));
|
||||
Headers headers = response.headers();
|
||||
for (int i = 0; i < headers.size(); i++) {
|
||||
actualHeaders.add(headers.name(i) + ": " + headers.value(i));
|
||||
}
|
||||
if (!actualHeaders.containsAll(Arrays.asList(expectedHeaders))) {
|
||||
fail("Expected: " + actualHeaders + "\nto contain: " + Arrays.toString(expectedHeaders));
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.squareup.okhttp.internal.http;
|
||||
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
import java.io.IOException;
|
||||
@@ -39,8 +40,8 @@ public final class HeadersTest {
|
||||
assertEquals("HTTP/1.1 200 OK", response.statusLine());
|
||||
assertEquals("no-cache, no-store", headers.get("cache-control"));
|
||||
assertEquals("Cookie2", headers.get("set-cookie"));
|
||||
assertEquals("spdy/3", headers.get(SyntheticHeaders.SELECTED_TRANSPORT));
|
||||
assertEquals(SyntheticHeaders.SELECTED_TRANSPORT, headers.name(0));
|
||||
assertEquals("spdy/3", headers.get(OkHeaders.SELECTED_TRANSPORT));
|
||||
assertEquals(OkHeaders.SELECTED_TRANSPORT, headers.name(0));
|
||||
assertEquals("spdy/3", headers.value(0));
|
||||
assertEquals("cache-control", headers.name(1));
|
||||
assertEquals("no-cache, no-store", headers.value(1));
|
||||
|
||||
@@ -233,14 +233,10 @@ public final class HttpResponseCacheTest {
|
||||
@Override public CacheRequest put(Response response) throws IOException {
|
||||
assertEquals(server.getUrl("/"), response.request().url());
|
||||
assertEquals(200, response.code());
|
||||
assertEquals(body.length(), response.body().contentLength());
|
||||
assertEquals("text/plain", response.body().contentType().toString());
|
||||
assertNull(response.body());
|
||||
assertEquals("5", response.header("Content-Length"));
|
||||
assertEquals("text/plain", response.header("Content-Type"));
|
||||
assertEquals("ijk", response.header("fgh"));
|
||||
try {
|
||||
response.body().byteStream(); // the RI doesn't forbid this, but it should
|
||||
fail();
|
||||
} catch (IllegalStateException expected) {
|
||||
}
|
||||
cacheCount.incrementAndGet();
|
||||
return null;
|
||||
}
|
||||
@@ -1696,7 +1692,7 @@ public final class HttpResponseCacheTest {
|
||||
connection.addRequestProperty("Cache-Control", "only-if-cached");
|
||||
assertEquals("A", readAscii(connection));
|
||||
|
||||
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
|
||||
String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
|
||||
assertEquals(ResponseSource.CACHE + " 200", source);
|
||||
}
|
||||
|
||||
@@ -1713,7 +1709,7 @@ public final class HttpResponseCacheTest {
|
||||
HttpURLConnection connection = openConnection(server.getUrl("/"));
|
||||
assertEquals("B", readAscii(connection));
|
||||
|
||||
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
|
||||
String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
|
||||
assertEquals(ResponseSource.CONDITIONAL_CACHE + " 200", source);
|
||||
}
|
||||
|
||||
@@ -1728,7 +1724,7 @@ public final class HttpResponseCacheTest {
|
||||
HttpURLConnection connection = openConnection(server.getUrl("/"));
|
||||
assertEquals("A", readAscii(connection));
|
||||
|
||||
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
|
||||
String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
|
||||
assertEquals(ResponseSource.CONDITIONAL_CACHE + " 304", source);
|
||||
}
|
||||
|
||||
@@ -1739,7 +1735,7 @@ public final class HttpResponseCacheTest {
|
||||
URLConnection connection = openConnection(server.getUrl("/"));
|
||||
assertEquals("A", readAscii(connection));
|
||||
|
||||
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
|
||||
String source = connection.getHeaderField(OkHeaders.RESPONSE_SOURCE);
|
||||
assertEquals(ResponseSource.NETWORK + " 200", source);
|
||||
}
|
||||
|
||||
@@ -1956,7 +1952,7 @@ public final class HttpResponseCacheTest {
|
||||
assertEquals(504, connection.getResponseCode());
|
||||
assertEquals(-1, connection.getErrorStream().read());
|
||||
assertEquals(ResponseSource.NONE + " 504",
|
||||
connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE));
|
||||
connection.getHeaderField(OkHeaders.RESPONSE_SOURCE));
|
||||
}
|
||||
|
||||
enum TransferKind {
|
||||
|
||||
Reference in New Issue
Block a user