1b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/*
2b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Copyright 2008 Netflix, Inc.
3b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
4b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Licensed under the Apache License, Version 2.0 (the "License");
5b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * you may not use this file except in compliance with the License.
6b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * You may obtain a copy of the License at
7b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
8b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *     http://www.apache.org/licenses/LICENSE-2.0
9b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
10b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * Unless required by applicable law or agreed to in writing, software
11b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * distributed under the License is distributed on an "AS IS" BASIS,
12b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * See the License for the specific language governing permissions and
14b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * limitations under the License.
15b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */
16b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
17b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienpackage net.oauth.client;
18b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
19b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.IOException;
20b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.InputStream;
21b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.io.OutputStream;
22b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.HttpURLConnection;
23b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.URL;
24b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.net.URLConnection;
25b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Collection;
26b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.List;
27b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport java.util.Map;
28b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.http.HttpClient;
29b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.http.HttpMessage;
30b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienimport net.oauth.http.HttpResponseMessage;
31b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
32b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien/**
33b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * An HttpClient based on HttpURLConnection.
34b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * <p>
35b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * HttpClient3 or HttpClient4 perform better than this class, as a rule; since
36b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * they do things like connection pooling.  They also support reading the body
37b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * of an HTTP response whose status code isn't 200 (OK), which can enable your
38b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * application to handle problems better.
39b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien *
40b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @author John Kristian
41b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien * @hide
42b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien */
43b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembienpublic class URLConnectionClient implements HttpClient {
44b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
45b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    /** Send a message to the service provider and get the response. */
46b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    public HttpResponseMessage execute(HttpMessage request) throws IOException {
47b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final String httpMethod = request.method;
48b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final Collection<Map.Entry<String, String>> addHeaders = request.headers;
49b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final URL url = request.url;
50b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final URLConnection connection = url.openConnection();
51b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        connection.setDoInput(true);
52b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (connection instanceof HttpURLConnection) {
53b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            HttpURLConnection http = (HttpURLConnection) connection;
54b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            http.setRequestMethod(httpMethod);
55b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            http.setInstanceFollowRedirects(false);
56b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
57b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        StringBuilder headers = new StringBuilder(httpMethod);
58b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        {
59b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            headers.append(" ").append(url.getPath());
60b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            String query = url.getQuery();
61b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (query != null && query.length() > 0) {
62b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                headers.append("?").append(query);
63b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
64b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            headers.append(EOL);
65b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            for (Map.Entry<String, List<String>> header : connection
66b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    .getRequestProperties().entrySet()) {
67b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                String key = header.getKey();
68b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                for (String value : header.getValue()) {
69b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    headers.append(key).append(": ").append(value).append(EOL);
70b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                }
71b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
72b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
73b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        String contentLength = null;
74b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        for (Map.Entry<String, String> header : addHeaders) {
75b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            String key = header.getKey();
76b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            if (HttpMessage.CONTENT_LENGTH.equalsIgnoreCase(key)
77b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    && connection instanceof HttpURLConnection) {
78b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                contentLength = header.getValue();
79b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            } else {
80b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                connection.setRequestProperty(key, header.getValue());
81b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
82b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            headers.append(key).append(": ").append(header.getValue()).append(EOL);
83b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
84b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        byte[] excerpt = null;
85b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        final InputStream body = request.getBody();
86b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        if (body != null) {
87b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            try {
88b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                if (contentLength != null) {
89b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    ((HttpURLConnection) connection)
90b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    .setFixedLengthStreamingMode(Integer.parseInt(contentLength));
91b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                }
92b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                connection.setDoOutput(true);
93b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                OutputStream output = connection.getOutputStream();
94b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                try {
95b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    final ExcerptInputStream ex = new ExcerptInputStream(body);
96b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    byte[] b = new byte[1024];
97b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    for (int n; 0 < (n = ex.read(b));) {
98b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                        output.write(b, 0, n);
99b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    }
100b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    excerpt = ex.getExcerpt();
101b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                } finally {
102b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                    output.close();
103b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                }
104b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            } finally {
105b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien                body.close();
106b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien            }
107b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        }
108b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien        return new URLConnectionResponse(request, headers.toString(), excerpt, connection);
109b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    }
110b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
111b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien    private static final String EOL = HttpResponseMessage.EOL;
112b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien
113b852fcf48a8909164d7f323dd02a35d2a8056a61Nico Sallembien}
114