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