db50876b32aafb144bc89047e73073c391c569f8
[fanfix.git] / ServerBridge.java
1 package be.nikiroo.utils.serial.server;
2
3 import java.io.IOException;
4 import java.lang.reflect.Array;
5 import java.net.Socket;
6 import java.net.UnknownHostException;
7
8 import be.nikiroo.utils.StringUtils;
9 import be.nikiroo.utils.TraceHandler;
10 import be.nikiroo.utils.Version;
11 import be.nikiroo.utils.serial.Importer;
12
13 /**
14 * This class implements a simple server that can bridge two other
15 * {@link Server}s.
16 * <p>
17 * It can, of course, inspect the data that goes through it (by default, it
18 * prints traces of the data).
19 * <p>
20 * Note: this {@link ServerBridge} has to be discarded after use (cannot be
21 * started twice).
22 *
23 * @author niki
24 */
25 public class ServerBridge extends Server {
26 private final String forwardToHost;
27 private final int forwardToPort;
28 private final boolean forwardToSsl;
29
30 /**
31 * Create a new server that will start listening on the network when
32 * {@link ServerBridge#start()} is called.
33 *
34 * @param port
35 * the port to listen on, or 0 to assign any unallocated port
36 * found (which can later on be queried via
37 * {@link ServerBridge#getPort()}
38 * @param ssl
39 * use an SSL connection (or not)
40 * @param forwardToHost
41 * the host server to forward the calls to
42 * @param forwardToPort
43 * the host port to forward the calls to
44 * @param forwardToSsl
45 * use an SSL connection for the forward server or not
46 *
47 * @throws IOException
48 * in case of I/O error
49 * @throws UnknownHostException
50 * if the IP address of the host could not be determined
51 * @throws IllegalArgumentException
52 * if the port parameter is outside the specified range of valid
53 * port values, which is between 0 and 65535, inclusive
54 */
55 public ServerBridge(int port, boolean ssl, String forwardToHost,
56 int forwardToPort, boolean forwardToSsl) throws IOException {
57 super(port, ssl);
58 this.forwardToHost = forwardToHost;
59 this.forwardToPort = forwardToPort;
60 this.forwardToSsl = forwardToSsl;
61 }
62
63 /**
64 * Create a new server that will start listening on the network when
65 * {@link ServerBridge#start()} is called.
66 *
67 * @param name
68 * the server name (only used for debug info and traces)
69 * @param port
70 * the port to listen on
71 * @param ssl
72 * use an SSL connection (or not)
73 * @param forwardToHost
74 * the host server to forward the calls to
75 * @param forwardToPort
76 * the host port to forward the calls to
77 * @param forwardToSsl
78 * use an SSL connection for the forward server or not
79 *
80 * @throws IOException
81 * in case of I/O error
82 * @throws UnknownHostException
83 * if the IP address of the host could not be determined
84 * @throws IllegalArgumentException
85 * if the port parameter is outside the specified range of valid
86 * port values, which is between 0 and 65535, inclusive
87 */
88 public ServerBridge(String name, int port, boolean ssl,
89 String forwardToHost, int forwardToPort, boolean forwardToSsl)
90 throws IOException {
91 super(name, port, ssl);
92 this.forwardToHost = forwardToHost;
93 this.forwardToPort = forwardToPort;
94 this.forwardToSsl = forwardToSsl;
95 }
96
97 /**
98 * The traces handler for this {@link Server}.
99 * <p>
100 * The trace levels are handled as follow:
101 * <ul>
102 * <li>1: it will only print basic IN/OUT messages with length</li>
103 * <li>2: it will try to interpret it as an object (SLOW) and print the
104 * object class if possible</li>
105 * <li>3: it will try to print the {@link Object#toString()} value, or the
106 * data if it is not an object</li>
107 * <li>4: it will also print the unzipped serialised value if it is an
108 * object</li>
109 * </ul>
110 *
111 * @param tracer
112 * the new traces handler
113 */
114 @Override
115 public void setTraceHandler(TraceHandler tracer) {
116 super.setTraceHandler(tracer);
117 }
118
119 @Override
120 protected ConnectActionServer createConnectActionServer(Socket s) {
121 return new ConnectActionServerString(s) {
122 @Override
123 public void action(final Version clientVersion) throws Exception {
124 onClientContact(clientVersion);
125 final ConnectActionServerString bridge = this;
126
127 try {
128 new ConnectActionClientString(forwardToHost, forwardToPort,
129 forwardToSsl, clientVersion) {
130 @Override
131 public void action(final Version serverVersion)
132 throws Exception {
133 onServerContact(serverVersion);
134
135 for (String fromClient = bridge.rec(); fromClient != null; fromClient = bridge
136 .rec()) {
137 onRec(clientVersion, fromClient);
138 String fromServer = send(fromClient);
139 onSend(serverVersion, fromServer);
140 bridge.send(fromServer);
141 }
142
143 getTraceHandler().trace("=== DONE", 1);
144 getTraceHandler().trace("", 1);
145 }
146
147 @Override
148 protected void onError(Exception e) {
149 ServerBridge.this.onError(e);
150 }
151 }.connect();
152 } catch (Exception e) {
153 ServerBridge.this.onError(e);
154 }
155 }
156 };
157 }
158
159 /**
160 * This is the method that is called each time a client contact us.
161 *
162 * @param clientVersion
163 * the client version
164 */
165 protected void onClientContact(Version clientVersion) {
166 getTraceHandler().trace(">>> CLIENT " + clientVersion);
167 }
168
169 /**
170 * This is the method that is called each time a client contact us.
171 *
172 * @param serverVersion
173 * the server version
174 */
175 protected void onServerContact(Version serverVersion) {
176 getTraceHandler().trace("<<< SERVER " + serverVersion);
177 getTraceHandler().trace("");
178 }
179
180 /**
181 * This is the method that is called each time a client contact us.
182 *
183 * @param clientVersion
184 * the client version
185 * @param data
186 * the data sent by the client
187 */
188 protected void onRec(Version clientVersion, String data) {
189 trace(">>> CLIENT (" + clientVersion + ")", data);
190 }
191
192 /**
193 * This is the method that is called each time the forwarded server contact
194 * us.
195 *
196 * @param serverVersion
197 * the client version
198 * @param data
199 * the data sent by the client
200 */
201 protected void onSend(Version serverVersion, String data) {
202 trace("<<< SERVER (" + serverVersion + ")", data);
203 }
204
205 @Override
206 public void run() {
207 getTraceHandler().trace(
208 getName() + ": will forward to " + forwardToHost + ":"
209 + forwardToPort + " ("
210 + (forwardToSsl ? "SSL" : "plain text") + ")");
211 super.run();
212 }
213
214 /**
215 * Trace the data with the given prefix.
216 *
217 * @param prefix
218 * the prefix (client, server, version...)
219 * @param data
220 * the data to trace
221 */
222 private void trace(String prefix, String data) {
223 int size = data == null ? 0 : data.length();
224 String ssize = size + " byte";
225 if (size > 1) {
226 ssize = size + " bytes";
227 if (size >= 1000) {
228 size = size / 1000;
229 ssize = size + " kb";
230 if (size > 1000) {
231 size = size / 1000;
232 ssize = size + " MB";
233 }
234 }
235 }
236
237 getTraceHandler().trace(prefix + ": " + ssize, 1);
238
239 if (getTraceHandler().getTraceLevel() >= 2) {
240 try {
241 while (data.startsWith("ZIP:") || data.startsWith("B64:")) {
242 if (data.startsWith("ZIP:")) {
243 data = StringUtils.unbase64s(data.substring(4), true);
244 } else if (data.startsWith("B64:")) {
245 data = StringUtils.unbase64s(data.substring(4), false);
246 }
247 }
248
249 Object obj = new Importer().read(data).getValue();
250 if (obj == null) {
251 getTraceHandler().trace("NULL", 2);
252 getTraceHandler().trace("NULL", 3);
253 getTraceHandler().trace("NULL", 4);
254 } else {
255 if (obj.getClass().isArray()) {
256 getTraceHandler().trace(
257 "(" + obj.getClass() + ") with "
258 + Array.getLength(obj) + "element(s)",
259 3);
260 } else {
261 getTraceHandler().trace("(" + obj.getClass() + ")", 2);
262 }
263 getTraceHandler().trace("" + obj.toString(), 3);
264 getTraceHandler().trace(data, 4);
265 }
266 } catch (NoSuchMethodException e) {
267 getTraceHandler().trace("(not an object)", 2);
268 getTraceHandler().trace(data, 3);
269 getTraceHandler().trace("", 4);
270 } catch (NoSuchFieldException e) {
271 getTraceHandler().trace(
272 "(incompatible: " + e.getMessage() + ")", 2);
273 getTraceHandler().trace(data, 3);
274 getTraceHandler().trace("", 4);
275 } catch (ClassNotFoundException e) {
276 getTraceHandler().trace(
277 "(unknown object: " + e.getMessage() + ")", 2);
278 getTraceHandler().trace(data, 3);
279 getTraceHandler().trace("", 4);
280 } catch (Exception e) {
281 getTraceHandler().trace(
282 "(decode error: " + e.getMessage() + ")", 2);
283 getTraceHandler().trace(data, 3);
284 getTraceHandler().trace("", 4);
285 }
286
287 getTraceHandler().trace("", 2);
288 }
289 }
290 }