1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */

17 package okhttp3.internal.http;
18
19 import java.io.IOException;
20 import java.util.List;
21 import okhttp3.Cookie;
22 import okhttp3.CookieJar;
23 import okhttp3.Headers;
24 import okhttp3.Interceptor;
25 import okhttp3.MediaType;
26 import okhttp3.Request;
27 import okhttp3.RequestBody;
28 import okhttp3.Response;
29 import okhttp3.internal.Version;
30 import okio.GzipSource;
31 import okio.Okio;
32
33 import static okhttp3.internal.Util.hostHeader;
34
35 /**
36  * Bridges from application code to network code. First it builds a network request from a user
37  * request. Then it proceeds to call the network. Finally it builds a user response from the network
38  * response.
39  */

40 public final class BridgeInterceptor implements Interceptor {
41   private final CookieJar cookieJar;
42
43   public BridgeInterceptor(CookieJar cookieJar) {
44     this.cookieJar = cookieJar;
45   }
46
47   @Override public Response intercept(Chain chain) throws IOException {
48     Request userRequest = chain.request();
49     Request.Builder requestBuilder = userRequest.newBuilder();
50
51     RequestBody body = userRequest.body();
52     if (body != null) {
53       MediaType contentType = body.contentType();
54       if (contentType != null) {
55         requestBuilder.header("Content-Type", contentType.toString());
56       }
57
58       long contentLength = body.contentLength();
59       if (contentLength != -1) {
60         requestBuilder.header("Content-Length", Long.toString(contentLength));
61         requestBuilder.removeHeader("Transfer-Encoding");
62       } else {
63         requestBuilder.header("Transfer-Encoding""chunked");
64         requestBuilder.removeHeader("Content-Length");
65       }
66     }
67
68     if (userRequest.header("Host") == null) {
69       requestBuilder.header("Host", hostHeader(userRequest.url(), false));
70     }
71
72     if (userRequest.header("Connection") == null) {
73       requestBuilder.header("Connection""Keep-Alive");
74     }
75
76     // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
77     // the transfer stream.
78     boolean transparentGzip = false;
79     if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
80       transparentGzip = true;
81       requestBuilder.header("Accept-Encoding""gzip");
82     }
83
84     List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
85     if (!cookies.isEmpty()) {
86       requestBuilder.header("Cookie", cookieHeader(cookies));
87     }
88
89     if (userRequest.header("User-Agent") == null) {
90       requestBuilder.header("User-Agent", Version.userAgent());
91     }
92
93     Response networkResponse = chain.proceed(requestBuilder.build());
94
95     HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
96
97     Response.Builder responseBuilder = networkResponse.newBuilder()
98         .request(userRequest);
99
100     if (transparentGzip
101         && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
102         && HttpHeaders.hasBody(networkResponse)) {
103       GzipSource responseBody = new GzipSource(networkResponse.body().source());
104       Headers strippedHeaders = networkResponse.headers().newBuilder()
105           .removeAll("Content-Encoding")
106           .removeAll("Content-Length")
107           .build();
108       responseBuilder.headers(strippedHeaders);
109       String contentType = networkResponse.header("Content-Type");
110       responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
111     }
112
113     return responseBuilder.build();
114   }
115
116   /** Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}. */
117   private String cookieHeader(List<Cookie> cookies) {
118     StringBuilder cookieHeader = new StringBuilder();
119     for (int i = 0, size = cookies.size(); i < size; i++) {
120       if (i > 0) {
121         cookieHeader.append("; ");
122       }
123       Cookie cookie = cookies.get(i);
124       cookieHeader.append(cookie.name()).append('=').append(cookie.value());
125     }
126     return cookieHeader.toString();
127   }
128 }
129