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 */
17package org.apache.harmony.luni.internal.net.www.protocol.https;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.OutputStream;
22import java.net.ProtocolException;
23import java.net.Proxy;
24import java.net.URL;
25import java.security.Permission;
26import java.security.Principal;
27import java.security.cert.Certificate;
28import java.util.List;
29import java.util.Map;
30
31import javax.net.ssl.HttpsURLConnection;
32import javax.net.ssl.SSLPeerUnverifiedException;
33import javax.net.ssl.SSLSocket;
34
35import org.apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl;
36import org.apache.harmony.luni.internal.nls.Messages;
37
38/**
39 * HttpsURLConnection implementation.
40 */
41public class HttpsURLConnectionImpl extends HttpsURLConnection {
42
43    // Https engine to be wrapped
44    private final HttpsEngine httpsEngine;
45
46    // SSLSocket to be used for connection
47    private SSLSocket sslSocket;
48
49    protected HttpsURLConnectionImpl(URL url, int port) {
50        super(url);
51        httpsEngine = new HttpsEngine(url, port);
52    }
53
54    protected HttpsURLConnectionImpl(URL url, int port, Proxy proxy) {
55        super(url);
56        httpsEngine = new HttpsEngine(url, port, proxy);
57    }
58
59    @Override
60    public String getCipherSuite() {
61        if (sslSocket == null) {
62            throw new IllegalStateException(Messages.getString("luni.00")); //$NON-NLS-1$
63        }
64        return sslSocket.getSession().getCipherSuite();
65    }
66
67    @Override
68    public Certificate[] getLocalCertificates() {
69        if (sslSocket == null) {
70            throw new IllegalStateException(Messages.getString("luni.00")); //$NON-NLS-1$
71        }
72        return sslSocket.getSession().getLocalCertificates();
73    }
74
75    @Override
76    public Certificate[] getServerCertificates()
77            throws SSLPeerUnverifiedException {
78        if (sslSocket == null) {
79            throw new IllegalStateException(Messages.getString("luni.00")); //$NON-NLS-1$
80        }
81        return sslSocket.getSession().getPeerCertificates();
82    }
83
84    @Override
85    public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
86        if (sslSocket == null) {
87            throw new IllegalStateException(Messages.getString("luni.00")); //$NON-NLS-1$
88        }
89        return sslSocket.getSession().getPeerPrincipal();
90    }
91
92    @Override
93    public Principal getLocalPrincipal() {
94        if (sslSocket == null) {
95            throw new IllegalStateException(Messages.getString("luni.00")); //$NON-NLS-1$
96        }
97        return sslSocket.getSession().getLocalPrincipal();
98    }
99
100    @Override
101    public void disconnect() {
102        httpsEngine.disconnect();
103    }
104
105    @Override
106    public InputStream getErrorStream() {
107        return httpsEngine.getErrorStream();
108    }
109
110    @Override
111    public String getRequestMethod() {
112        return httpsEngine.getRequestMethod();
113    }
114
115    @Override
116    public int getResponseCode() throws IOException {
117        return httpsEngine.getResponseCode();
118    }
119
120    @Override
121    public String getResponseMessage() throws IOException {
122        return httpsEngine.getResponseMessage();
123    }
124
125    @Override
126    public void setRequestMethod(String method) throws ProtocolException {
127        httpsEngine.setRequestMethod(method);
128    }
129
130    @Override
131    public boolean usingProxy() {
132        return httpsEngine.usingProxy();
133    }
134
135    @Override
136    public boolean getInstanceFollowRedirects() {
137        return httpsEngine.getInstanceFollowRedirects();
138    }
139
140    @Override
141    public void setInstanceFollowRedirects(boolean followRedirects) {
142        httpsEngine.setInstanceFollowRedirects(followRedirects);
143    }
144
145    @Override
146    public void connect() throws IOException {
147        httpsEngine.connect();
148    }
149
150    @Override
151    public boolean getAllowUserInteraction() {
152        return httpsEngine.getAllowUserInteraction();
153    }
154
155    @Override
156    public Object getContent() throws IOException {
157        return httpsEngine.getContent();
158    }
159
160    @SuppressWarnings("unchecked") // Spec does not generify
161    @Override
162    public Object getContent(Class[] types) throws IOException {
163        return httpsEngine.getContent(types);
164    }
165
166    @Override
167    public String getContentEncoding() {
168        return httpsEngine.getContentEncoding();
169    }
170
171    @Override
172    public int getContentLength() {
173        return httpsEngine.getContentLength();
174    }
175
176    @Override
177    public String getContentType() {
178        return httpsEngine.getContentType();
179    }
180
181    @Override
182    public long getDate() {
183        return httpsEngine.getDate();
184    }
185
186    @Override
187    public boolean getDefaultUseCaches() {
188        return httpsEngine.getDefaultUseCaches();
189    }
190
191    @Override
192    public boolean getDoInput() {
193        return httpsEngine.getDoInput();
194    }
195
196    @Override
197    public boolean getDoOutput() {
198        return httpsEngine.getDoOutput();
199    }
200
201    @Override
202    public long getExpiration() {
203        return httpsEngine.getExpiration();
204    }
205
206    @Override
207    public String getHeaderField(int pos) {
208        return httpsEngine.getHeaderField(pos);
209    }
210
211    @Override
212    public Map<String, List<String>> getHeaderFields() {
213        return httpsEngine.getHeaderFields();
214    }
215
216    @Override
217    public Map<String, List<String>> getRequestProperties() {
218        return httpsEngine.getRequestProperties();
219    }
220
221    @Override
222    public void addRequestProperty(String field, String newValue) {
223        httpsEngine.addRequestProperty(field, newValue);
224    }
225
226    @Override
227    public String getHeaderField(String key) {
228        return httpsEngine.getHeaderField(key);
229    }
230
231    @Override
232    public long getHeaderFieldDate(String field, long defaultValue) {
233        return httpsEngine.getHeaderFieldDate(field, defaultValue);
234    }
235
236    @Override
237    public int getHeaderFieldInt(String field, int defaultValue) {
238        return httpsEngine.getHeaderFieldInt(field, defaultValue);
239    }
240
241    @Override
242    public String getHeaderFieldKey(int posn) {
243        return httpsEngine.getHeaderFieldKey(posn);
244    }
245
246    @Override
247    public long getIfModifiedSince() {
248        return httpsEngine.getIfModifiedSince();
249    }
250
251    @Override
252    public InputStream getInputStream() throws IOException {
253        return httpsEngine.getInputStream();
254    }
255
256    @Override
257    public long getLastModified() {
258        return httpsEngine.getLastModified();
259    }
260
261    @Override
262    public OutputStream getOutputStream() throws IOException {
263        return httpsEngine.getOutputStream();
264    }
265
266    @Override
267    public Permission getPermission() throws IOException {
268        return httpsEngine.getPermission();
269    }
270
271    @Override
272    public String getRequestProperty(String field) {
273        return httpsEngine.getRequestProperty(field);
274    }
275
276    @Override
277    public URL getURL() {
278        return httpsEngine.getURL();
279    }
280
281    @Override
282    public boolean getUseCaches() {
283        return httpsEngine.getUseCaches();
284    }
285
286    @Override
287    public void setAllowUserInteraction(boolean newValue) {
288        httpsEngine.setAllowUserInteraction(newValue);
289    }
290
291    @Override
292    public void setDefaultUseCaches(boolean newValue) {
293        httpsEngine.setDefaultUseCaches(newValue);
294    }
295
296    @Override
297    public void setDoInput(boolean newValue) {
298        httpsEngine.setDoInput(newValue);
299    }
300
301    @Override
302    public void setDoOutput(boolean newValue) {
303        httpsEngine.setDoOutput(newValue);
304    }
305
306    @Override
307    public void setIfModifiedSince(long newValue) {
308        httpsEngine.setIfModifiedSince(newValue);
309    }
310
311    @Override
312    public void setRequestProperty(String field, String newValue) {
313        httpsEngine.setRequestProperty(field, newValue);
314    }
315
316    @Override
317    public void setUseCaches(boolean newValue) {
318        httpsEngine.setUseCaches(newValue);
319    }
320
321    @Override
322    public void setConnectTimeout(int timeout) {
323        httpsEngine.setConnectTimeout(timeout);
324    }
325
326    @Override
327    public int getConnectTimeout() {
328        return httpsEngine.getConnectTimeout();
329    }
330
331    @Override
332    public void setReadTimeout(int timeout) {
333        httpsEngine.setReadTimeout(timeout);
334    }
335
336    @Override
337    public int getReadTimeout() {
338        return httpsEngine.getReadTimeout();
339    }
340
341    @Override
342    public String toString() {
343        return httpsEngine.toString();
344    }
345
346    /**
347     * HttpsEngine
348     */
349    private class HttpsEngine extends HttpURLConnectionImpl {
350
351        // In case of using proxy this field indicates
352        // if it is a SSL Tunnel establishing stage
353        private boolean makingSSLTunnel;
354
355        protected HttpsEngine(URL url, int port) {
356            super(url, port);
357        }
358
359        protected HttpsEngine(URL url, int port, Proxy proxy) {
360            super(url, port, proxy);
361        }
362
363        @Override
364        public void connect() throws IOException {
365            if (connected) {
366                return;
367            }
368            if (super.usingProxy() && !makingSSLTunnel) {
369                // SSL Tunnel through the proxy was not established yet, do so
370                makingSSLTunnel = true;
371                // first - make the connection
372                super.connect();
373                // keep request method
374                String save_meth = method;
375                // make SSL Tunnel
376                method = "CONNECT"; //$NON-NLS-1$
377                try {
378                    doRequest();
379                    endRequest();
380                } finally {
381                    // restore initial request method
382                    method = save_meth;
383                }
384                if (!connected) {
385                    throw new IOException(Messages.getString("luni.01", //$NON-NLS-1$
386                            responseMessage, responseCode));
387                }
388                // if there are some remaining data in the stream - read it out
389                InputStream is = connection.getInputStream();
390                while (is.available() != 0) {
391                    is.read();
392                }
393                makingSSLTunnel = false;
394            } else {
395                // no need in SSL tunnel
396                super.connect();
397            }
398            if (!makingSSLTunnel) {
399                sslSocket = connection.getSecureSocket(getSSLSocketFactory(), getHostnameVerifier());
400                setUpTransportIO(connection);
401            }
402        }
403
404        @Override
405        protected String requestString() {
406            if (super.usingProxy()) {
407                if (makingSSLTunnel) {
408                    // we are making the SSL Tunneling, return remotehost:port
409                    int port = url.getPort();
410                    return (port > 0) ? url.getHost() + ":" + port //$NON-NLS-1$
411                    : url.getHost();
412                }
413                // we has made SSL Tunneling, return /requested.data
414                String file = url.getFile();
415                if (file == null || file.length() == 0) {
416                    file = "/"; //$NON-NLS-1$
417                }
418                return file;
419            }
420            return super.requestString();
421        }
422
423    }
424}
425