RawHTTP Duplex
The rawhttp-duplex module can be used to create a duplex communication channel as either a client or a server.
The entry point of the library is the com.athaydes.rawhttp.duplex.RawHttpDuplex
class.
Its connect
methods are used from a client to connect to a server,
while the accept
methods should be used within a HTTP server to handle requests from a client.
Example Kotlin code on the server:
import rawhttp.core.*
import com.athaydes.rawhttp.duplex.*
import rawhttp.core.server.TcpRawHttpServer;
val http = RawHttp()
val duplex = RawHttpDuplex()
val server = TcpRawHttpServer(8082)
server.start { request ->
// TODO check the request is a POST to the /connect path!
// call duplex.accept() to return a response that can initiate duplex communication
Optional.of(duplex.accept(request, { sender ->
object : MessageHandler {
override fun onTextMessage(message: String) {
// handle text message
sender.sendTextMessage("Hi there! You sent this: $message")
}
override fun onBinaryMessage(message: ByteArray, headers: RawHttpHeaders) { /* handle binary message */ }
override fun onClose() { /* handle closed connection */ }
}
}))
}
Example Kotlin code on the client:
import rawhttp.core.*
import com.athaydes.rawhttp.duplex.*
import rawhttp.core.server.TcpRawHttpServer;
val http = RawHttp()
val duplex = RawHttpDuplex()
duplex.connect(http.parseRequest("POST http://localhost:8082/connect"), { sender ->
object : MessageHandler { /* same API as on the server */ }
}
How duplex works
The way duplex communication is achieved uses only HTTP/1.1 standard mechanisms and can be described as follows:
- The server listens for requests to start duplex communication.
- When a client connects, the server sends out a single chunked response in which each chunk is a new message from the server to the client.
- The client does the same: it sends a chunked body with the request in which each chunk is a message from the client to the server.
In other words, a single request/response is used to bootstrap communications. Both the request and the response have effectively infinite chunked bodies where each chunk represents a message.
RawHttpDuplex
sends a single extension parameter to idenfity text
messages: Content-Type: text/plain
(notice that each chunk may contain “extensions”).
If the chunk does not contain this extension, then it is considered to be a binary message.
Text messages may also contain the Charset: <charset>
(e.g. Charset: US-ASCII
) extension parameter to provide a
charset for the message. By default, UTF-8
is used.
Each side of a connection pings the other every 5 seconds, by default, to avoid the TCP socket timing out. To use a different ping period, use the {@link RawHttpDuplex#RawHttpDuplex(TcpRawHttpClient, Duration)} constructor.
Demo
As is mandatory for duplex communication implementations, a Chat Demo application was written in Kotlin to demonstrate usage of this library.
The video below shows it in action: