1package com.squareup.okhttp;
2
3import com.squareup.okhttp.internal.Platform;
4import com.squareup.okhttp.internal.URLFilter;
5import com.squareup.okhttp.internal.io.FileSystem;
6import com.squareup.okhttp.internal.io.InMemoryFileSystem;
7import com.squareup.okhttp.mockwebserver.MockResponse;
8import com.squareup.okhttp.mockwebserver.MockWebServer;
9import java.io.File;
10import java.io.IOException;
11import java.net.HttpURLConnection;
12import java.net.URL;
13import java.text.DateFormat;
14import java.text.SimpleDateFormat;
15import java.util.Date;
16import java.util.Locale;
17import java.util.TimeZone;
18import java.util.concurrent.TimeUnit;
19import org.junit.Before;
20import org.junit.Rule;
21import org.junit.Test;
22
23import static java.nio.charset.StandardCharsets.US_ASCII;
24import static okio.Okio.buffer;
25import static okio.Okio.source;
26import static org.junit.Assert.assertEquals;
27import static org.junit.Assert.fail;
28
29public class OkUrlFactoryTest {
30  @Rule public MockWebServer server = new MockWebServer();
31
32  private FileSystem fileSystem = new InMemoryFileSystem();
33  private OkUrlFactory factory;
34
35  @Before public void setUp() throws IOException {
36    OkHttpClient client = new OkHttpClient();
37    client.setCache(new Cache(new File("/cache/"), 10 * 1024 * 1024, fileSystem));
38    factory = new OkUrlFactory(client);
39  }
40
41  /**
42   * Response code 407 should only come from proxy servers. Android's client
43   * throws if it is sent by an origin server.
44   */
45  @Test public void originServerSends407() throws Exception {
46    server.enqueue(new MockResponse().setResponseCode(407));
47
48    HttpURLConnection conn = factory.open(server.getUrl("/"));
49    try {
50      conn.getResponseCode();
51      fail();
52    } catch (IOException ignored) {
53    }
54  }
55
56  @Test public void networkResponseSourceHeader() throws Exception {
57    server.enqueue(new MockResponse().setBody("Isla Sorna"));
58
59    HttpURLConnection connection = factory.open(server.getUrl("/"));
60    assertResponseHeader(connection, "NETWORK 200");
61    assertResponseBody(connection, "Isla Sorna");
62  }
63
64  @Test public void networkFailureResponseSourceHeader() throws Exception {
65    server.enqueue(new MockResponse().setResponseCode(404));
66
67    HttpURLConnection connection = factory.open(server.getUrl("/"));
68    assertResponseHeader(connection, "NETWORK 404");
69  }
70
71  @Test public void conditionalCacheHitResponseSourceHeaders() throws Exception {
72    server.enqueue(new MockResponse()
73        .addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
74        .addHeader("Cache-Control: max-age=0")
75        .setBody("Isla Nublar"));
76    server.enqueue(new MockResponse().setResponseCode(304));
77
78    HttpURLConnection connection1 = factory.open(server.getUrl("/"));
79    assertResponseHeader(connection1, "NETWORK 200");
80    assertResponseBody(connection1, "Isla Nublar");
81
82    HttpURLConnection connection2 = factory.open(server.getUrl("/"));
83    assertResponseHeader(connection2, "CONDITIONAL_CACHE 304");
84    assertResponseBody(connection2, "Isla Nublar");
85  }
86
87  @Test public void conditionalCacheMissResponseSourceHeaders() throws Exception {
88    server.enqueue(new MockResponse()
89        .addHeader("Last-Modified: " + formatDate(0, TimeUnit.SECONDS))
90        .addHeader("Cache-Control: max-age=0")
91        .setBody("Isla Nublar"));
92    server.enqueue(new MockResponse().setBody("Isla Sorna"));
93
94    HttpURLConnection connection1 = factory.open(server.getUrl("/"));
95    assertResponseHeader(connection1, "NETWORK 200");
96    assertResponseBody(connection1, "Isla Nublar");
97
98    HttpURLConnection connection2 = factory.open(server.getUrl("/"));
99    assertResponseHeader(connection2, "CONDITIONAL_CACHE 200");
100    assertResponseBody(connection2, "Isla Sorna");
101  }
102
103  @Test public void cacheResponseSourceHeaders() throws Exception {
104    server.enqueue(new MockResponse()
105        .addHeader("Expires: " + formatDate(2, TimeUnit.HOURS))
106        .setBody("Isla Nublar"));
107
108    HttpURLConnection connection1 = factory.open(server.getUrl("/"));
109    assertResponseHeader(connection1, "NETWORK 200");
110    assertResponseBody(connection1, "Isla Nublar");
111
112    HttpURLConnection connection2 = factory.open(server.getUrl("/"));
113    assertResponseHeader(connection2, "CACHE 200");
114    assertResponseBody(connection2, "Isla Nublar");
115  }
116
117  @Test public void noneResponseSourceHeaders() throws Exception {
118    server.enqueue(new MockResponse().setBody("Isla Nublar"));
119
120    HttpURLConnection connection1 = factory.open(server.getUrl("/"));
121    assertResponseHeader(connection1, "NETWORK 200");
122    assertResponseBody(connection1, "Isla Nublar");
123
124    HttpURLConnection connection2 = factory.open(server.getUrl("/"));
125    connection2.setRequestProperty("Cache-Control", "only-if-cached");
126    assertResponseHeader(connection2, "NONE");
127  }
128
129  @Test
130  public void setInstanceFollowRedirectsFalse() throws Exception {
131    server.enqueue(new MockResponse()
132        .setResponseCode(302)
133        .addHeader("Location: /b")
134        .setBody("A"));
135    server.enqueue(new MockResponse()
136        .setBody("B"));
137
138    HttpURLConnection connection = factory.open(server.getUrl("/a"));
139    connection.setInstanceFollowRedirects(false);
140    assertResponseBody(connection, "A");
141    assertResponseCode(connection, 302);
142  }
143
144  @Test
145  public void testURLFilter() throws Exception {
146    server.enqueue(new MockResponse()
147        .setBody("B"));
148    final URL blockedURL = server.url("/a").url();
149    factory.setUrlFilter(new URLFilter() {
150      @Override
151      public void checkURLPermitted(URL url) throws IOException {
152        if (blockedURL.equals(url)) {
153          throw new IOException("Blocked");
154        }
155      }
156    });
157    try {
158      HttpURLConnection connection = factory.open(server.url("/a").url());
159      connection.getInputStream();
160      fail("Connection was successful");
161    } catch (IOException e) {
162      assertEquals("Blocked", e.getMessage());
163    }
164    HttpURLConnection connection = factory.open(server.url("/b").url());
165    assertResponseBody(connection, "B");
166  }
167
168  private void assertResponseBody(HttpURLConnection connection, String expected) throws Exception {
169    String actual = buffer(source(connection.getInputStream())).readString(US_ASCII);
170    assertEquals(expected, actual);
171  }
172
173  private void assertResponseHeader(HttpURLConnection connection, String expected) {
174    final String headerFieldPrefix = Platform.get().getPrefix();
175    assertEquals(expected, connection.getHeaderField(headerFieldPrefix + "-Response-Source"));
176  }
177
178  private void assertResponseCode(HttpURLConnection connection, int expected) throws IOException {
179    assertEquals(expected, connection.getResponseCode());
180  }
181
182  private static String formatDate(long delta, TimeUnit timeUnit) {
183    return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
184  }
185
186  private static String formatDate(Date date) {
187    DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
188    rfc1123.setTimeZone(TimeZone.getTimeZone("GMT"));
189    return rfc1123.format(date);
190  }
191}
192