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 | } | |
131 | }.connect(); | |
132 | } | |
133 | }; | |
134 | } | |
135 | ||
136 | /** | |
137 | * This is the method that is called each time a client contact us. | |
138 | * | |
139 | * @param clientVersion | |
140 | * the client version | |
8537d55a NR |
141 | */ |
142 | protected void onClientContact(Version clientVersion) { | |
452f38c8 | 143 | getTraceHandler().trace("<<< CLIENT " + clientVersion); |
8537d55a NR |
144 | } |
145 | ||
146 | /** | |
147 | * This is the method that is called each time a client contact us. | |
148 | * | |
149 | * @param serverVersion | |
150 | * the server version | |
8537d55a NR |
151 | */ |
152 | protected void onServerContact(Version serverVersion) { | |
452f38c8 | 153 | getTraceHandler().trace(">>> SERVER " + serverVersion); |
217a3310 | 154 | getTraceHandler().trace(""); |
8537d55a NR |
155 | } |
156 | ||
157 | /** | |
158 | * This is the method that is called each time a client contact us. | |
159 | * | |
160 | * @param clientVersion | |
161 | * the client version | |
162 | * @param data | |
163 | * the data sent by the client | |
164 | */ | |
165 | protected void onRec(Version clientVersion, String data) { | |
166 | trace("<<< CLIENT (" + clientVersion + ")", data); | |
167 | } | |
168 | ||
169 | /** | |
170 | * This is the method that is called each time the forwarded server contact | |
171 | * us. | |
172 | * | |
173 | * @param serverVersion | |
174 | * the client version | |
175 | * @param data | |
176 | * the data sent by the client | |
177 | */ | |
178 | protected void onSend(Version serverVersion, String data) { | |
179 | trace(">>> SERVER (" + serverVersion + ")", data); | |
180 | } | |
181 | ||
182 | /** | |
183 | * Trace the data with the given prefix. | |
184 | * | |
185 | * @param prefix | |
186 | * the prefix (client, server, version...) | |
187 | * @param data | |
188 | * the data to trace | |
189 | */ | |
190 | private void trace(String prefix, String data) { | |
452f38c8 NR |
191 | int size = data.length(); |
192 | String ssize = size + " byte"; | |
193 | if (size > 1) { | |
194 | ssize = size + " bytes"; | |
195 | if (size >= 1000) { | |
196 | size = size / 1000; | |
197 | ssize = size + " kb"; | |
198 | if (size > 1000) { | |
199 | size = size / 1000; | |
200 | ssize = size + " MB"; | |
201 | } | |
202 | } | |
203 | } | |
204 | ||
205 | getTraceHandler().trace(prefix + ": " + ssize, 1); | |
8537d55a NR |
206 | |
207 | if (getTraceHandler().getTraceLevel() >= 2) { | |
208 | try { | |
452f38c8 NR |
209 | if (data.startsWith("ZIP:")) { |
210 | data = StringUtils.unzip64(data.substring(4)); | |
211 | } | |
212 | ||
8537d55a NR |
213 | Object obj = new Importer().read(data).getValue(); |
214 | if (obj == null) { | |
452f38c8 | 215 | getTraceHandler().trace("NULL", 2); |
8537d55a NR |
216 | getTraceHandler().trace("NULL", 3); |
217 | getTraceHandler().trace("NULL", 4); | |
218 | } else { | |
452f38c8 NR |
219 | if (obj.getClass().isArray()) { |
220 | getTraceHandler().trace( | |
221 | "(" + obj.getClass() + ") with " | |
222 | + Array.getLength(obj) + "element(s)", | |
223 | 3); | |
224 | } else { | |
225 | getTraceHandler().trace("(" + obj.getClass() + ")", 2); | |
226 | } | |
227 | getTraceHandler().trace("" + obj.toString(), 3); | |
228 | getTraceHandler().trace(data, 4); | |
8537d55a | 229 | } |
452f38c8 NR |
230 | } catch (NoSuchMethodException e) { |
231 | getTraceHandler().trace("(not an object)", 2); | |
232 | getTraceHandler().trace(data, 3); | |
233 | getTraceHandler().trace("", 4); | |
234 | } catch (NoSuchFieldException e) { | |
235 | getTraceHandler().trace( | |
217a3310 | 236 | "(incompatible: " + e.getMessage() + ")", 2); |
452f38c8 NR |
237 | getTraceHandler().trace(data, 3); |
238 | getTraceHandler().trace("", 4); | |
239 | } catch (ClassNotFoundException e) { | |
240 | getTraceHandler().trace( | |
241 | "(unknown object: " + e.getMessage() + ")", 2); | |
242 | getTraceHandler().trace(data, 3); | |
243 | getTraceHandler().trace("", 4); | |
8537d55a | 244 | } catch (Exception e) { |
217a3310 NR |
245 | getTraceHandler().trace( |
246 | "(decode error: " + e.getMessage() + ")", 2); | |
452f38c8 NR |
247 | getTraceHandler().trace(data, 3); |
248 | getTraceHandler().trace("", 4); | |
8537d55a NR |
249 | } |
250 | ||
452f38c8 | 251 | getTraceHandler().trace("", 2); |
8537d55a NR |
252 | } |
253 | } | |
254 | ||
255 | /** | |
256 | * Start a bridge between 2 servers. | |
257 | * | |
258 | * @param args | |
259 | * an array containing: | |
260 | * <ul> | |
261 | * <li>The bridge name</li> | |
262 | * <li>The bridge port</li> | |
452f38c8 | 263 | * <li>TRUE for an SSL bridge, FALSE for plain text</li> |
8537d55a NR |
264 | * <li>The forward server host</li> |
265 | * <li>The forward server port</li> | |
452f38c8 | 266 | * <li>TRUE for an SSL forward server, FALSE for plain text</li> |
8537d55a | 267 | * <li>(optional) a trace level</li> |
217a3310 | 268 | * <li>(optional) a truncate size for data</li> |
8537d55a NR |
269 | * </ul> |
270 | */ | |
271 | public static void main(String[] args) { | |
272 | final TraceHandler tracer = new TraceHandler(true, false, 0); | |
273 | try { | |
274 | if (args.length < 6) { | |
275 | tracer.error("Invalid syntax.\n" | |
217a3310 | 276 | + "Syntax: [name] [port] [ssl] [fhost] [fport] [fssl] ([trace level]) ([max])\n" |
8537d55a NR |
277 | + "\tname: the bridge name\n" |
278 | + "\tport: the bridge port\n" | |
279 | + "\tssl: TRUE for an SSL bridge, FALSE for plain text\n" | |
280 | + "\tfhost: the forward server host\n" | |
281 | + "\tfport: the forward server port\n" | |
282 | + "\tfssl: TRUE for an SSL forward server, FALSE for plain text\n" | |
217a3310 NR |
283 | + "\ttrace level: the optional trace level (default is 1)\n" |
284 | + "\tmax: the maximum size after which to truncate data\n"); | |
8537d55a NR |
285 | return; |
286 | } | |
287 | ||
288 | int i = 0; | |
289 | String name = args[i++]; | |
290 | int port = Integer.parseInt(args[i++]); | |
291 | boolean ssl = Boolean.parseBoolean(args[i++]); | |
292 | String fhost = args[i++]; | |
293 | int fport = Integer.parseInt(args[i++]); | |
294 | boolean fssl = Boolean.parseBoolean(args[i++]); | |
295 | ||
296 | int traceLevel = 1; | |
297 | if (args.length > 6) { | |
298 | traceLevel = Integer.parseInt(args[i++]); | |
299 | } | |
217a3310 NR |
300 | int maxPrintSize = 1; |
301 | if (args.length > 7) { | |
302 | maxPrintSize = Integer.parseInt(args[i++]); | |
303 | } | |
8537d55a NR |
304 | |
305 | ServerBridge bridge = new ServerBridge(name, port, ssl, fhost, | |
306 | fport, fssl); | |
217a3310 NR |
307 | bridge.setTraceHandler(new TraceHandler(true, true, traceLevel, |
308 | maxPrintSize)); | |
8537d55a NR |
309 | bridge.run(); |
310 | } catch (Exception e) { | |
311 | tracer.error(e); | |
312 | } | |
313 | } | |
314 | } |