1/*
2 * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package sun.net;
26
27import java.io.*;
28import java.net.Socket;
29import java.net.InetAddress;
30import java.net.InetSocketAddress;
31import java.net.UnknownHostException;
32import java.net.Proxy;
33import java.util.Arrays;
34import java.security.AccessController;
35import java.security.PrivilegedAction;
36
37/**
38 * This is the base class for network clients.
39 *
40 * @author      Jonathan Payne
41 */
42public class NetworkClient {
43    /* Default value of read timeout, if not specified (infinity) */
44    public static final int DEFAULT_READ_TIMEOUT = -1;
45
46    /* Default value of connect timeout, if not specified (infinity) */
47    public static final int DEFAULT_CONNECT_TIMEOUT = -1;
48
49    protected Proxy     proxy = Proxy.NO_PROXY;
50    /** Socket for communicating with server. */
51    protected Socket    serverSocket = null;
52
53    /** Stream for printing to the server. */
54    public PrintStream  serverOutput;
55
56    /** Buffered stream for reading replies from server. */
57    public InputStream  serverInput;
58
59    protected static int defaultSoTimeout;
60    protected static int defaultConnectTimeout;
61
62    protected int readTimeout = DEFAULT_READ_TIMEOUT;
63    protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
64    /* Name of encoding to use for output */
65    protected static String encoding;
66
67    static {
68        final int vals[] = {0, 0};
69        final String encs[] = { null };
70
71        AccessController.doPrivileged(
72                new PrivilegedAction<>() {
73                    public Void run() {
74                        vals[0] = Integer.getInteger("sun.net.client.defaultReadTimeout", 0).intValue();
75                        vals[1] = Integer.getInteger("sun.net.client.defaultConnectTimeout", 0).intValue();
76                        encs[0] = System.getProperty("file.encoding", "ISO8859_1");
77                        return null;
78            }
79        });
80        if (vals[0] != 0) {
81            defaultSoTimeout = vals[0];
82        }
83        if (vals[1] != 0) {
84            defaultConnectTimeout = vals[1];
85        }
86
87        encoding = encs[0];
88        try {
89            if (!isASCIISuperset (encoding)) {
90                encoding = "ISO8859_1";
91            }
92        } catch (Exception e) {
93            encoding = "ISO8859_1";
94        }
95    }
96
97
98    /**
99     * Test the named character encoding to verify that it converts ASCII
100     * characters correctly. We have to use an ASCII based encoding, or else
101     * the NetworkClients will not work correctly in EBCDIC based systems.
102     * However, we cannot just use ASCII or ISO8859_1 universally, because in
103     * Asian locales, non-ASCII characters may be embedded in otherwise
104     * ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398)
105     * are a little ambiguous in this matter. For instance, RFC2398 [part 2.1]
106     * says that the HTTP request URI should be escaped using a defined
107     * mechanism, but there is no way to specify in the escaped string what
108     * the original character set is. It is not correct to assume that
109     * UTF-8 is always used (as in URLs in HTML 4.0).  For this reason,
110     * until the specifications are updated to deal with this issue more
111     * comprehensively, and more importantly, HTTP servers are known to
112     * support these mechanisms, we will maintain the current behavior
113     * where it is possible to send non-ASCII characters in their original
114     * unescaped form.
115     */
116    private static boolean isASCIISuperset (String encoding) throws Exception {
117        String chkS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"+
118                        "abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,";
119
120        // Expected byte sequence for string above
121        byte[] chkB = { 48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,71,72,
122                73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,
123                100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,
124                115,116,117,118,119,120,121,122,45,95,46,33,126,42,39,40,41,59,
125                47,63,58,64,38,61,43,36,44};
126
127        byte[] b = chkS.getBytes (encoding);
128        return Arrays.equals (b, chkB);
129    }
130
131    /** Open a connection to the server. */
132    public void openServer(String server, int port)
133        throws IOException, UnknownHostException {
134        if (serverSocket != null)
135            closeServer();
136        serverSocket = doConnect (server, port);
137        try {
138            serverOutput = new PrintStream(new BufferedOutputStream(
139                                        serverSocket.getOutputStream()),
140                                        true, encoding);
141        } catch (UnsupportedEncodingException e) {
142            throw new InternalError(encoding +"encoding not found", e);
143        }
144        serverInput = new BufferedInputStream(serverSocket.getInputStream());
145    }
146
147    /**
148     * Return a socket connected to the server, with any
149     * appropriate options pre-established
150     */
151    protected Socket doConnect (String server, int port)
152    throws IOException, UnknownHostException {
153        Socket s;
154        if (proxy != null) {
155            if (proxy.type() == Proxy.Type.SOCKS) {
156                s = AccessController.doPrivileged(
157                    new PrivilegedAction<>() {
158                        public Socket run() {
159                                       return new Socket(proxy);
160                                   }});
161            } else if (proxy.type() == Proxy.Type.DIRECT) {
162                s = createSocket();
163            } else {
164                // Still connecting through a proxy
165                // server & port will be the proxy address and port
166                s = new Socket(Proxy.NO_PROXY);
167            }
168        } else {
169            s = createSocket();
170        }
171
172        // Instance specific timeouts do have priority, that means
173        // connectTimeout & readTimeout (-1 means not set)
174        // Then global default timeouts
175        // Then no timeout.
176        if (connectTimeout >= 0) {
177            s.connect(new InetSocketAddress(server, port), connectTimeout);
178        } else {
179            if (defaultConnectTimeout > 0) {
180                s.connect(new InetSocketAddress(server, port), defaultConnectTimeout);
181            } else {
182                s.connect(new InetSocketAddress(server, port));
183            }
184        }
185        if (readTimeout >= 0)
186            s.setSoTimeout(readTimeout);
187        else if (defaultSoTimeout > 0) {
188            s.setSoTimeout(defaultSoTimeout);
189        }
190        return s;
191    }
192
193    /**
194     * The following method, createSocket, is provided to allow the
195     * https client to override it so that it may use its socket factory
196     * to create the socket.
197     */
198    protected Socket createSocket() throws IOException {
199        return new java.net.Socket(Proxy.NO_PROXY);  // direct connection
200    }
201
202    protected InetAddress getLocalAddress() throws IOException {
203        if (serverSocket == null)
204            throw new IOException("not connected");
205        return  AccessController.doPrivileged(
206                        new PrivilegedAction<>() {
207                            public InetAddress run() {
208                                return serverSocket.getLocalAddress();
209
210                            }
211                        });
212    }
213
214    /** Close an open connection to the server. */
215    public void closeServer() throws IOException {
216        if (! serverIsOpen()) {
217            return;
218        }
219        serverSocket.close();
220        serverSocket = null;
221        serverInput = null;
222        serverOutput = null;
223    }
224
225    /** Return server connection status */
226    public boolean serverIsOpen() {
227        return serverSocket != null;
228    }
229
230    /** Create connection with host <i>host</i> on port <i>port</i> */
231    public NetworkClient(String host, int port) throws IOException {
232        openServer(host, port);
233    }
234
235    public NetworkClient() {}
236
237    public void setConnectTimeout(int timeout) {
238        connectTimeout = timeout;
239    }
240
241    public int getConnectTimeout() {
242        return connectTimeout;
243    }
244
245    /**
246     * Sets the read timeout.
247     *
248     * Note: Public URLConnection (and protocol specific implementations)
249     * protect against negative timeout values being set. This implementation,
250     * and protocol specific implementations, use -1 to represent the default
251     * read timeout.
252     *
253     * This method may be invoked with the default timeout value when the
254     * protocol handler is trying to reset the timeout after doing a
255     * potentially blocking internal operation, e.g. cleaning up unread
256     * response data, buffering error stream response data, etc
257     */
258    public void setReadTimeout(int timeout) {
259        if (timeout == DEFAULT_READ_TIMEOUT)
260            timeout = defaultSoTimeout;
261
262        if (serverSocket != null && timeout >= 0) {
263            try {
264                serverSocket.setSoTimeout(timeout);
265            } catch(IOException e) {
266                // We tried...
267            }
268        }
269        readTimeout = timeout;
270    }
271
272    public int getReadTimeout() {
273        return readTimeout;
274    }
275}
276