Sunday, July 30, 2023

how to transfer oversized string over message system

for text based message system, if there's a limit of x KB, you can still transfer (~5 * x) KB by

  1. compressing the original payload
  2. encoding compressed binary to text
  3. sending it
receiver reverses the process to get original payload by
  1. testing to make sure message is encoded
  2. decoding it to compressed binary
  3. uncompressing decoded binary
import org.apache.commons.codec.binary.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

String payload = """
        {
        }""";
System.out.printf("original size: %s bytes\n", payload.length());

ByteArrayOutputStream baos = new ByteArrayOutputStream();
GZIPOutputStream gos = new GZIPOutputStream(baos);
gos.write(payload.getBytes());
gos.close();
byte[] compressed = baos.toByteArray();
System.out.printf("compressed size: %s bytes\n", compressed.length);

String encoded = Base64.encodeBase64String(compressed);
System.out.printf("encoded size: %s bytes\n", encoded.length());

System.out.println("is message encoded? " + Base64.isBase64(encoded));
byte[] decoded = Base64.decodeBase64(encoded.getBytes());
System.out.printf("decoded size: %s bytes\n", decoded.length);

GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(decoded));
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = gis.read(buffer)) > 0) {
    output.write(buffer, 0, bytesRead);
}
gis.close();
String uncompressed = output.toString();
System.out.printf("uncompressed size: %s bytes\n", uncompressed.length());


original size: 9450 bytes
compressed size: 1235 bytes
encoded size: 1648 bytes
is message encoded? true
decoded size: 1235 bytes
uncompressed size: 9450 bytes


Saturday, July 01, 2023

revisit timeout settings after 9 years

After almost 9 year, this is part 2, or an update to Are you setting Connect Timeout correctly? I'm going to give the best practice of setting connect timeout and read timeout for org.springframework.boot.web.client.RestTemplateBuilder as of Spring Boot 2.7.13.

Let's say it takes up to 0.1 sec to setup connection and 1 sec to receive response, then it's easy to understand you'll get org.apache.http.conn.ConnectTimeoutException if

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(10))
.setReadTimeout(ofMillis(1000))
.build();

and java.net.SocketTimeoutException: Read timed out if

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(100))
.setReadTimeout(ofMillis(100))
.build();

You can either set both timeouts

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(100))
.setReadTimeout(ofMillis(1000))
.build();

or omit connect timeout and only set read timeout

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
//.setConnectTimeout(ofMillis(100))
.setReadTimeout(ofMillis(1000))
.build();

however by setting connect timeout only and omitting read timeout, you'll get java.net.SocketTimeoutException: Read timed out

return restTemplateBuilder
.rootUri(rootUri)
.basicAuthentication(username, password)
.setConnectTimeout(ofMillis(100))
//.setReadTimeout(ofMillis(1000))
.build();

Let me rephrase what I said 9 years ago, don't set both timeout to be the same, which shows you don't know how network communication works.