8

I'm implementing a Server-Sent Events library using OkHttp. Server Sent Events works by keeping an open HTTP connection to the server on which 'events' can be streamed back to the client. The connection will only close on errors, or if the client explicitly disconnects.

What's the best way to achieve this streaming behaviour using OkHttp? I've attempted to do something like:

response.body().source().readAll(new Sink() {
  @Override
  public void write(Buffer source, long byteCount) throws IOException {
    Log.d(TAG, "write(): byteCount = "+byteCount);
  }

  @Override
  public void flush() throws IOException {
    Log.d(TAG, "flush()");
  }

  @Override
  public Timeout timeout() {
    return Timeout.NONE;
  }

  @Override
  public void close() throws IOException {
    Log.d(TAG, "close()");
  }
});

With this approach I will eventually see the log message in write(), but it can sometimes take quite a while (minutes). That makes me think there might be some buffering going on under the hood and I don't get my data until the buffer is flushed.

I've used curl to verify the server is behaving correctly. The data is being sent on time, I'm just not getting my callback when it arrives.

My experience with OkHttp and Okio is very limited, so it's very possible I've messed something up, or have forgotten to set some option. Any help is greatly appreciated! :)

mkobit
  • 43,979
  • 12
  • 156
  • 150
Mike Fougere
  • 157
  • 1
  • 6

1 Answers1

13

When you call readAll() Okio prefers net throughput over latency, and so your messages are buffered into chunks. Instead, write a loop that repeatedly reads into a Buffer. That'll get you messages as they arrive.

Buffer buffer = new Buffer();
while (!source.exhausted()) {
  long count = response.body().source().read(buffer, 8192);
  // handle data in buffer.
}
mkobit
  • 43,979
  • 12
  • 156
  • 150
Jesse Wilson
  • 39,078
  • 8
  • 121
  • 128
  • How to handle data in buffer as you mentioned in code comment? – Kammy Jul 07 '23 at 04:11
  • 1
    @Kammy that is entirely an implementation detail of however your application/code requires the data. For example, you could use Jackson's `ObjectMapper`: `new ObjectMapper().readValue(buffer.inputStream(), MyExamplePojo.class)`, but again, that is entirely dependent on the response data and your use case. – Cisco Jul 15 '23 at 21:21