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