aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_http_service/docs/httpserver.html
blob: 128174b4cfbf32fb12f7fab6fd6a283ed989fd7a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
<!-- Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
        "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <title>HTTP Server Architecture</title>
    <style type="text/css">
        body {
        font: 13px/1.231 arial,helvetica,clean,sans-serif;
        *font-size: small;
        *font: x-small;
        }
        select,input,button,textarea {
        font: 99% arial,helvetica,clean,sans-serif;
        }
        table{
        font-size: inherit;
        font: 100%;
        }
        pre,code,kbd,samp,tt {
        font-family: monospace;
        *font-size: 108%;
        line-height: 100%;
        }
    </style>
</head>
<body>
<p>The HTTP server is started by calling <code>HttpServer.start()</code>, which in turn calls <code>ServerBootstrap.bind()</code>
    provided by
    Netty.</p>

<img src="class-diagram.png" alt="Class diagram">

<p>Since our HttpServer implements <code>ChannelPipelineFactory</code> (provided by jetty), its
    <code>getPipeline()</code> method is called for every new channel that is connected. There is hence a one-to-many
    relationship between a <code>HttpServer</code> and a pipeline (and a one-to-one relationship between an actual
    channel and a pipeline).</p>

<p>The pipeline is responsible for decoding (and possibly deflating etc.) every new request that is received on a
    channel. The final element in the pipeline is a <code>ChannelContext</code>, which is the jDISC class for handling
    requests on a channel.</p>

<p>The <code>ChannelContext</code> implements <code>SimpleChannelUpstreamHandler</code> (provided by Jetty), which has
    simple callback methods for various event types. <br/>Examples:</p>

<ul>
    <li><code>channelConnected()</code></li>
    <li><code>channelDisconnected()</code></li>
    <li><code>messageReceived()</code></li>
</ul>

<p>Since <code>ChannelContext</code> supports HTTP keep-alive and HTTP pipelining, it needs to keep track of multiple
    requests made on the channel, and their order.</p>

<p>In <code>messageReceived()</code> it will:</p>
<ul>
    <li>Determine if the element received is a new HTTP request, or a chunk belonging to the previous one.</li>
    <li>If it's a request, create a DISC <code>Request</code> object for it, and call <code>Request.connect()</code>,
        which will in turn give it to the actual application, through the use of
        <code>RequestHandler.handleRequest()</code>.
    </li>
    <li>If it's a chunk, fetch the previously added <code>RequestContext</code>, and use it to write the data received
        into the <code>ContentChannel</code>.
    </li>
</ul>

<p><code>RequestContext</code> keeps track of a request and its input and output <code>ContentChannel</code>s, and
    related objects. Since <code>RequestContext</code> is a <code>ResponseHandler</code>, it is responsible for
    instantiating and returning a <code>ContentChannel</code> when an application calls <code>handleResponse()</code>.
    Two types are supported, one that supports HTTP response chunking, and one that does not. The type used is chosen
    automatically based on HTTP version, headers etc.</p>

<p>Since the jDISC API is fully asynchronous, operations can occur in any order. This is very extensively tested in the
    HTTP server implementation. For instance, an application (<code>RequestHandler</code>) may choose to respond and
    close the output <code>ContentChannel</code> immediately upon receiving the request, before the body of the request
    has been written into the input <code>ContentChannel</code> of the <code>RequestHandler</code>. All such cases are
    tested and properly handled.</p>

<p>As one can see from the illustration, <code>ChannelContext</code> is also a <code>Runnable</code>, i.e. it keeps one
    thread per channel. The HTTP server has two modes of operation, <code>optimizeForPipeline</code> <code>true</code>
    or <code>false</code> in <code>HttpServerConfig</code>.</p>

<p>If <code>optimizeForPipeline</code> is set to <code>true</code>, response chunks are enqueued on a blocking queue in
    <code>ChannelContext</code> when <code>ContentChannel.write()</code> is called. The <code>ChannelContext</code>
    thread is responsible for actually writing them, and closing the channel when appropriate. Since the HTTP server
    supports pipelining, and writes from an application may occur in any order, special care is taken to write response
    chunks in the correct order.</p>

<p>If <code>optimizeForPipeline</code> is set to <code>false</code>, a call to <code>ContentChannel.write()</code> will
    lead to an actual write on the wire, iff. the given chunk to be written is the next in line. Otherwise this is a
    no-op. This also means that a <code>ContentChannel.write()</code> may lead to a cascade of writes that have been
    enqueued since they were out-of-order when their <code>write()</code> was called. The <code>ChannelContext</code>
    thread still takes care of channel closing in most cases.</p>
</body>
</html>