4646import com .github .jlangch .venice .impl .threadpool .ManagedCachedThreadPoolExecutor ;
4747import com .github .jlangch .venice .impl .types .VncBoolean ;
4848import com .github .jlangch .venice .impl .types .VncKeyword ;
49+ import com .github .jlangch .venice .impl .types .VncLong ;
4950import com .github .jlangch .venice .impl .types .VncVal ;
5051import com .github .jlangch .venice .impl .types .collections .VncMap ;
5152import com .github .jlangch .venice .impl .types .collections .VncOrderedMap ;
53+ import com .github .jlangch .venice .impl .types .util .Coerce ;
5254import com .github .jlangch .venice .impl .util .CollectionUtil ;
5355import com .github .jlangch .venice .impl .util .StringUtil ;
5456import com .github .jlangch .venice .util .dh .DiffieHellmanKeys ;
@@ -76,7 +78,7 @@ public class TcpClient implements Cloneable, Closeable {
7678 * @param port a port
7779 */
7880 public TcpClient (final int port ) {
79- this (null , port , false );
81+ this (null , port );
8082 }
8183
8284 /**
@@ -91,61 +93,42 @@ public TcpClient(final int port) {
9193 * @param port a port
9294 */
9395 public TcpClient (final String host , final int port ) {
94- this (host , port , false );
95- }
96-
97- /**
98- * Create a new TcpClient connecting to a TcpServer on the local host
99- * and port
100- *
101- * <p>The client is NOT thread safe!
102- *
103- * <p>The client must be closed after use!
104- *
105- * @param port a port
106- * @param encrypt if <code>true</code> encrypt the payload data at transport
107- * level communication between this client and the server.
108- */
109- public TcpClient (final int port , final boolean encrypt ) {
110- this (null , port , encrypt );
111- }
112-
113- /**
114- * Create a new TcpClient connecting to a TcpServer on the specified host
115- * and port
116- *
117- * <p>The client is NOT thread safe!
118- *
119- * <p>The client must be closed after use!
120- *
121- * @param host a host
122- * @param port a port
123- * @param encrypt if <code>true</code> encrypt the payload data at transport
124- * level communication between this client and the server.
125- */
126- public TcpClient (final String host , final int port , final boolean encrypt ) {
12796 this .host = StringUtil .isBlank (host ) ? "127.0.0.1" : host ;
12897 this .port = port ;
129- this .encrypt = encrypt ;
13098 this .endpointId = UUID .randomUUID ().toString ();
13199 this .dhKeys = DiffieHellmanKeys .create ();
132100 }
133101
134102
135103 @ Override
136104 public Object clone () {
137- final TcpClient client = new TcpClient (host , port , isEncrypted ());
105+ final TcpClient client = new TcpClient (host , port );
106+ client .setEncryption (isEncrypted ());
138107 client .setCompressCutoffSize (getCompressCutoffSize ());
139- client .setMaximumMessageSize (getMaximumMessageSize ());
140- return client ;
108+ return client ;
109+ }
110+
111+ /**
112+ * Set the encryption mode
113+ *
114+ * @param encrypt if <code>true</code> encrypt the payload data at transport
115+ * level communication between this client and the server.
116+ */
117+ public void setEncryption (final boolean encrypt ) {
118+ if (opened .get ()) {
119+ throw new VncException (
120+ "The encryption mode cannot be set anymore "
121+ + "once the client has been opened!" );
122+ }
123+ this .encrypt .set (encrypt );
141124 }
142125
143126 /**
144127 * @return <code>true</code> if this client has transport level encryption
145128 * enabled else <code>false</code>
146129 */
147130 public boolean isEncrypted () {
148- return encryptor .get (). isActive ();
131+ return encrypt .get ();
149132 }
150133
151134 /**
@@ -177,23 +160,15 @@ public long getCompressCutoffSize() {
177160 return compressor .get ().cutoffSize ();
178161 }
179162
180- /**
181- * Set the maximum message size.
182- *
183- * <p>Defaults to 200 MB
184- *
185- * @param maxSize the max message size in bytes
186- * @return this client
187- */
188- public TcpClient setMaximumMessageSize (final long maxSize ) {
189- maxMessageSize .set (Math .max (MESSAGE_LIMIT_MIN , Math .min (MESSAGE_LIMIT_MAX , maxSize )));
190- return this ;
191- }
192-
193163 /**
194164 * @return return the client's max message size
195165 */
196166 public long getMaximumMessageSize () {
167+ if (!opened .get ()) {
168+ throw new VncException (
169+ "The max message size cannot be queried if the client has "
170+ + "been opened and has requested the size from the server!" );
171+ }
197172 return maxMessageSize .get ();
198173 }
199174
@@ -237,7 +212,37 @@ public void open() {
237212 ex );
238213 }
239214
240- if (encrypt ) {
215+ // request config from server
216+ try {
217+ final IMessage response = send (createConfigRequestMessage ());
218+ if (response .getResponseStatus () != ResponseStatus .OK ) {
219+ throw new VncException (
220+ "Failed to get client config from server. Server answered with "
221+ + response .getResponseStatus () + " !" );
222+ }
223+
224+ // handle config
225+ final VncMap config = (VncMap )((Message )response ).getVeniceData ();
226+ maxMessageSize .set (
227+ Coerce .toVncLong (
228+ config .get (
229+ new VncKeyword ("max-msg-size" ),
230+ new VncLong (MESSAGE_LIMIT_MAX ))).toJavaLong ());
231+ encrypt .set (
232+ encrypt .get () // client side encrypt request
233+ || VncBoolean .isTrue ( // server side encrypt request
234+ config .get (
235+ new VncKeyword (":encrypt" ),
236+ VncBoolean .False )));
237+ }
238+ catch (Exception ex ) {
239+ IO .safeClose (ch );
240+ opened .set (false );
241+ channel .set (null );
242+ throw new VncException ("Failed to get client config from server!" , ex );
243+ }
244+
245+ if (encrypt .get ()) {
241246 try {
242247 diffieHellmanKeyExchange ();
243248 }
@@ -932,20 +937,27 @@ private IMessage sendAtomically(
932937 final Encryptor encryptor
933938 ) {
934939 try {
940+ final boolean auditCount = msg .getType () != MessageType .DIFFIE_HELLMAN_KEY_REQUEST
941+ && msg .getType () != MessageType .CLIENT_CONFIG ;
942+
935943 // sending the request message and receiving the response
936944 // must be atomic otherwise request and response can be mixed
937945 // in multi-threaded environments
938946 if (sendSemaphore .tryAcquire (120L , TimeUnit .SECONDS )) {
939947 try {
940948 Protocol .sendMessage (ch , (Message )msg , compressor , encryptor );
941- messageSentCount .incrementAndGet ();
949+ if (auditCount ) {
950+ messageSentCount .incrementAndGet ();
951+ }
942952
943953 if (msg .isOneway ()) {
944954 return null ;
945955 }
946956 else {
947957 final Message response = Protocol .receiveMessage (ch , compressor , encryptor );
948- messageReceiveCount .incrementAndGet ();
958+ if (auditCount ) {
959+ messageReceiveCount .incrementAndGet ();
960+ }
949961 return response ;
950962 }
951963 }
@@ -1141,6 +1153,25 @@ private static Message createQueuePollRequestMessage(
11411153 new byte [0 ]);
11421154 }
11431155
1156+ private static Message createConfigRequestMessage () {
1157+ return new Message (
1158+ null ,
1159+ null ,
1160+ MessageType .CLIENT_CONFIG ,
1161+ ResponseStatus .NULL ,
1162+ false ,
1163+ false ,
1164+ null ,
1165+ null ,
1166+ System .currentTimeMillis (),
1167+ Message .EXPIRES_NEVER ,
1168+ Message .NO_TIMEOUT ,
1169+ Topics .of ("client-config" ),
1170+ "text/plain" ,
1171+ "UTF-8" ,
1172+ new byte [0 ]);
1173+ }
1174+
11441175
11451176 private static class FutureNull implements Future <IMessage > {
11461177
@@ -1202,8 +1233,8 @@ private static byte[] toBytes(final String s, final String charset) {
12021233 private final AtomicReference <Compressor > compressor = new AtomicReference <>(Compressor .off ());
12031234
12041235 // encryption
1205- private final boolean encrypt ;
12061236 private final DiffieHellmanKeys dhKeys ;
1237+ private final AtomicBoolean encrypt = new AtomicBoolean (false );
12071238 private final AtomicReference <Encryptor > encryptor = new AtomicReference <>(Encryptor .off ());
12081239
12091240 private final ManagedCachedThreadPoolExecutor mngdExecutor =
0 commit comments