Server-sent events support for OkHttp
npx @tessl/cli install tessl/maven-com-squareup-okhttp3--okhttp-sse@4.12.00
# OkHttp Server-Sent Events (SSE)
1
2
OkHttp Server-Sent Events provides experimental support for real-time data streaming using the Server-Sent Events protocol. It enables applications to receive live updates from servers for use cases like chat applications, live feeds, notifications, and monitoring dashboards.
3
4
## Package Information
5
6
- **Package Name**: okhttp-sse
7
- **Package Type**: maven
8
- **Language**: Kotlin
9
- **Coordinates**: `com.squareup.okhttp3:okhttp-sse`
10
- **Installation**: Add `implementation 'com.squareup.okhttp3:okhttp-sse:4.12.0'` to build.gradle
11
12
## Core Imports
13
14
```kotlin
15
import okhttp3.sse.EventSource
16
import okhttp3.sse.EventSourceListener
17
import okhttp3.sse.EventSources
18
import okhttp3.OkHttpClient
19
import okhttp3.Request
20
```
21
22
For Java:
23
24
```java
25
import okhttp3.sse.EventSource;
26
import okhttp3.sse.EventSourceListener;
27
import okhttp3.sse.EventSources;
28
import okhttp3.OkHttpClient;
29
import okhttp3.Request;
30
```
31
32
## Basic Usage
33
34
```kotlin
35
import okhttp3.OkHttpClient
36
import okhttp3.Request
37
import okhttp3.Response
38
import okhttp3.sse.EventSource
39
import okhttp3.sse.EventSourceListener
40
import okhttp3.sse.EventSources
41
42
// Create HTTP client and event source factory
43
val client = OkHttpClient()
44
val factory = EventSources.createFactory(client)
45
46
// Create event listener
47
val listener = object : EventSourceListener() {
48
override fun onOpen(eventSource: EventSource, response: Response) {
49
println("Connection opened")
50
}
51
52
override fun onEvent(eventSource: EventSource, id: String?, type: String?, data: String) {
53
println("Received event: $data")
54
}
55
56
override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
57
println("Connection failed: ${t?.message}")
58
}
59
60
override fun onClosed(eventSource: EventSource) {
61
println("Connection closed")
62
}
63
}
64
65
// Create and start event source
66
val request = Request.Builder()
67
.url("https://example.com/events")
68
.build()
69
70
val eventSource = factory.newEventSource(request, listener)
71
72
// Clean up resources when done
73
eventSource.cancel()
74
```
75
76
## Architecture
77
78
The OkHttp SSE library follows a factory and listener pattern design:
79
80
- **Factory Pattern**: `EventSources.createFactory()` creates event source factories configured with specific OkHttp clients
81
- **Listener Pattern**: `EventSourceListener` provides callback methods for handling SSE lifecycle events
82
- **Resource Management**: Manual cleanup required via `cancel()` method
83
- **Asynchronous Operation**: All callbacks occur on background threads
84
- **OkHttp Integration**: Built on OkHttp's robust networking infrastructure with connection pooling and HTTP/2 support
85
86
## Capabilities
87
88
### Event Source Factory
89
90
Core factory functionality for creating server-sent event connections.
91
92
```kotlin { .api }
93
/**
94
* Creates a factory for event sources using the provided OkHttpClient.
95
* Automatically adds "Accept: text/event-stream" header if not present.
96
*/
97
@JvmStatic
98
fun createFactory(client: OkHttpClient): EventSource.Factory
99
100
/**
101
* Processes an existing Response as server-sent events.
102
* Useful when you already have a Response object from an HTTP call.
103
*/
104
@JvmStatic
105
fun processResponse(response: Response, listener: EventSourceListener)
106
```
107
108
### Event Source Interface
109
110
Core interface representing a server-sent event source connection.
111
112
```kotlin { .api }
113
interface EventSource {
114
/** Returns the original request that initiated this event source */
115
fun request(): Request
116
117
/** Immediately and violently release resources held by this event source */
118
fun cancel()
119
120
/** Factory interface for creating new event sources */
121
fun interface Factory {
122
/** Creates a new event source and immediately returns it */
123
fun newEventSource(request: Request, listener: EventSourceListener): EventSource
124
}
125
}
126
```
127
128
### Event Source Listener
129
130
Abstract class providing callback methods for handling server-sent event lifecycle.
131
132
```kotlin { .api }
133
abstract class EventSourceListener {
134
/**
135
* Invoked when an event source has been accepted by the remote peer
136
* and may begin transmitting events.
137
*/
138
open fun onOpen(eventSource: EventSource, response: Response) {}
139
140
/**
141
* Invoked when a server-sent event is received.
142
*
143
* Server-sent events have the format:
144
* - id: optional unique identifier for the event
145
* - event: optional event type name
146
* - data: the actual event payload/message
147
*
148
* @param eventSource The event source that received the event
149
* @param id Optional event ID from the 'id:' field (null if not specified)
150
* @param type Optional event type from the 'event:' field (null if not specified)
151
* @param data The event data from the 'data:' field
152
*/
153
open fun onEvent(eventSource: EventSource, id: String?, type: String?, data: String) {}
154
155
/**
156
* Invoked when the event source has been closed normally by the server.
157
* No further calls to this listener will be made.
158
*/
159
open fun onClosed(eventSource: EventSource) {}
160
161
/**
162
* Invoked when an event source has been closed due to an error.
163
* Incoming events may have been lost. No further calls to this listener will be made.
164
*
165
* @param eventSource The event source that failed
166
* @param t The throwable that caused the failure (null for certain types of failures)
167
* @param response The HTTP response if available (null for network failures)
168
*/
169
open fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {}
170
}
171
```
172
173
## Usage Examples
174
175
### Java Usage
176
177
```java
178
import okhttp3.OkHttpClient;
179
import okhttp3.Request;
180
import okhttp3.Response;
181
import okhttp3.sse.EventSource;
182
import okhttp3.sse.EventSourceListener;
183
import okhttp3.sse.EventSources;
184
185
// Create client and factory
186
OkHttpClient client = new OkHttpClient();
187
EventSource.Factory factory = EventSources.createFactory(client);
188
189
// Create listener
190
EventSourceListener listener = new EventSourceListener() {
191
@Override
192
public void onOpen(EventSource eventSource, Response response) {
193
System.out.println("Connection opened");
194
}
195
196
@Override
197
public void onEvent(EventSource eventSource, String id, String type, String data) {
198
System.out.println("Event: " + data);
199
}
200
201
@Override
202
public void onFailure(EventSource eventSource, Throwable t, Response response) {
203
System.out.println("Error: " + (t != null ? t.getMessage() : "Unknown"));
204
}
205
206
@Override
207
public void onClosed(EventSource eventSource) {
208
System.out.println("Connection closed");
209
}
210
};
211
212
// Create event source
213
Request request = new Request.Builder()
214
.url("https://example.com/events")
215
.build();
216
217
EventSource eventSource = factory.newEventSource(request, listener);
218
219
// Clean up
220
eventSource.cancel();
221
```
222
223
### Processing Existing Response
224
225
```kotlin
226
import okhttp3.sse.EventSources
227
228
// When you already have a Response object from an HTTP call
229
val response = client.newCall(request).execute()
230
231
// Process the response as SSE
232
EventSources.processResponse(response, listener)
233
```
234
235
### Custom Headers and Configuration
236
237
```kotlin
238
import java.util.concurrent.TimeUnit
239
240
// Add custom headers to the request
241
val request = Request.Builder()
242
.url("https://example.com/events")
243
.header("Authorization", "Bearer token")
244
.header("Custom-Header", "value")
245
.build()
246
247
// Configure OkHttp client with custom settings
248
val client = OkHttpClient.Builder()
249
.connectTimeout(30, TimeUnit.SECONDS)
250
.readTimeout(60, TimeUnit.SECONDS)
251
.build()
252
253
val factory = EventSources.createFactory(client)
254
val eventSource = factory.newEventSource(request, listener)
255
```
256
257
## Error Handling
258
259
Common error scenarios and how they're handled:
260
261
- **Invalid Content-Type**: If the server doesn't return `text/event-stream`, `onFailure` is called with the HTTP response
262
- **Network Errors**: Connection issues trigger `onFailure` with an `IOException` and null response
263
- **Malformed SSE Data**: Parsing errors cause `onFailure` to be invoked with parsing exception
264
- **Normal Closure**: Server closes connection normally triggers `onClosed` (not `onFailure`)
265
- **HTTP Errors**: Non-2xx status codes trigger `onFailure` with the error response
266
267
```kotlin
268
override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) {
269
when {
270
t is IOException && response == null -> {
271
println("Network connection error: ${t.message}")
272
}
273
response != null -> {
274
when (response.code) {
275
401 -> println("Authentication failed")
276
404 -> println("SSE endpoint not found")
277
in 400..499 -> println("Client error: ${response.code} ${response.message}")
278
in 500..599 -> println("Server error: ${response.code} ${response.message}")
279
else -> println("HTTP error: ${response.code} ${response.message}")
280
}
281
}
282
t != null -> println("Parsing or protocol error: ${t.message}")
283
else -> println("Unknown error occurred")
284
}
285
}
286
```
287
288
## Threading and Lifecycle
289
290
- All event source operations are asynchronous
291
- Callbacks (`onOpen`, `onEvent`, `onFailure`, `onClosed`) are invoked on background threads
292
- Resources must be manually released using `cancel()` method
293
- No automatic reconnection is provided - implement retry logic in your application if needed
294
- Event sources do not support pausing or resuming - use `cancel()` and create new instances
295
296
## Types
297
298
```kotlin { .api }
299
// Core SSE API types
300
interface EventSource {
301
fun request(): Request
302
fun cancel()
303
304
fun interface Factory {
305
fun newEventSource(request: Request, listener: EventSourceListener): EventSource
306
}
307
}
308
309
abstract class EventSourceListener {
310
open fun onOpen(eventSource: EventSource, response: Response)
311
open fun onEvent(eventSource: EventSource, id: String?, type: String?, data: String)
312
open fun onClosed(eventSource: EventSource)
313
open fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?)
314
}
315
316
object EventSources {
317
@JvmStatic fun createFactory(client: OkHttpClient): EventSource.Factory
318
@JvmStatic fun processResponse(response: Response, listener: EventSourceListener)
319
}
320
321
// Required OkHttp types (from okhttp3 package)
322
class OkHttpClient
323
class Request
324
class Response
325
```