Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Sync/Async Communication in JavaScript
branch: master
Failed to load latest commit information.
Distribution Added JavaScript distribution
Sources Updated to version 1.1.1
Tests Updated to version 0.7.6
.gitignore Added gitignore
Makefile Updated Channels to 0.9.1
README.html Updated readme
README.txt Updated readme

README.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Channels</title>
<style><!-- 
body {
	margin-left: 10%;
	margin-right: 10%;
	padding: 20pt;
	padding-top: 10pt;
	background: rgb(255,255,255);
	font:  10.5pt/15pt "Helvetica",Helvetica,sans-serif;
	color: rgb(80,80,80);
}

h1, h2, h3, h4 {
	font-family: "Trebuchet MS",Helvetica,sans-serif;
	color: rgb(22, 130, 178);
	font-weight: normal;
	padding-top: 0.5em;
	cursor: pointer;
}

hr {
	color: rgb(150, 220, 238);
	background: rgb(150, 220, 238);
	height: 1px;
	border: 0;
}


b {
	color: rgb(22,130,178);
}

strong {
	color: rgb(103,183,0);
}


a:link, a:active, a:visited {
	color: rgb(22,130,178);
	text-decoration: none;
}

a:hover {
	text-decoration: none;
	background-color: #dbecf4;
}

aimg {
	border: 0;
}

#header, #footer {
	font-size: 7pt;
	clear: both;
	width: 100%;
	color: rgb(177,208,223);
}

.textoContent {
	text-align: left;
}


#footer {
	padding-top:  30pt;
	text-align: right;
}

/*  Kiwi-specific  */

.title {
	margin-bottom: 0;
}

.textoMeta {
	max-width: 700px;
	padding: 5pt;
	margin-bottom:  2em;
	border-top:  1px solid rgb(150, 220, 238);
	background-color: rgb(250,250,250);
}

.textoMeta tr td {
	color: rgb(22, 130, 178);
}

.textoMeta tr td.name {
	font-weight: bold;
}

.textoContent {
	max-width: 700px;
}

.textoContent .heading .number {
	display: none;
	padding-right: 8pt;
}

.textoContent .heading .number .lastDot {
	display: none;
}


.textoContent .heading .number .level0 .lastDot {
	display: inline;
}

.textoContent h1 {
	font-size: 1.8em;
	font-weight: bold;
	margin-top: 1.5em;
	padding-bottom: 0.5em;
	border-bottom:  1px dotted rgb(150, 220, 238);
}

.textoContent h2 {
	font-size: 1.4em;
	font-weight: bold;
	padding-bottom: 0.5em;
	border-bottom:  1px dotted rgb(150, 220, 238);
}

.textoContent h3 {
	font-size: 1.2em;
	font-weight: normal;
}

.textoContent pre {
	padding: 5pt;
	border:  1px solid rgb(150, 220, 238);
	padding-left: 20pt;
	background-color: rgb(240,240,250);
	font-size: 8pt;
	color: rgb(22,130,178);
}

.textoContent code {
	font-size: 8pt;
	background-color: rgb(240,240,250);
}

.textoContent dt {
	color: rgb(22,130,178);
	font-weight: bold;
}

.textoContent dd {
	border-left:  1px solid rgb(150, 220, 238);
	padding-left: 20pt;
	margin-bottom: 2em;
}

.textoContent dd pre {
}

.textoContent ul {
	padding-top: 0em;
	margin-top: 0em;
}

.textoContent ul li {
	padding-bottom: 0.2em;
}

.textoContent ul li.todo {
	list-style-type: square;
}

.textoContent ul li.todo.done {
	text-decoration: line-through;
}

.textoContent table {
	border:  1px solid rgb(150, 220, 238);
	padding: 0pt;
}

.textoContent table caption {
	font-family: serif;
	padding-top: 1em;
	padding-bottom: 0.5em;
	font-style: italic;
	font-size: 90%;
	color: rgb(22, 130, 178);
}

.textoContent table tbody {
}

.textoContent table tr {
	margin: 0;
}

.textoContent table tr td {
	margin: 0;
	padding: 5pt;
	font-size: 90%;
	min-width: 125px;
	background-color: rgb(250,250,250);
	border-bottom: 1px solid rgb(150, 220, 238);
}

.textoContent table tr td.lastRow {
	border-bottom: none;
}

.textoContent table tr td.lastCol {
	border-right: none;
}

