blob: 0fa37d4e46804dd225bea36f7acb7d89d23ef8ca [file] [log] [blame]
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Creating a Custom Protocol</title><link rel="stylesheet" type="text/css" href="css/docbook.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><meta name="keywords" content="jetty, servlet, servlet-api, cometd, http, websocket, eclipse, maven, java, server, software"><link rel="home" href="index.html" title="Jetty"><link rel="up" href="architecture.html" title="Chapter&nbsp;30.&nbsp;Architecture"><link rel="prev" href="jetty-1xx-responses.html" title="Managing 1xx Responses"><link rel="next" href="platforms.html" title="Chapter&nbsp;31.&nbsp;Platforms, Stacks and Alternative Distributions"><link xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension" xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension" xmlns:d="http://docbook.org/ns/docbook" xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0" xmlns:xslthl="http://xslthl.sf.net" xmlns:gcse="http://www.google.com" xmlns:date="http://exslt.org/dates-and-times" rel="shortcut icon" href="images/favicon.ico"><link rel="stylesheet" href="css/highlighter/foundation.css"><script src="js/highlight.pack.js"></script><script>
hljs.initHighlightingOnLoad();
</script><link type="text/css" rel="stylesheet" href="css/font-awesome/font-awesome.min.css"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><table xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension" xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension" xmlns:d="http://docbook.org/ns/docbook" xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0" xmlns:xslthl="http://xslthl.sf.net" xmlns:gcse="http://www.google.com" xmlns:date="http://exslt.org/dates-and-times"><tr><td style="width: 25%"><a href="http://www.eclipse.org/jetty"><img src="images/jetty-header-logo.png" alt="Jetty Logo"></a><br><span style="font-size: small">
Version: 9.3.28.v20191105</span></td><td style="width: 50%"></td></tr></table><div xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension" xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension" xmlns:d="http://docbook.org/ns/docbook" xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0" xmlns:xslthl="http://xslthl.sf.net" xmlns:gcse="http://www.google.com" xmlns:date="http://exslt.org/dates-and-times" class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Creating a Custom Protocol</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="jetty-1xx-responses.html"><i class="fa fa-chevron-left" aria-hidden="true"></i> Previous</a>&nbsp;</td><th width="60%" align="center">Chapter&nbsp;30.&nbsp;Architecture<br><a accesskey="p" href="index.html"><i class="fa fa-home" aria-hidden="true"></i> Home</a></th><td width="20%" align="right">&nbsp;<a accesskey="n" href="platforms.html">Next <i class="fa fa-chevron-right" aria-hidden="true"></i></a></td></tr></table><hr></div><div xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension" xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension" xmlns:d="http://docbook.org/ns/docbook" xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0" xmlns:xslthl="http://xslthl.sf.net" xmlns:gcse="http://www.google.com" xmlns:date="http://exslt.org/dates-and-times" class="jetty-callout"><h5 class="callout"><a href="http://www.webtide.com/">Contact the core Jetty developers at
<span class="website">www.webtide.com</span></a></h5><p>
private support for your internal/customer projects ... custom extensions and distributions ... versioned snapshots for indefinite support ...
scalability guidance for your apps and Ajax/Comet projects ... development services for sponsored feature development
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="creating-custom-protocol"></a>Creating a Custom Protocol</h2></div></div></div><div class="toc"><dl class="toc"><dt><span class="section"><a href="creating-custom-protocol.html#server-connection-factory">Implementing a TelnetServerConnectionFactory</a></span></dt><dt><span class="section"><a href="creating-custom-protocol.html#telnet-server-connection">Implementing the TelnetServerConnection</a></span></dt><dt><span class="section"><a href="creating-custom-protocol.html#parser-interpreter">Parsing the Bytes Received</a></span></dt><dt><span class="section"><a href="creating-custom-protocol.html#api-byte-processor">Designing an API to Process Bytes</a></span></dt></dl></div><p>You can create custom protocols with Jetty. This page provides an example of how to do so, with Telnet as the protocol.</p><p>To create a custom Telnet protocol, complete the following tasks:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Implement a <code class="literal">TelnetServerConnectionFactory</code>.</li><li class="listitem">Implement a <code class="literal">TelnetServerConnection</code> by extending <code class="literal">o.e.j.io.AbstractConnection</code>.</li><li class="listitem">Create a parser/interpreter for the bytes you receive (this is totally independent from Jetty).</li><li class="listitem">If needed, design an API for the application to use to process the bytes received (also independent from Jetty).
The API likely has a <span class="emphasis"><em>respond back</em></span> primitive that uses a Jetty provided <code class="literal">EndPoint</code> and <code class="literal">EndPoint.write(Callback, Buffer...)</code> to write the response bytes.</li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="server-connection-factory"></a>Implementing a TelnetServerConnectionFactory</h3></div></div></div><p>Begin with an <code class="literal">org.eclipse.jetty.server.ServerConnector</code>, which you can use as is. <code class="literal">ServerConnector</code> takes a <code class="literal">o.e.j.server.ConnectionFactory</code>, which creates <code class="literal">o.e.j.io.Connection</code> objects that interpret the bytes the connector receives.
You must implement <code class="literal">ConnectionFactory</code> with a <code class="literal">TelnetServerConnectionFactory</code>, where you return a Connection implementation (for example, <code class="literal">TelnetServerConnection</code>).</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="telnet-server-connection"></a>Implementing the TelnetServerConnection</h3></div></div></div><p>For the Connection implementation you need to extend from <code class="literal">o.e.j.io.AbstractConnection</code> because it provides many facilities that you would otherwise need to re-implement from scratch.</p><p>For each Connection instance there is associated an <code class="literal">o.e.j.io.EndPoint</code> instance.
Think of <code class="literal">EndPoint</code> as a specialized version of JDK&#8217;s <code class="literal">SocketChannel</code>.
You use the <code class="literal">EndPoint</code> to read, write, and close.
You don&#8217;t need to implement <code class="literal">EndPoint</code>, because Jetty provides concrete
classes for you to use.</p><p>The Connection is the <span class="emphasis"><em>passive</em></span> side (that is, Jetty calls it when there is data to read), while the <code class="literal">EndPoint</code> is the active part (that is, applications call it to write data to the other end).
When there is data to read, Jetty calls <code class="literal">AbstractConnection.onFillable()</code>, which you must implement in your <code class="literal">TelnetServerConnection</code>.</p><p>A typical implementation reads bytes from the <code class="literal">EndPoint</code> by calling <code class="literal">EndPoint.fill(ByteBuffer)</code>.
For examples, look at both the simpler <code class="literal">SPDYConnection</code> (in the SPDY client package, but server also uses it), and the slightly more complex <code class="literal">HttpConnection</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="parser-interpreter"></a>Parsing the Bytes Received</h3></div></div></div><p>After you read the bytes, you need to parse them.
For the Telnet protocol there is not much to parse, but perhaps you have your own commands that you want to interpret and execute.
Therefore typically every connection has an associated parser instance.
In turn, a parser usually emits parse events that a parser listener interprets, as the following examples illustrate:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">In HTTP, the Jetty HTTP parser parses the request line (and emits a parser event), then parses the headers (and emits a parser event for each) until it recognizes the end of the headers (and emits another parser event).
At that point, the <span class="emphasis"><em>interpreter</em></span> or parser listener (which for HTTP is <code class="literal">o.e.j.server.HttpChannel</code>) has all the information necessary to build a <code class="literal">HttpServletRequest</code> object and can call the user code (the web application, that is, servlets/filters).</li><li class="listitem">In SPDY, the Jetty SPDY parser parses a SPDY frame (and emits a parser event), and the parser listener (an instance of o.e.j.spdy.StandardSession) interprets the parser events and calls user code (application-provided listeners).</li></ul></div><p>With <code class="literal">ConnectionFactory</code>, Connection, parser, and parser listeners in place, you have configured the read side.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="api-byte-processor"></a>Designing an API to Process Bytes</h3></div></div></div><p>At this point, server applications typically write data back to the client.</p><p>The Servlet API (for HTTP) or application-provided listeners (for SPDY) expose an interface to web applications so that they can write data back to the client.
The implementation of those interfaces must link back to the <code class="literal">EndPoint</code> instance associated with the Connection instance so that it can write data via <code class="literal">EndPoint.write(Callback, ByteBuffer...)</code>.
This is an asynchronous call, and it notifies the callback when all the buffers have been fully written.</p><p>For example, in the Servlet API, applications use a <code class="literal">ServletOutputStream</code> to write the response content.
<code class="literal">ServletOutputStream</code> is an abstract class that Jetty implements, enabling Jetty to handle the writes from the web application; the writes eventually end up in an <code class="literal">EndPoint.write(...)</code> call.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="api-tips"></a>Tips for Designing an API</h4></div></div></div><p>If you want to write a completely asynchronous implementation, your API to write data to the client must have a callback/promise concept: &#8220;Call me back when you are done, and (possibly) give me the result of the computation."</p><p>SPDY&#8217;s Stream class is a typical example.
Notice how the methods there exist in two versions, a synchronous (blocking) one, and an asynchronous one that takes as last parameter a Callback (if no result is needed), or a Promise (if a result is needed).
It is trivial to write the synchronous version in terms of the asynchronous version.</p><p>You can use <code class="literal">EndPoint.write(Callback, ByteBuffer...)</code> in a blocking way as follows:</p><pre xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension" xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension" xmlns:d="http://docbook.org/ns/docbook" xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0" xmlns:xslthl="http://xslthl.sf.net" xmlns:gcse="http://www.google.com" xmlns:date="http://exslt.org/dates-and-times"><code>FutureCallback callback = new FutureCallback();
endPoint.write(callback, buffers);
callback.get();</code></pre><p>With the snippet above your API can be synchronous or asynchronous (your choice), but implemented synchronously.</p></div></div></div><script type="text/javascript">
SyntaxHighlighter.all()
</script><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="jetty-1xx-responses.html"><i class="fa fa-chevron-left" aria-hidden="true"></i> Previous</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="architecture.html"><i class="fa fa-chevron-up" aria-hidden="true"></i> Top</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="platforms.html">Next <i class="fa fa-chevron-right" aria-hidden="true"></i></a></td></tr><tr><td width="40%" align="left" valign="top">Managing 1xx Responses&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="index.html"><i class="fa fa-home" aria-hidden="true"></i> Home</a></td><td width="40%" align="right" valign="top">&nbsp;Chapter&nbsp;31.&nbsp;Platforms, Stacks and Alternative Distributions</td></tr></table></div><p xmlns:jfetch="java:org.eclipse.jetty.xslt.tools.JavaSourceFetchExtension" xmlns:fetch="java:org.eclipse.jetty.xslt.tools.SourceFetchExtension" xmlns:d="http://docbook.org/ns/docbook" xmlns:l="http://docbook.sourceforge.net/xmlns/l10n/1.0" xmlns:xslthl="http://xslthl.sf.net" xmlns:gcse="http://www.google.com" xmlns:date="http://exslt.org/dates-and-times"><div class="jetty-callout">
See an error or something missing?
<span class="callout"><a href="http://github.com/eclipse/jetty.project">Contribute to this documentation at
<span class="website"><i class="fa fa-github" aria-hidden="true"></i> Github!</span></a></span><span style="float: right"><i>(Generated: 2019-11-05)</i></span></div></p></body></html>