1 /*
2 * Copyright 1994-2003 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package sun.net;
26
27 import java.io;
28 import java.net.Socket;
29 import java.net.InetAddress;
30 import java.net.InetSocketAddress;
31 import java.net.UnknownHostException;
32 import java.net.URL;
33 import java.net.Proxy;
34 import java.util.Arrays;
35 import java.security.AccessController;
36 import java.security.PrivilegedAction;
37
38 /**
39 * This is the base class for network clients.
40 *
41 * @author Jonathan Payne
42 */
43 public class NetworkClient {
44 protected Proxy proxy = Proxy.NO_PROXY;
45 /** Socket for communicating with server. */
46 protected Socket serverSocket = null;
47
48 /** Stream for printing to the server. */
49 public PrintStream serverOutput;
50
51 /** Buffered stream for reading replies from server. */
52 public InputStream serverInput;
53
54 protected static int defaultSoTimeout;
55 protected static int defaultConnectTimeout;
56
57 protected int readTimeout = -1;
58 protected int connectTimeout = -1;
59 /* Name of encoding to use for output */
60 protected static String encoding;
61
62 static {
63 final int vals[] = {0, 0};
64 final String encs[] = { null };
65
66 AccessController.doPrivileged(
67 new PrivilegedAction<Void>() {
68 public Void run() {
69 vals[0] = Integer.getInteger("sun.net.client.defaultReadTimeout", 0).intValue();
70 vals[1] = Integer.getInteger("sun.net.client.defaultConnectTimeout", 0).intValue();
71 encs[0] = System.getProperty("file.encoding", "ISO8859_1");
72 return null;
73 }
74 });
75 if (vals[0] == 0)
76 defaultSoTimeout = -1;
77 else
78 defaultSoTimeout = vals[0];
79
80 if (vals[1] == 0)
81 defaultConnectTimeout = -1;
82 else
83 defaultConnectTimeout = vals[1];
84
85
86 encoding = encs[0];
87 try {
88 if (!isASCIISuperset (encoding)) {
89 encoding = "ISO8859_1";
90 }
91 } catch (Exception e) {
92 encoding = "ISO8859_1";
93 }
94 }
95
96
97 /**
98 * Test the named character encoding to verify that it converts ASCII
99 * characters correctly. We have to use an ASCII based encoding, or else
100 * the NetworkClients will not work correctly in EBCDIC based systems.
101 * However, we cannot just use ASCII or ISO8859_1 universally, because in
102 * Asian locales, non-ASCII characters may be embedded in otherwise
103 * ASCII based protocols (eg. HTTP). The specifications (RFC2616, 2398)
104 * are a little ambiguous in this matter. For instance, RFC2398 [part 2.1]
105 * says that the HTTP request URI should be escaped using a defined
106 * mechanism, but there is no way to specify in the escaped string what
107 * the original character set is. It is not correct to assume that
108 * UTF-8 is always used (as in URLs in HTML 4.0). For this reason,
109 * until the specifications are updated to deal with this issue more
110 * comprehensively, and more importantly, HTTP servers are known to
111 * support these mechanisms, we will maintain the current behavior
112 * where it is possible to send non-ASCII characters in their original
113 * unescaped form.
114 */
115 private static boolean isASCIISuperset (String encoding) throws Exception {
116 String chkS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"+
117 "abcdefghijklmnopqrstuvwxyz-_.!~*'();/?:@&=+$,";
118
119 // Expected byte sequence for string above
120 byte[] chkB = { 48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70,71,72,
121 73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,
122 100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,
123 115,116,117,118,119,120,121,122,45,95,46,33,126,42,39,40,41,59,
124 47,63,58,64,38,61,43,36,44};
125
126 byte[] b = chkS.getBytes (encoding);
127 return Arrays.equals (b, chkB);
128 }
129
130 /** Open a connection to the server. */
131 public void openServer(String server, int port)
132 throws IOException, UnknownHostException {
133 if (serverSocket != null)
134 closeServer();
135 serverSocket = doConnect (server, port);
136 try {
137 serverOutput = new PrintStream(new BufferedOutputStream(
138 serverSocket.getOutputStream()),
139 true, encoding);
140 } catch (UnsupportedEncodingException e) {
141 throw new InternalError(encoding +"encoding not found");
142 }
143 serverInput = new BufferedInputStream(serverSocket.getInputStream());
144 }
145
146 /**
147 * Return a socket connected to the server, with any
148 * appropriate options pre-established
149 */
150 protected Socket doConnect (String server, int port)
151 throws IOException, UnknownHostException {
152 Socket s;
153 if (proxy != null) {
154 if (proxy.type() == Proxy.Type.SOCKS) {
155 s = AccessController.doPrivileged(
156 new PrivilegedAction<Socket>() {
157 public Socket run() {
158 return new Socket(proxy);
159 }});
160 } else
161 s = new Socket(Proxy.NO_PROXY);
162 } else
163 s = new Socket();
164 // Instance specific timeouts do have priority, that means
165 // connectTimeout & readTimeout (-1 means not set)
166 // Then global default timeouts
167 // Then no timeout.
168 if (connectTimeout >= 0) {
169 s.connect(new InetSocketAddress(server, port), connectTimeout);
170 } else {
171 if (defaultConnectTimeout > 0) {
172 s.connect(new InetSocketAddress(server, port), defaultConnectTimeout);
173 } else {
174 s.connect(new InetSocketAddress(server, port));
175 }
176 }
177 if (readTimeout >= 0)
178 s.setSoTimeout(readTimeout);
179 else if (defaultSoTimeout > 0) {
180 s.setSoTimeout(defaultSoTimeout);
181 }
182 return s;
183 }
184
185 protected InetAddress getLocalAddress() throws IOException {
186 if (serverSocket == null)
187 throw new IOException("not connected");
188 return serverSocket.getLocalAddress();
189 }
190
191 /** Close an open connection to the server. */
192 public void closeServer() throws IOException {
193 if (! serverIsOpen()) {
194 return;
195 }
196 serverSocket.close();
197 serverSocket = null;
198 serverInput = null;
199 serverOutput = null;
200 }
201
202 /** Return server connection status */
203 public boolean serverIsOpen() {
204 return serverSocket != null;
205 }
206
207 /** Create connection with host <i>host</i> on port <i>port</i> */
208 public NetworkClient(String host, int port) throws IOException {
209 openServer(host, port);
210 }
211
212 public NetworkClient() {}
213
214 public void setConnectTimeout(int timeout) {
215 connectTimeout = timeout;
216 }
217
218 public int getConnectTimeout() {
219 return connectTimeout;
220 }
221
222 public void setReadTimeout(int timeout) {
223 if (serverSocket != null && timeout >= 0) {
224 try {
225 serverSocket.setSoTimeout(timeout);
226 } catch(IOException e) {
227 // We tried...
228 }
229 }
230 readTimeout = timeout;
231 }
232
233 public int getReadTimeout() {
234 return readTimeout;
235 }
236 }