16f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org/*
26f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * Copyright (C) 2013 Square, Inc.
36f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org *
46f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * Licensed under the Apache License, Version 2.0 (the "License");
56f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * you may not use this file except in compliance with the License.
66f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * You may obtain a copy of the License at
76f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org *
86f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org *      http://www.apache.org/licenses/LICENSE-2.0
96f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org *
106f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * Unless required by applicable law or agreed to in writing, software
116f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * distributed under the License is distributed on an "AS IS" BASIS,
126f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * See the License for the specific language governing permissions and
146f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * limitations under the License.
156f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org */
166f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgpackage com.squareup.okhttp;
176f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
186f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport com.squareup.okhttp.internal.Platform;
196f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport com.squareup.okhttp.internal.Util;
206f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.io.File;
216f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.io.FileInputStream;
226f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.io.IOException;
236f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.io.InputStream;
246f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.io.UnsupportedEncodingException;
256f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.net.MalformedURLException;
266f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.net.URI;
276f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.net.URISyntaxException;
286f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.net.URL;
296f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport java.util.List;
306f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgimport okio.BufferedSink;
316f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
326f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org/**
336f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * An HTTP request. Instances of this class are immutable if their {@link #body}
346f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org * is null or itself immutable.
356f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org */
366f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.orgpublic final class Request {
376f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private final URL url;
386f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private final String method;
396f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private final Headers headers;
406f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private final Body body;
416f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private final Object tag;
426f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
436f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private volatile ParsedHeaders parsedHeaders; // Lazily initialized.
446f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private volatile URI uri; // Lazily initialized.
456f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private volatile CacheControl cacheControl; // Lazily initialized.
466f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
476f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private Request(Builder builder) {
486f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    this.url = builder.url;
496f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    this.method = builder.method;
506f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    this.headers = builder.headers.build();
516f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    this.body = builder.body;
526f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    this.tag = builder.tag != null ? builder.tag : this;
536f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
546f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
556f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public URL url() {
566f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return url;
576f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
586f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
596f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public URI uri() throws IOException {
606f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    try {
616f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      URI result = uri;
626f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return result != null ? result : (uri = Platform.get().toUriLenient(url));
636f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    } catch (URISyntaxException e) {
646f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      throw new IOException(e.getMessage());
656f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
666f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
676f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
686f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public String urlString() {
696f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return url.toString();
706f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
716f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
726f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public String method() {
736f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return method;
746f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
756f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
766f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public Headers headers() {
776f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return headers;
786f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
796f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
806f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public String header(String name) {
816f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return headers.get(name);
826f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
836f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
846f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public List<String> headers(String name) {
856f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return headers.values(name);
866f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
876f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
886f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public Body body() {
896f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return body;
906f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
916f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
926f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public Object tag() {
936f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return tag;
946f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
956f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
966f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public Builder newBuilder() {
976f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return new Builder(this);
986f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
996f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1006f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public Headers getHeaders() {
1016f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return headers;
1026f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
1036f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1046f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public String getUserAgent() {
1056f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return parsedHeaders().userAgent;
1066f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
1076f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1086f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public String getProxyAuthorization() {
1096f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return parsedHeaders().proxyAuthorization;
1106f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
1116f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1126f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private ParsedHeaders parsedHeaders() {
1136f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    ParsedHeaders result = parsedHeaders;
1146f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return result != null ? result : (parsedHeaders = new ParsedHeaders(headers));
1156f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
1166f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1176f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  /**
1186f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org   * Returns the cache control directives for this response. This is never null,
1196f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org   * even if this response contains no {@code Cache-Control} header.
1206f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org   */
1216f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public CacheControl cacheControl() {
1226f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    CacheControl result = cacheControl;
1236f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return result != null ? result : (cacheControl = CacheControl.parse(headers));
1246f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
1256f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1266f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public boolean isHttps() {
1276f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    return url().getProtocol().equals("https");
1286f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
1296f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1306f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  /** Parsed request headers, computed on-demand and cached. */
1316f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  private static class ParsedHeaders {
1326f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private String userAgent;
1336f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private String proxyAuthorization;
1346f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1356f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public ParsedHeaders(Headers headers) {
1366f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      for (int i = 0; i < headers.size(); i++) {
1376f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        String fieldName = headers.name(i);
1386f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        String value = headers.value(i);
1396f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        if ("User-Agent".equalsIgnoreCase(fieldName)) {
1406f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          userAgent = value;
1416f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        } else if ("Proxy-Authorization".equalsIgnoreCase(fieldName)) {
1426f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          proxyAuthorization = value;
1436f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        }
1446f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      }
1456f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
1466f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
1476f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1486f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public abstract static class Body {
1496f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /** Returns the Content-Type header for this body. */
1506f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public abstract MediaType contentType();
1516f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1526f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /**
1536f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * Returns the number of bytes that will be written to {@code out} in a call
1546f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * to {@link #writeTo}, or -1 if that count is unknown.
1556f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     */
1566f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public long contentLength() {
1576f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return -1;
1586f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
1596f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1606f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /** Writes the content of this request to {@code out}. */
1616f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public abstract void writeTo(BufferedSink sink) throws IOException;
1626f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1636f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /**
1646f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * Returns a new request body that transmits {@code content}. If {@code
1656f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * contentType} lacks a charset, this will use UTF-8.
1666f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     */
1676f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public static Body create(MediaType contentType, String content) {
1686f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      contentType = contentType.charset() != null
1696f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          ? contentType
1706f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          : MediaType.parse(contentType + "; charset=utf-8");
1716f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      try {
1726f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        byte[] bytes = content.getBytes(contentType.charset().name());
1736f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        return create(contentType, bytes);
1746f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      } catch (UnsupportedEncodingException e) {
1756f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        throw new AssertionError();
1766f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      }
1776f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
1786f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1796f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /** Returns a new request body that transmits {@code content}. */
1806f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public static Body create(final MediaType contentType, final byte[] content) {
1816f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      if (contentType == null) throw new NullPointerException("contentType == null");
1826f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      if (content == null) throw new NullPointerException("content == null");
1836f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1846f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return new Body() {
1856f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        @Override public MediaType contentType() {
1866f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          return contentType;
1876f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        }
1886f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1896f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        @Override public long contentLength() {
1906f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          return content.length;
1916f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        }
1926f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1936f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        @Override public void writeTo(BufferedSink sink) throws IOException {
1946f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          sink.write(content);
1956f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        }
1966f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      };
1976f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
1986f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
1996f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /** Returns a new request body that transmits the content of {@code file}. */
2006f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public static Body create(final MediaType contentType, final File file) {
2016f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      if (contentType == null) throw new NullPointerException("contentType == null");
2026f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      if (file == null) throw new NullPointerException("content == null");
2036f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2046f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return new Body() {
2056f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        @Override public MediaType contentType() {
2066f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          return contentType;
2076f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        }
2086f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2096f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        @Override public long contentLength() {
2106f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          return file.length();
2116f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        }
2126f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2136f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        @Override public void writeTo(BufferedSink sink) throws IOException {
2146f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          long length = contentLength();
2156f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          if (length == 0) return;
2166f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2176f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          InputStream in = null;
2186f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          try {
2196f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org            in = new FileInputStream(file);
2206f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org            byte[] buffer = new byte[(int) Math.min(8192, length)];
2216f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org            for (int c; (c = in.read(buffer)) != -1; ) {
2226f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org              sink.write(buffer, 0, c);
2236f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org            }
2246f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          } finally {
2256f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org            Util.closeQuietly(in);
2266f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org          }
2276f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        }
2286f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      };
2296f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2306f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
2316f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2326f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  public static class Builder {
2336f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private URL url;
2346f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private String method;
2356f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private Headers.Builder headers;
2366f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private Body body;
2376f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private Object tag;
2386f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2396f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder() {
2406f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.method = "GET";
2416f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.headers = new Headers.Builder();
2426f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2436f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2446f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    private Builder(Request request) {
2456f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.url = request.url;
2466f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.method = request.method;
2476f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.body = request.body;
2486f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.tag = request.tag;
2496f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.headers = request.headers.newBuilder();
2506f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2516f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2526f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder url(String url) {
2536f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      try {
2546f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        return url(new URL(url));
2556f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      } catch (MalformedURLException e) {
2566f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        throw new IllegalArgumentException("Malformed URL: " + url);
2576f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      }
2586f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2596f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2606f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder url(URL url) {
2616f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      if (url == null) throw new IllegalArgumentException("url == null");
2626f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.url = url;
2636f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return this;
2646f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2656f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2666f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /**
2676f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * Sets the header named {@code name} to {@code value}. If this request
2686f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * already has any headers with that name, they are all replaced.
2696f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     */
2706f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder header(String name, String value) {
2716f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      headers.set(name, value);
2726f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return this;
2736f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2746f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2756f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /**
2766f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * Adds a header with {@code name} and {@code value}. Prefer this method for
2776f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * multiply-valued headers like "Cookie".
2786f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     */
2796f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder addHeader(String name, String value) {
2806f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      headers.add(name, value);
2816f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return this;
2826f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2836f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2846f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder removeHeader(String name) {
2856f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      headers.removeAll(name);
2866f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return this;
2876f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2886f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2896f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /** Removes all headers on this builder and adds {@code headers}. */
2906f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder headers(Headers headers) {
2916f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.headers = headers.newBuilder();
2926f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return this;
2936f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2946f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2956f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder setUserAgent(String userAgent) {
2966f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return header("User-Agent", userAgent);
2976f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
2986f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
2996f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder get() {
3006f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return method("GET", null);
3016f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
3026f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
3036f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder head() {
3046f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return method("HEAD", null);
3056f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
3066f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
3076f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder post(Body body) {
3086f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return method("POST", body);
3096f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
3106f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
3116f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder put(Body body) {
3126f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return method("PUT", body);
3136f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
3146f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
3156f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder method(String method, Body body) {
3166f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      if (method == null || method.length() == 0) {
3176f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org        throw new IllegalArgumentException("method == null || method.length() == 0");
3186f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      }
3196f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.method = method;
3206f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.body = body;
3216f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return this;
3226f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
3236f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
3246f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    /**
3256f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * Attaches {@code tag} to the request. It can be used later to cancel the
3266f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * request. If the tag is unspecified or null, the request is canceled by
3276f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     * using the request itself as the tag.
3286f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org     */
3296f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Builder tag(Object tag) {
3306f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      this.tag = tag;
3316f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return this;
3326f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
3336f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org
3346f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    public Request build() {
3356f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      if (url == null) throw new IllegalStateException("url == null");
3366f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org      return new Request(this);
3376f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org    }
3386f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org  }
3396f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org}
3406f31ac30b9092fd02a8c97e5216cf53f3e4fae4jshin@chromium.org