.textoContent table tr.even td {
	background-color: #FEFEFE;

}

.textoContent table tr.odd td {
	background-color: rgb(240,240,240);
}

.textoContent .term {
	color: rgb(22, 130, 178);
	background: rgb(240, 250, 256);
}

.textoContent .quote {
	font-style: italic;
	color: rgb(120, 120, 120);
}

.textoContent .citation {
	font-style: italic;
	color: rgb(120, 120, 120);
}

.textoContent div[class^="ann"] {
	margin-top: 5pt;
	margin-bottom: 5pt;
	padding: 5pt;
	padding-left: 20pt;
	color: rgb(100, 100, 100);
}

.textoContent div[class^="ann"] .title {
	font-weight: bold;
}

.textoContent .annNote {
	border:  1px solid rgb(103,183,0);
	margin-top: 5pt;
	margin-bottom: 5pt;
	padding: 5pt;
	padding-left: 20pt;
	background: #fffbe4;
	color: rgb(100, 100, 100);
	border:  1px solid #dedac3;
}

.textoContent .annNote .title {
	font-weight: bold;
	display: none;
}

.textoReferences {
	font:  8pt/12pt "Lucida Grande",Lucida,sans-serif;
	margin-top: 10pt;
	padding: 5pt;
	border-top:  1px solid rgb(200, 200, 200);
	background-color: rgb(250,250,250);
	color: rgb(200, 200, 200);
	font-size: 8pt;
}
.textoReferences a:link, .textoReferences a:active, .textoReferences a:visited {
	color: rgb(150,150,150);
}

.textoReferences .entry {
	padding-top: 5pt;
	clear: both;
}

.textoReferences .entry .name {
	float: left;
	font-weight: bold;
	padding-right: 5pt;
}

.textoReferences .entry .content {
	text-align: right;
}

 --></style>
</head>
<body>
<div
	class="title"><h1>Channels</h1><h2>Sync/Async Communication in JavaScript</h2></div><table class='textoMeta'><tr><td width='0px' class='name'>Author</td><td width='100%' class='value'>Sebastien Pierre &lt;sebastien@ffctn.com&gt;</td></tr><tr><td width='0px' class='name'>Revision</td><td width='100%' class='value'>2010-08-11</td></tr><tr><td width='0px' class='name'>Creation</td><td width='100%' class='value'>2007-11-12</td></tr></table>
