server: add trace to display SSL ciphers
[nikiroo-utils.git] / src / be / nikiroo / utils / serial / server / ConnectAction.java
CommitLineData
79ce1a49 1package be.nikiroo.utils.serial.server;
ce0974c4
NR
2
3import java.io.BufferedReader;
4import java.io.IOException;
5import java.io.InputStreamReader;
6import java.io.OutputStreamWriter;
7import java.net.Socket;
8
d9e2136b
N
9import javax.net.ssl.SSLException;
10
ce0974c4 11import be.nikiroo.utils.Version;
79ce1a49
NR
12import be.nikiroo.utils.serial.Exporter;
13import be.nikiroo.utils.serial.Importer;
ce0974c4 14
f157aed8
NR
15/**
16 * Base class used for the client/server basic handling.
17 * <p>
18 * It represents a single action: a client is expected to only execute one
19 * action, while a server is expected to execute one action for each client
20 * action.
21 *
22 * @author niki
23 */
ce0974c4
NR
24abstract class ConnectAction {
25 private Socket s;
26 private boolean server;
27 private Version version;
f157aed8 28 private Version clientVersion;
ce0974c4
NR
29
30 private Object lock = new Object();
31 private BufferedReader in;
32 private OutputStreamWriter out;
33 private boolean contentToSend;
34
f157aed8
NR
35 /**
36 * Method that will be called when an action is performed on either the
37 * client or server this {@link ConnectAction} represent.
38 *
39 * @param version
40 * the counter part version
41 *
42 * @throws Exception
43 * in case of I/O error
44 */
45 abstract protected void action(Version version) throws Exception;
46
47 /**
48 * Method called when we negotiate the version with the client.
49 * <p>
50 * Thus, it is only called on the server.
51 * <p>
52 * Will return the actual server version by default.
53 *
54 * @param clientVersion
55 * the client version
56 *
57 * @return the version to send to the client
58 */
59 abstract protected Version negotiateVersion(Version clientVersion);
60
61 /**
62 * Handler called when an unexpected error occurs in the code.
63 *
64 * @param e
65 * the exception that occurred
66 */
67 abstract protected void onError(Exception e);
ce0974c4 68
f157aed8
NR
69 /**
70 * Create a new {@link ConnectAction}.
71 *
72 * @param s
73 * the socket to bind to
74 * @param server
75 * TRUE for a server action, FALSE for a client action (will
76 * impact the process)
77 * @param version
78 * the version of this client-or-server
79 */
ce0974c4
NR
80 protected ConnectAction(Socket s, boolean server, Version version) {
81 this.s = s;
82 this.server = server;
83
84 if (version == null) {
85 this.version = new Version();
86 } else {
87 this.version = version;
88 }
f157aed8
NR
89
90 clientVersion = new Version();
ce0974c4
NR
91 }
92
f157aed8
NR
93 /**
94 * The version of this client-or-server.
95 *
96 * @return the version
97 */
98 public Version getVersion() {
99 return version;
ce0974c4
NR
100 }
101
f157aed8
NR
102 /**
103 * Actually start the process (this is synchronous).
104 */
ce0974c4
NR
105 public void connect() {
106 try {
d9e2136b
N
107 in = new BufferedReader(
108 new InputStreamReader(s.getInputStream(), "UTF-8"));
ce0974c4
NR
109 try {
110 out = new OutputStreamWriter(s.getOutputStream(), "UTF-8");
111 try {
112 if (server) {
217a3310
NR
113 String line = in.readLine();
114 if (line != null && line.startsWith("VERSION ")) {
115 // "VERSION client-version" (VERSION 1.0.0)
116 Version clientVersion = new Version(
117 line.substring("VERSION ".length()));
118 this.clientVersion = clientVersion;
119 Version v = negotiateVersion(clientVersion);
120 if (v == null) {
121 v = new Version();
122 }
123
124 sendString("VERSION " + v.toString());
125 }
126
f157aed8 127 action(clientVersion);
ce0974c4
NR
128 } else {
129 String v = sendString("VERSION " + version.toString());
130 if (v != null && v.startsWith("VERSION ")) {
131 v = v.substring("VERSION ".length());
132 }
133
134 action(new Version(v));
135 }
136 } finally {
137 out.close();
0988831f 138 out = null;
ce0974c4
NR
139 }
140 } finally {
141 in.close();
0988831f 142 in = null;
ce0974c4
NR
143 }
144 } catch (Exception e) {
d9e2136b
N
145 if (e instanceof SSLException) {
146 String ciphers = "";
147 for (String cipher : Server.getAnonCiphers()) {
148 if (!ciphers.isEmpty()) {
149 ciphers += ", ";
150 }
151 ciphers += cipher;
152 }
153
154 e = new SSLException(
155 "SSL error (available SSL ciphers: " + ciphers + ")",
156 e);
157 }
158
ce0974c4
NR
159 onError(e);
160 } finally {
161 try {
162 s.close();
163 } catch (Exception e) {
164 onError(e);
165 }
166 }
167 }
168
f157aed8
NR
169 /**
170 * Serialise and send the given object to the counter part (and, only for
171 * client, return the deserialised answer -- the server will always receive
172 * NULL).
173 *
174 * @param data
175 * the data to send
176 *
177 * @return the answer (which can be NULL) if this action is a client, always
178 * NULL if it is a server
179 *
180 * @throws IOException
181 * in case of I/O error
182 * @throws NoSuchFieldException
183 * if the serialised data contains information about a field
184 * which does actually not exist in the class we know of
185 * @throws NoSuchMethodException
186 * if a class described in the serialised data cannot be created
187 * because it is not compatible with this code
188 * @throws ClassNotFoundException
189 * if a class described in the serialised data cannot be found
190 */
d9e2136b
N
191 protected Object sendObject(Object data)
192 throws IOException, NoSuchFieldException, NoSuchMethodException,
193 ClassNotFoundException {
ce0974c4 194 synchronized (lock) {
d9e2136b
N
195 String rep = sendString(
196 new Exporter().append(data).toString(true, true));
08a58812
NR
197 if (rep != null) {
198 return new Importer().read(rep).getValue();
199 }
200
201 return null;
ce0974c4
NR
202 }
203 }
204
f157aed8
NR
205 /**
206 * Reserved for the server: flush the data to the client and retrieve its
207 * answer.
208 * <p>
209 * Also used internally for the client (only do something if there is
210 * contentToSend).
211 * <p>
212 * Will only flush the data if there is contentToSend.
213 *
214 * @return the deserialised answer (which can actually be NULL)
215 *
216 * @throws IOException
217 * in case of I/O error
218 * @throws NoSuchFieldException
219 * if the serialised data contains information about a field
220 * which does actually not exist in the class we know of
221 * @throws NoSuchMethodException
222 * if a class described in the serialised data cannot be created
223 * because it is not compatible with this code
224 * @throws ClassNotFoundException
225 * if a class described in the serialised data cannot be found
226 * @throws java.lang.NullPointerException
227 * if the counter part has no data to send
228 */
d9e2136b
N
229 protected Object recObject()
230 throws IOException, NoSuchFieldException, NoSuchMethodException,
231 ClassNotFoundException, java.lang.NullPointerException {
79ce1a49 232 String str = recString();
ce0974c4 233 if (str == null) {
f157aed8 234 throw new NullPointerException("No more data available");
ce0974c4
NR
235 }
236
237 return new Importer().read(str).getValue();
238 }
239
cd0c27d2 240 /**
f157aed8
NR
241 * Send the given string to the counter part (and, only for client, return
242 * the answer -- the server will always receive NULL).
cd0c27d2 243 *
f157aed8
NR
244 * @param line
245 * the data to send (we will add a line feed)
cd0c27d2 246 *
f157aed8
NR
247 * @return the answer if this action is a client (without the added line
248 * feed), NULL if it is a server
249 *
250 * @throws IOException
251 * in case of I/O error
cd0c27d2 252 */
f157aed8 253 protected String sendString(String line) throws IOException {
ce0974c4
NR
254 synchronized (lock) {
255 out.write(line);
256 out.write("\n");
257
258 if (server) {
259 out.flush();
260 return null;
ce0974c4 261 }
cd0c27d2
NR
262
263 contentToSend = true;
79ce1a49 264 return recString();
ce0974c4
NR
265 }
266 }
267
f157aed8
NR
268 /**
269 * Reserved for the server (externally): flush the data to the client and
270 * retrieve its answer.
271 * <p>
272 * Also used internally for the client (only do something if there is
273 * contentToSend).
274 * <p>
275 * Will only flush the data if there is contentToSend.
276 *
277 * @return the answer (which can be NULL)
278 *
279 * @throws IOException
280 * in case of I/O error
281 */
79ce1a49 282 protected String recString() throws IOException {
ce0974c4
NR
283 synchronized (lock) {
284 if (server || contentToSend) {
285 if (contentToSend) {
286 out.flush();
287 contentToSend = false;
288 }
289
217a3310 290 return in.readLine();
ce0974c4 291 }
cd0c27d2
NR
292
293 return null;
ce0974c4
NR
294 }
295 }
296}