| <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 30. Architecture"><link rel="prev" href="jetty-1xx-responses.html" title="Managing 1xx Responses"><link rel="next" href="platforms.html" title="Chapter 31. 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> </td><th width="60%" align="center">Chapter 30. 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"> <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’s <code class="literal">SocketChannel</code>. |
| You use the <code class="literal">EndPoint</code> to read, write, and close. |
| You don’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: “Call me back when you are done, and (possibly) give me the result of the computation."</p><p>SPDY’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> </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"> <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 </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"> Chapter 31. 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> |