<div class="textoContent"><div class='content'><div class="section" level="1"><h1 class="heading"><span class="number"><span class="level0">1<span class="lastDot dot">.</span></span></span>The problem</h1><div class="level1"><div class='content'><p>Web applications now require a lot of client &harr; server interaction using HTTP requests. These requests can be either <span class='term'>blocking</span> (synchronous) or <span class='term'>non-blocking</span> (asynchronous). Doing synchronous HTTP requests is easy for a developer, as the HTTP request can be executed as a regular method call:</p><pre>var r = new XMLHttpRequest();
r.open("GET", "sampledata.json");
r.send(null);
var intermediate_result = doSomethinWithTheResponse(r.responseText)
var final_result = doSomethinElseWithTheResponse(intermediate_result)</pre><p>However, doing asynchronous HTTP requests implies using callbacks, which impacts your programming style:</p><pre>var r = new XMLHttpRequest();
r.open("GET", "sampledata.json");
r.onreadystatechange=function(){
  if (r.readyState==4) {
      var intermediate_result = doSomethinWithTheResponse(r.responseText)
      var final_result = doSomethinElseWithTheResponse(intermediate_result)
  }
}
r.send(null);</pre><p>The Channels library aims at providing a <span class='term'>uniform API to do synchronous and asynchronous requests</span>, as well as to offer constructs that will ease asynchronous concurrent programming on the client-side.</p></div></div></div><div class="section" level="1"><h1 class="heading"><span class="number"><span class="level0">2<span class="lastDot dot">.</span></span></span>Channels API</h1><div class="level1"><div class='content'><p>The Channels API is built on two main constructs:</p><dl><dt> Futures</dt><dd><div class='content'>Futures are objects that wrap values, so that you can register callbacks that will be triggered when the value is set, or if there was a failure while setting the value.</div></dd><dt> Channels</dt><dd><div class='content'>Channels are objects that abstract a connection between a client and a server. Channels produce futures to which you can attach callbacks.</div></dd></dl><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">2<span class="dot">.</span></span><span class="level1">1<span class="lastDot dot">.</span></span></span>Futures API</h2><div class="level2"><div class='content'><div class="section" level="3"><h3 class="heading"><span class="number"><span class="level0">2<span class="dot">.</span></span><span class="level1">1<span class="dot">.</span></span><span class="level2">1<span class="lastDot dot">.</span></span></span>Creating and operating on a future</h3><div class="level3"><div class='content'><dl><dt> <code>new Future()</code></dt><dd><div class='content'>Creates a new future without a value. The future will be by default in the <code>WAITING</code> state until a value is set, a failure occurs or the future is cancelled.</div></dd><dt> <code>&lt;Future&gt;.set(value:Any)</code></dt><dd><div class='content'>Sets a value to this future. The future will be in the <code>SET</code> state and the callbacks registered with <code>onSet</code> will be triggered in their order of registration.</div></dd><dt> <code>&lt;Future&gt;.setPartial(value:Any)</code></dt><dd><div class='content'>Sets a value to this future. The future will stay in the <code>WAITING</code> state until a value is bound using <code>set</code>, but the callbacks registered with <code>onPartial</code> will be triggered in their order of registration.</div></dd><dt> <code>&lt;Future&gt;.get():Any</code></dt><dd><div class='content'>Returns the value bound to this future, if any.</div></dd><dt> <code>&lt;Future&gt;.fail(status:Any,reason:Any,context:Any)</code></dt><dd><div class='content'>Makes this future fail. The future will be in the <code>FAILED</code> state.</div></dd><dt> <code>&lt;Future&gt;.cancel()</code></dt><dd><div class='content'>Cancels this future. The future will be in the <code>CANCELED</code> state.</div></dd></dl></div></div></div><div class="section" level="3"><h3 class="heading"><span class="number"><span class="level0">2<span class="dot">.</span></span><span class="level1">1<span class="dot">.</span></span><span class="level2">2<span class="lastDot dot">.</span></span></span>Registering callbacks</h3><div class="level3"><div class='content'><dl><dt> <code>&lt;Future&gt;.onSet(callback:(value:Any,future:Future)-&gt;Any):Future</code></dt><dd><div class='content'>Registers a callback that will be called when a value is bound to the future (using the <code>set</code> method).</div></dd><dt> <code>&lt;Future&gt;.onFail(callback:(status:Int,reason:String,context:Any,future:Future)-&gt;Any):Future</code></dt><dd><div class='content'>Registers a callback that will be called if the future fails. The given arguments are inspired from HTTP, with <code>status</code> being the error code, <code>reason</code> an optional string description, <code>context</code> an optional data object (typically the XMLHttpRequest object if the future comes from an HTTP channel) a reference to this future.</div></dd><dt> <code>&lt;Future&gt;.onPartial(callback:(value:Any,future:Future)-&gt;Any):Future</code></dt><dd><div class='content'>Registers a callback that will be called when the future is partially set. This is useful for the implementation of multi-body or streaming HTTP requests.</div></dd><dt> <code>&lt;Future&gt;.onException(callback:(e:Exception,future:Future)-&gt;Any):Future</code></dt><dd><div class='content'>Registers a callback that will be called if there is an exception while executing a callback. If an exception happens without any exception callback registered, the exception will be printed on the console.</div></dd><dt> <code>&lt;Future&gt;.onCancel(callback:(value:Any,future:Future)-&gt;Any):Future</code></dt><dd><div class='content'>Registers a callback that will be called if the future is cancelled.</div></dd><dt> <code>&lt;Future&gt;.onRefresh(callback:(future:Future)-&gt;Any):Future</code></dt><dd><div class='content'>Registers a callback that will be called when the future is refreshed. The typical usage is to pass the future to a function that will set its value or make it fail.</div></dd></dl></div></div></div><div class="section" level="3"><h3 class="heading"><span class="number"><span class="level0">2<span class="dot">.</span></span><span class="level1">1<span class="dot">.</span></span><span class="level2">3<span class="lastDot dot">.</span></span></span>Querying a future:</h3><div class="level3"><div class='content'><dl><dt> <code>&lt;Future&gt;.isSet()</code></dt><dd><div class='content'>Returns <code>true</code> if a value is bound to the future</div></dd><dt> <code>&lt;Future&gt;.hasFailed()</code></dt><dd><div class='content'>Returns <code>true</code> if the future has failed</div></dd><dt> <code>&lt;Future&gt;.hasSucceeded()</code></dt><dd><div class='content'>Returns <code>true</code> if the future has suceeded (same as <code>isSet()</code>)</div></dd><dt> <code>&lt;Future&gt;.getFailureStatus():Any</code></dt><dd><div class='content'>Retuens the failure status, when the future has failed</div></dd><dt> <code>&lt;Future&gt;.getFailureReason():Any</code></dt><dd><div class='content'>Retuens the failure reason, when the future has failed</div></dd></dl></div></div></div><div class="section" level="3"><h3 class="heading"><span class="number"><span class="level0">2<span class="dot">.</span></span><span class="level1">1<span class="dot">.</span></span><span class="level2">4<span class="lastDot dot">.</span></span></span>Retrying a future</h3><div class="level3"><div class='content'><p>Futures may fail due to timeouts, connection or server errors. Some channels may set <code>refresh</code> callbacks, in which case you can either call the <code>refresh</code> or <code>retry</code> functions to try to update the future.</p><dl><dt> <code>&lt;Future&gt;.refresh():Future</code></dt><dd><div class='content'>Triggers the callbacks registerd with <code>onRefresh</code>. These callback may or may not change this future.</div></dd><dt> <code>&lt;Future&gt;.retry(max:Int=5):Boolean</code></dt><dd><div class='content'>Triggers a refresh an increases the number of retries. It returns <code>true</code> if the number of retries for this future is below the given maximum. Typical usage of this is in the following case (in Sugar):<br /><pre>future onSet {
  # Do something
} onFail {s,r,c,future|
  if not future retry (5)
    # We had 5 errors in a row, so we have to take action
    alert "The server is down, sorry !"
  end
}</pre></div></dd></dl></div></div></div><div class="section" level="3"><h3 class="heading"><span class="number"><span class="level0">2<span class="dot">.</span></span><span class="level1">1<span class="dot">.</span></span><span class="level2">5<span class="lastDot dot">.</span></span></span>Chain-processing the value</h3><div class="level3"><div class='content'><p>You might want to pre-process values bound to a future. For instance, HTTP channels may want to automatically convert the incoming data to JSON and make the future fail if the JSON is invalid. You can register <span class='term'>processors</span> that convert the value into another or directly control the future.</p><dl><dt> <code>&lt;Future&gt;.process(processor:(value:Any,future:Future)-&gt;Any)</code></dt><dd><div class='content'>Registers a processor function that takes the bound value and the future as paramater and returns the transformed value.</div></dd><dt> <code>&lt;Future&gt;.rawValue():Any</code></dt><dd><div class='content'>Returns the raw value bound to the future, before being processed.</div></dd></dl></div></div></div></div></div></div><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">2<span class="dot">.</span></span><span class="level1">2<span class="lastDot dot">.</span></span></span>Channels API</h2><div class="level2"><div class='content'></div></div></div></div></div></div><div class="section" level="1"><h1 class="heading"><span class="number"><span class="level0">3<span class="lastDot dot">.</span></span></span>How-to</h1><div class="level1"><div class='content'><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">3<span class="dot">.</span></span><span class="level1">1<span class="lastDot dot">.</span></span></span>Handle failures in HTTP channels</h2><div class="level2"><div class='content'><p>While most of the request you'll do using the HTTP channels will succeed, some of them will fail for various reasons: there was a timeout, the server crashed, or you simply gave wrong arguments.</p><p>As channels always return your futures, you'll simply use the channels 'onFail' callback to register a function that will be invoked if the channel is not able to get the value to your future.</p><p>Here is an example (in Sugar):</p><pre>var f = channel get "/some/url"
f onFail { status, reason, context, future|
  ....
}</pre><p>In the callback you gave, you will be given the following arguments:</p><ul><li><code>status</code> with the HTTP response status code (404, 500, etc) </li><li><code>reason</code> with the body of the HTTP response, expected to be a human-readable string </li><li><code>context</code> with the reference to the request that failed, so you can still get extra information from the headers </li><li><code>future</code> referencing the future that failed, in case you have a shared error handler.</li></ul></div></div></div><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">3<span class="dot">.</span></span><span class="level1">2<span class="lastDot dot">.</span></span></span>Handle exceptions in channels and futures callbacks</h2><div class="level2"><div class='content'><p>You may think at first that exceptions are a specific case of failures. In channels and futures, there is a major difference between failures and exceptions:</p><ul><li>A failure indicates that the future won't have a value, which is something that can happen (eg. when using an HTTP channel, server can timeout, request can be invalid, etc). </li><li>An exception indicates that there was an unhandled error happening within the code, in this case the callbacks given to channels and futures.</li></ul><p>So if you implement <em>callbacks that raise exceptions</em>, you'll need to define callbacks to catch these exceptions, otherwise they will propagate to your program and your application will break.</p><p>Here is an example of a callback that raises an exception:</p><pre>var f = new Future ()
f onSet {v| if v &lt; 3 -&gt; throw "Expects 3 or more"}</pre><p>doing <code>f(2)</code> will raise an exception, while <code>f(3)</code> won't. To catch the exception (and allow your code to continue after the 'f(2)' invocation), you can define a callback for exception:</p><pre>f onException {e,f| print ("Exception happened",e,"in future",f)}</pre><p>You can add multiple handlers for exception. They will stack up, and the last callback you added will be invoked first. If the callback returns <code>False</code>, the other callback won't be called (so you have a chance to stop propagation).</p><pre>f onException {return False}</pre><p>will simply absorb the exceptions without notifying you. You can also define exception handlers for channels (and every future created by the channel will make use of it):</p><pre>var c = new AsyncChannel ()
c onException {e,f| print ("Exception happened",e,"in future",f) ; return False}</pre><p>If you want to know more about this have a look at <code>Future.onException</code>, <code>Channel.onException</code> and the <code>channelExceptionPropagation</code> in the test suite.</p></div></div></div></div></div></div><div class="section" level="1"><h1 class="heading"><span class="number"><span class="level0">4<span class="lastDot dot">.</span></span></span>Burst Channel Protocol</h1><div class="level1"><div class='content'><p>Burst channels is an easy way to do HTTP tunneling over HTTP. The idea is that when you have a lot of tiny requests to send to the server, it is may be better to group them as a larger request, so that you don't pay the overhead of multiplied latency.</p><p>For instance, if you have to send 10 requests, and you have a 100ms latency to the server, you'll end up having 1000ms total latency, while when using burst channels, you could keep the 100ms latency.</p><p>The burst channel protocol is designed for JSON-based communication between client and server. As opposed to plain tunneling of requests, where you'd expect requests and responses to be embedded "raw" in composite request and responses, the burst channel protocol specifies that only requests are embedded 'raw' (because server parse requests) while the responses are embedded as JSON maps (because clients should not spend time parsing requests).</p><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">4<span class="dot">.</span></span><span class="level1">1<span class="lastDot dot">.</span></span></span>Request format</h2><div class="level2"><div class='content'><p>The Burst Channel protocol is modeled after the MIME RFC <a href="#RFC1341" class="internal">RFC1341</a>]. The idea is that the client will simply embed each request as an individual body in a <span class='term'>composite request</span>. The only difference with RFC1341 is that we don't use the content-type header to identify boundary, but we use the 'X-Channel-Boundary' specific header (to make things easier for both client and server).</p><pre>+---------------------------------------------------------------+
| GET         /url/supporting/burst/channel                     |
+---------------------------------------------------------------+
| X-Channel-Boundary: 8&lt;-----BURST-CHANNEL-REQUEST------        |
| X-Channel-Type: burst                                         |
| X-Channel-Requests: 4                                         |
+---------------------------------------------------------------+
| HTTP REQUEST 1                                                |
| ............................................................. |
| 8&lt;-----BURST-CHANNEL-REQUEST------                            |
| ............................................................. |
| HTTP REQUEST 2                                                |
| ............................................................. |
| 8&lt;-----BURST-CHANNEL-REQUEST------                            |
| ............................................................. |
| HTTP REQUEST 4                                                |
+---------------------------------------------------------------+</pre><p>Each HTTP request is given as-is, including request URI, headers and body. The only thing to watch for is to avoid conflicts between boundary and request content (but this is the problem of the client).</p><p>Aside from that, it is a good idea to add an <code>X-Channel-Type:burst</code> header to the requests headers, just to be more explicit (and give a chance to reverse proxies to direct the burst requests to the appropriate server)&ndash;but severs should not require that field.</p><p>The optional <code>X-Channel-Requests</code> field can also be added to indicate how many requests are expected to be found in the request bodies. This is only useful for debugging purposes, as HTTP requests are expected to arrive completely.</p><p>There is no other particular requirement for the URL either, it is more a matter of convention. It's generally OK to provide a single URL that processes burst channels requests. In some cases, you might prefer to partition your site and provide different URLs that can handle burst channels.</p><p>It may be good to mention that in the HTTP spec, the request, headers and body must be separated by CR/LF (<code>\r\n</code>), so every request should look like:</p><pre>REQUEST
\r\n
HEADER\n
HEADER\n
\r\n
BODY\n</pre><p>in case you don't have headers, it would look like</p><pre>REQUEST
\r\n
\r\n
BODY\n</pre></div></div></div><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">4<span class="dot">.</span></span><span class="level1">2<span class="lastDot dot">.</span></span></span>Response format</h2><div class="level2"><div class='content'><p>As for requests, responses are embedded in a composite response, the main difference being that responses are embedded as <span class='term'>json maps</span> (as opposed to raw texT). The only additional constraint to respect here is to make sure the ordering of responses is the same as the ordering of requests.</p><pre>+---------------------------------------------------------------+
| HTTP 200 OK                                                   |
+---------------------------------------------------------------+
| X-Channel-Boundary: 8&lt;-----BURST-CHANNEL-RESPONSE------       |
| X-Channel-Responses: 4                                        |
+---------------------------------------------------------------+
| HTTP RESPONSE 1                                               |
| ............................................................. |
| 8&lt;-----BURST-CHANNEL-RESPONSE------                           |
| ............................................................. |
| HTTP RESPONSE 2                                               |
| ............................................................. |
| 8&lt;-----BURST-CHANNEL-RESPONSE------                           |
| ............................................................. |
| HTTP RESPONSE 4                                               |
+---------------------------------------------------------------+</pre><p>The optional <code>X-Channel-Responses</code> plays the same role as <code>X-Channel-Requests</code> and is only useful for debugging purposes.</p><p>As we mentioned, the content of <code>HTTP RESPONSE</code> is not the raw response : to save unnecessary parsing time on the client-side, the response is already given as JSON structure, with the following fields:</p><ul><li><code>status</code> the HTTP status code, as an integer </li><li><code>reason</code>, the HTTP reason, as a string </li><li><code>headers</code>, the HTTP heads, as an array or <code>[name,value]</code> </li><li><code>body</code>, the HTTP body, as a string</li></ul><p>An example response would be:</p><pre>{
  "status":200,
  "reason":"OK",
  "headers":[["Content-Type","application/json"]],
  "body":'["hello","world"]'
}</pre><p>You're not required to specify all the headers in the 'headers' field, as the client is expected to first look in the embedded response headers, and if the header is not found, it will look in the parent response header (but generally, only 'Content-Type' will be useful).</p></div></div></div><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">4<span class="dot">.</span></span><span class="level1">3<span class="lastDot dot">.</span></span></span>Expected client behaviour</h2><div class="level2"><div class='content'><p>The client can choose whether he wants to send the request all at once or if he will "stream" the bodies, meaning that there may be some delay between the sending of each body. However, this is unlikely to happen as web browsers do not support streaming of request bodies.</p><p>Clients should take advantage of response streaming and start to process the responses even if all the responses have not been received.</p></div></div></div><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">4<span class="dot">.</span></span><span class="level1">4<span class="lastDot dot">.</span></span></span>Expected server behaviour</h2><div class="level2"><div class='content'><p>Conversely, the server can choose between waiting for all the requests to be processed to before sending the response, or to send the responses as soon as they are available, provided the ordering is kept.</p><p>The optimal behaviour would be to start processing the requests as soon as they are available, and to stream the responses as soon as they are available and in order.</p></div></div></div><div class="section" level="2"><h2 class="heading"><span class="number"><span class="level0">4<span class="dot">.</span></span><span class="level1">5<span class="lastDot dot">.</span></span></span>Possible extensions</h2><div class="level2"><div class='content'><p>One of the obvious extensions to burst channels is to do simple HTTP streaming, where the client sends only one request, and that the server returns an infinite amount of responses.</p></div></div></div></div></div></div><div class="section" level="1"><h1 class="heading"><span class="number"><span class="level0">5<span class="lastDot dot">.</span></span></span>Examples</h1><div class="level1"><div class='content'><p><a href="#RFC1341" class="internal">RFC1341</a>The Multipart content type <a href="http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html" class="external">RFC section 7</a></p></div></div></div></div></div>

</body>
</html>
Something went wrong with that request. Please try again.