diff options
| author | mo khan <mo@mokhan.ca> | 2013-08-28 07:20:13 -0600 |
|---|---|---|
| committer | mo khan <mo@mokhan.ca> | 2013-08-28 07:20:13 -0600 |
| commit | db1191ec0e7305d684383f8974e2fa437f77ad5a (patch) | |
| tree | 5e27b197dff849d22d8e1f50eb75aa499b16bd06 /code/spyglass/docs | |
Diffstat (limited to 'code/spyglass/docs')
18 files changed, 2230 insertions, 0 deletions
diff --git a/code/spyglass/docs/configurator.html b/code/spyglass/docs/configurator.html new file mode 100644 index 0000000..69c19a5 --- /dev/null +++ b/code/spyglass/docs/configurator.html @@ -0,0 +1,137 @@ +<!DOCTYPE html> + +<html> +<head> + <title>configurator.rb</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> + <link rel="stylesheet" media="all" href="docco.css" /> +</head> +<body> + <div id="container"> + <div id="background"></div> + + <ul id="jump_to"> + <li> + <a class="large" href="javascript:void(0);">Jump To …</a> + <a class="small" href="javascript:void(0);">+</a> + <div id="jump_wrapper"> + <div id="jump_page"> + + + <a class="source" href="configurator.html"> + configurator.rb + </a> + + + <a class="source" href="logging.html"> + logging.rb + </a> + + + <a class="source" href="lookout.html"> + lookout.rb + </a> + + + <a class="source" href="master.html"> + master.rb + </a> + + + <a class="source" href="server.html"> + server.rb + </a> + + + <a class="source" href="worker.html"> + worker.rb + </a> + + </div> + </li> + </ul> + + <ul class="sections"> + + <li id="title"> + <div class="annotation"> + <h1>configurator.rb</h1> + </div> + </li> + + + + <li id="section-1"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-1">¶</a> + </div> + + </div> + + <div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">module</span> <span class="title">Spyglass</span></span> + <span class="class"><span class="keyword">class</span> <span class="title">Configurator</span></span></pre></div></div> + + </li> + + + <li id="section-2"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-2">¶</a> + </div> + <p>A hash of key => default</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="constant">OPTIONS</span> = { + <span class="symbol">:port</span> => <span class="number">4222</span>, + <span class="symbol">:host</span> => <span class="string">'0.0.0.0'</span>, + <span class="symbol">:workers</span> => <span class="number">2</span>, + <span class="symbol">:timeout</span> => <span class="number">30</span>, + <span class="symbol">:config_ru_path</span> => <span class="string">'config.ru'</span>, + <span class="symbol">:verbose</span> => <span class="keyword">false</span>, + <span class="symbol">:vverbose</span> => <span class="keyword">false</span> + } + + <span class="class"><span class="keyword">class</span> <span class="inheritance"><</span><span class="inheritance">< <span class="parent">self</span></span></span> + <span class="constant">OPTIONS</span>.each <span class="keyword">do</span> |key, default|</pre></div></div> + + </li> + + + <li id="section-3"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-3">¶</a> + </div> + <p>attr_writer key</p> + + </div> + + <div class="content"><div class='highlight'><pre> + define_method(key) <span class="keyword">do</span> |*args| + arg = args.shift + <span class="keyword">if</span> arg + instance_variable_set(<span class="string">"@<span class="subst">#{key}</span>"</span>, arg) + <span class="keyword">else</span> + instance_variable_get(<span class="string">"@<span class="subst">#{key}</span>"</span>) || default + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + + <span class="constant">Config</span> = <span class="constant">Configurator</span> +<span class="keyword">end</span></pre></div></div> + + </li> + + </ul> + </div> +</body> +</html> diff --git a/code/spyglass/docs/docco.css b/code/spyglass/docs/docco.css new file mode 100644 index 0000000..f690a07 --- /dev/null +++ b/code/spyglass/docs/docco.css @@ -0,0 +1,500 @@ +/*--------------------- Typography ----------------------------*/ + +@font-face { + font-family: 'aller-light'; + src: url('public/fonts/aller-light.eot'); + src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'), + url('public/fonts/aller-light.woff') format('woff'), + url('public/fonts/aller-light.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'aller-bold'; + src: url('public/fonts/aller-bold.eot'); + src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'), + url('public/fonts/aller-bold.woff') format('woff'), + url('public/fonts/aller-bold.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: 'novecento-bold'; + src: url('public/fonts/novecento-bold.eot'); + src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'), + url('public/fonts/novecento-bold.woff') format('woff'), + url('public/fonts/novecento-bold.ttf') format('truetype'); + font-weight: normal; + font-style: normal; +} + +/*--------------------- Layout ----------------------------*/ +html { height: 100%; } +body { + font-family: "aller-light"; + font-size: 14px; + line-height: 18px; + color: #30404f; + margin: 0; padding: 0; + height:100%; +} +#container { min-height: 100%; } + +a { + color: #000; +} + +b, strong { + font-weight: normal; + font-family: "aller-bold"; +} + +p, ul, ol { + margin: 15px 0 0px; +} + +h1, h2, h3, h4, h5, h6 { + color: #112233; + line-height: 1em; + font-weight: normal; + font-family: "novecento-bold"; + text-transform: uppercase; + margin: 30px 0 15px 0; +} + +h1 { + margin-top: 40px; +} + +hr { + border: 0; + background: 1px solid #ddd; + height: 1px; + margin: 20px 0; +} + +pre, tt, code { + font-size: 12px; line-height: 16px; + font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; +} + .annotation pre { + display: block; + margin: 0; + padding: 7px 10px; + background: #fcfcfc; + -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); + -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1); + box-shadow: inset 0 0 10px rgba(0,0,0,0.1); + overflow-x: auto; + } + .annotation pre code { + border: 0; + padding: 0; + background: transparent; + } + + +blockquote { + border-left: 5px solid #ccc; + margin: 0; + padding: 1px 0 1px 1em; +} + .sections blockquote p { + font-family: Menlo, Consolas, Monaco, monospace; + font-size: 12px; line-height: 16px; + color: #999; + margin: 10px 0 0; + white-space: pre-wrap; + } + +ul.sections { + list-style: none; + padding:0 0 5px 0;; + margin:0; +} + +/* + Force border-box so that % widths fit the parent + container without overlap because of margin/padding. + + More Info : http://www.quirksmode.org/css/box.html +*/ +ul.sections > li > div { + -moz-box-sizing: border-box; /* firefox */ + -ms-box-sizing: border-box; /* ie */ + -webkit-box-sizing: border-box; /* webkit */ + -khtml-box-sizing: border-box; /* konqueror */ + box-sizing: border-box; /* css3 */ +} + + +/*---------------------- Jump Page -----------------------------*/ +#jump_to, #jump_page { + margin: 0; + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 16px Arial; + cursor: pointer; + text-align: right; + list-style: none; +} + +#jump_to a { + text-decoration: none; +} + +#jump_to a.large { + display: none; +} +#jump_to a.small { + font-size: 22px; + font-weight: bold; + color: #676767; +} + +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 10px 15px; + margin:0; +} + +#jump_wrapper { + display: none; + padding:0; +} + +#jump_to:hover #jump_wrapper { + display: block; +} + +#jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; +} + +#jump_page .source { + display: block; + padding: 15px; + text-decoration: none; + border-top: 1px solid #eee; +} + +#jump_page .source:hover { + background: #f5f5ff; +} + +#jump_page .source:first-child { +} + +/*---------------------- Low resolutions (> 320px) ---------------------*/ +@media only screen and (min-width: 320px) { + .pilwrap { display: none; } + + ul.sections > li > div { + display: block; + padding:5px 10px 0 10px; + } + + ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { + padding-left: 30px; + } + + ul.sections > li > div.content { + background: #f5f5ff; + overflow-x:auto; + -webkit-box-shadow: inset 0 0 5px #e5e5ee; + box-shadow: inset 0 0 5px #e5e5ee; + border: 1px solid #dedede; + margin:5px 10px 5px 10px; + padding-bottom: 5px; + } + + ul.sections > li > div.annotation pre { + margin: 7px 0 7px; + padding-left: 15px; + } + + ul.sections > li > div.annotation p tt, .annotation code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; + } +} + +/*---------------------- (> 481px) ---------------------*/ +@media only screen and (min-width: 481px) { + #container { + position: relative; + } + body { + background-color: #F5F5FF; + font-size: 15px; + line-height: 21px; + } + pre, tt, code { + line-height: 18px; + } + p, ul, ol { + margin: 0 0 15px; + } + + + #jump_to { + padding: 5px 10px; + } + #jump_wrapper { + padding: 0; + } + #jump_to, #jump_page { + font: 10px Arial; + text-transform: uppercase; + } + #jump_page .source { + padding: 5px 10px; + } + #jump_to a.large { + display: inline-block; + } + #jump_to a.small { + display: none; + } + + + + #background { + position: absolute; + top: 0; bottom: 0; + width: 350px; + background: #fff; + border-right: 1px solid #e5e5ee; + z-index: -1; + } + + ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol { + padding-left: 40px; + } + + ul.sections > li { + white-space: nowrap; + } + + ul.sections > li > div { + display: inline-block; + } + + ul.sections > li > div.annotation { + max-width: 350px; + min-width: 350px; + min-height: 5px; + padding: 13px; + overflow-x: hidden; + white-space: normal; + vertical-align: top; + text-align: left; + } + ul.sections > li > div.annotation pre { + margin: 15px 0 15px; + padding-left: 15px; + } + + ul.sections > li > div.content { + padding: 13px; + vertical-align: top; + background: #f5f5ff; + border: none; + -webkit-box-shadow: none; + box-shadow: none; + } + + .pilwrap { + position: relative; + display: inline; + } + + .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + } + .for-h1 .pilcrow { + top: 47px; + } + .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow { + top: 35px; + } + + ul.sections > li > div.annotation:hover .pilcrow { + opacity: 1; + } +} + +/*---------------------- (> 1025px) ---------------------*/ +@media only screen and (min-width: 1025px) { + + body { + font-size: 16px; + line-height: 24px; + } + + #background { + width: 525px; + } + ul.sections > li > div.annotation { + max-width: 525px; + min-width: 525px; + padding: 10px 25px 1px 50px; + } + ul.sections > li > div.content { + padding: 9px 15px 16px 25px; + } +} + +/*---------------------- Syntax Highlighting -----------------------------*/ + +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +/* + +github.com style (c) Vasily Polovnyov <vast@whiteants.net> + +*/ + +pre code { + display: block; padding: 0.5em; + color: #000; + background: #f8f8ff +} + +pre .comment, +pre .template_comment, +pre .diff .header, +pre .javadoc { + color: #408080; + font-style: italic +} + +pre .keyword, +pre .assignment, +pre .literal, +pre .css .rule .keyword, +pre .winutils, +pre .javascript .title, +pre .lisp .title, +pre .subst { + color: #954121; + /*font-weight: bold*/ +} + +pre .number, +pre .hexcolor { + color: #40a070 +} + +pre .string, +pre .tag .value, +pre .phpdoc, +pre .tex .formula { + color: #219161; +} + +pre .title, +pre .id { + color: #19469D; +} +pre .params { + color: #00F; +} + +pre .javascript .title, +pre .lisp .title, +pre .subst { + font-weight: normal +} + +pre .class .title, +pre .haskell .label, +pre .tex .command { + color: #458; + font-weight: bold +} + +pre .tag, +pre .tag .title, +pre .rules .property, +pre .django .tag .keyword { + color: #000080; + font-weight: normal +} + +pre .attribute, +pre .variable, +pre .instancevar, +pre .lisp .body { + color: #008080 +} + +pre .regexp { + color: #B68 +} + +pre .class { + color: #458; + font-weight: bold +} + +pre .symbol, +pre .ruby .symbol .string, +pre .ruby .symbol .keyword, +pre .ruby .symbol .keymethods, +pre .lisp .keyword, +pre .tex .special, +pre .input_number { + color: #990073 +} + +pre .builtin, +pre .constructor, +pre .built_in, +pre .lisp .title { + color: #0086b3 +} + +pre .preprocessor, +pre .pi, +pre .doctype, +pre .shebang, +pre .cdata { + color: #999; + font-weight: bold +} + +pre .deletion { + background: #fdd +} + +pre .addition { + background: #dfd +} + +pre .diff .change { + background: #0086b3 +} + +pre .chunk { + color: #aaa +} + +pre .tex .formula { + opacity: 0.5; +} diff --git a/code/spyglass/docs/logging.html b/code/spyglass/docs/logging.html new file mode 100644 index 0000000..5c4262a --- /dev/null +++ b/code/spyglass/docs/logging.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> + +<html> +<head> + <title>logging.rb</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> + <link rel="stylesheet" media="all" href="docco.css" /> +</head> +<body> + <div id="container"> + <div id="background"></div> + + <ul id="jump_to"> + <li> + <a class="large" href="javascript:void(0);">Jump To …</a> + <a class="small" href="javascript:void(0);">+</a> + <div id="jump_wrapper"> + <div id="jump_page"> + + + <a class="source" href="configurator.html"> + configurator.rb + </a> + + + <a class="source" href="logging.html"> + logging.rb + </a> + + + <a class="source" href="lookout.html"> + lookout.rb + </a> + + + <a class="source" href="master.html"> + master.rb + </a> + + + <a class="source" href="server.html"> + server.rb + </a> + + + <a class="source" href="worker.html"> + worker.rb + </a> + + </div> + </li> + </ul> + + <ul class="sections"> + + <li id="title"> + <div class="annotation"> + <h1>logging.rb</h1> + </div> + </li> + + + + <li id="section-1"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-1">¶</a> + </div> + + </div> + + <div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">module</span> <span class="title">Spyglass</span></span> + <span class="class"><span class="keyword">module</span> <span class="title">Logging</span></span> + <span class="function"><span class="keyword">def</span> <span class="title">out</span><span class="params">(message)</span></span> + <span class="variable">$stdout</span>.puts preamble + message + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">err</span><span class="params">(message)</span></span> + <span class="variable">$stderr</span>.puts preamble + message + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">verbose</span><span class="params">(message)</span></span> + <span class="keyword">return</span> <span class="keyword">unless</span> <span class="constant">Config</span>.verbose + out(message) + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">vverbose</span><span class="params">(message)</span></span> + <span class="keyword">return</span> <span class="keyword">unless</span> <span class="constant">Config</span>.vverbose + out(message) + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">preamble</span></span> + <span class="string">"[<span class="subst">#{<span class="constant">Process</span>.pid}</span>] [<span class="subst">#{<span class="keyword">self</span>.<span class="keyword">class</span>.name}</span>] "</span> + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span></pre></div></div> + + </li> + + </ul> + </div> +</body> +</html> diff --git a/code/spyglass/docs/lookout.html b/code/spyglass/docs/lookout.html new file mode 100644 index 0000000..a017f33 --- /dev/null +++ b/code/spyglass/docs/lookout.html @@ -0,0 +1,325 @@ +<!DOCTYPE html> + +<html> +<head> + <title>lookout.rb</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> + <link rel="stylesheet" media="all" href="docco.css" /> +</head> +<body> + <div id="container"> + <div id="background"></div> + + <ul id="jump_to"> + <li> + <a class="large" href="javascript:void(0);">Jump To …</a> + <a class="small" href="javascript:void(0);">+</a> + <div id="jump_wrapper"> + <div id="jump_page"> + + + <a class="source" href="configurator.html"> + configurator.rb + </a> + + + <a class="source" href="logging.html"> + logging.rb + </a> + + + <a class="source" href="lookout.html"> + lookout.rb + </a> + + + <a class="source" href="master.html"> + master.rb + </a> + + + <a class="source" href="server.html"> + server.rb + </a> + + + <a class="source" href="worker.html"> + worker.rb + </a> + + </div> + </li> + </ul> + + <ul class="sections"> + + <li id="title"> + <div class="annotation"> + <h1>lookout.rb</h1> + </div> + </li> + + + + <li id="section-1"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-1">¶</a> + </div> + + </div> + + <div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">module</span> <span class="title">Spyglass</span></span> + <span class="class"><span class="keyword">class</span> <span class="title">Lookout</span></span> + <span class="keyword">include</span> <span class="constant">Singleton</span>, <span class="constant">Logging</span></pre></div></div> + + </li> + + + <li id="section-2"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-2">¶</a> + </div> + <p>This method is the main entry point for the Lookout class. It takes +a socket object.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="function"><span class="keyword">def</span> <span class="title">start</span><span class="params">(socket)</span></span> + trap_signals</pre></div></div> + + </li> + + + <li id="section-3"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-3">¶</a> + </div> + <p>The Lookout doesn't know anything about the app itself, so there's +no app related setup to do here.</p> + + </div> + + <div class="content"><div class='highlight'><pre> loop <span class="keyword">do</span></pre></div></div> + + </li> + + + <li id="section-4"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-4">¶</a> + </div> + <p>Accepts a new connection on our socket. This class won't actually +do anything interesting with this connection, it will pass it down +to the <code>Master</code> class created below to do the actual request handling.</p> + + </div> + + <div class="content"><div class='highlight'><pre> conn = socket.accept + out <span class="string">"Received incoming connection"</span></pre></div></div> + + </li> + + + <li id="section-5"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-5">¶</a> + </div> + <p>In this block the Lookout forks a new process and invokes a Master, +passing along the socket it received and the connection it accepted +above.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="variable">@master_pid</span> = fork <span class="keyword">do</span> + master = <span class="constant">Master</span>.new(conn, socket) + master.start + <span class="keyword">end</span></pre></div></div> + + </li> + + + <li id="section-6"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-6">¶</a> + </div> + <p>The Lookout can now close its handle on the client socket. This doesn't +translate to the socket being closed on the clients end because the +forked Master process also has a handle on the same socket. Since this +handle is now cleaned up it's up to the Master process to ensure that +its handle gets cleaned up.</p> + + </div> + + <div class="content"><div class='highlight'><pre> conn.close</pre></div></div> + + </li> + + + <li id="section-7"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-7">¶</a> + </div> + <p>Now this process blocks until the Master process exits. The Master process +will only exit once traffic is slow enough that it has reached its timeout +without receiving any new connections.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="constant">Process</span>.waitpid(<span class="variable">@master_pid</span>)</pre></div></div> + + </li> + + + <li id="section-8"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-8">¶</a> + </div> + <p>The interaction of fork(2)/waitpid(2) above deserve some explanation.</p> + + </div> + + </li> + + + <li id="section-9"> + <div class="annotation"> + + <div class="pilwrap for-h3"> + <a class="pilcrow" href="#section-9">¶</a> + </div> + <h3>Why fork(2)? Why not just spin up the Master?</h3> +<p>The whole point of the Lookout process is to be very lean. The only resource +that it initializes is the listening socket for the server. It doesn't load +any of your application into memory, so its resource footprint is very small.</p> + + </div> + + </li> + + + <li id="section-10"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-10">¶</a> + </div> + <p>The reason that it does a fork(2) before invoking the Master is because once +the Master times out we want the Lookout process to remain lean when accepting +the next connection. </p> + + </div> + + </li> + + + <li id="section-11"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-11">¶</a> + </div> + <p>If it were to load the application code without forking +then there would be no (simple) way for it to later unload the application code.</p> + + </div> + + </li> + + + <li id="section-12"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-12">¶</a> + </div> + <p>By doing a fork(2), then waiting for the Master process to exit, that guarantees +that all resources (notably memory usage) that were in use by the Master process +will be reclaimed by the kernel. </p> + + </div> + + </li> + + + <li id="section-13"> + <div class="annotation"> + + <div class="pilwrap for-h3"> + <a class="pilcrow" href="#section-13">¶</a> + </div> + <h3>Who knows what your app will demand!</h3> +<p>While handling requests your app may require lots of memory. Containing this in a +child process, and exiting that process, is the easiest way to ensure that memory +bloat isn't shared with our simple parent process.</p> + + </div> + + </li> + + + <li id="section-14"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-14">¶</a> + </div> + <p>This allows our Lookout process will to go back around +the loop with nothing more than it started with, just a listening socket.</p> + + </div> + + </li> + + + <li id="section-15"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-15">¶</a> + </div> + <p>The fork(2)/waitpid(2) approach requires little code to implement, and pushes +responsibility down to the kernel to track resource usage and nicely clean up +the Master process when it's finished.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="keyword">end</span> + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">trap_signals</span></span> + [<span class="symbol">:INT</span>, <span class="symbol">:QUIT</span>].each <span class="keyword">do</span> |sig| + trap(sig) { + <span class="keyword">begin</span> + <span class="constant">Process</span>.kill(sig, <span class="variable">@master_pid</span>) <span class="keyword">if</span> <span class="variable">@master_pid</span> + <span class="keyword">rescue</span> <span class="constant">Errno::ESRCH</span> + <span class="keyword">end</span> + exit + } + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span></pre></div></div> + + </li> + + </ul> + </div> +</body> +</html> diff --git a/code/spyglass/docs/master.html b/code/spyglass/docs/master.html new file mode 100644 index 0000000..11f2abc --- /dev/null +++ b/code/spyglass/docs/master.html @@ -0,0 +1,268 @@ +<!DOCTYPE html> + +<html> +<head> + <title>master.rb</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> + <link rel="stylesheet" media="all" href="docco.css" /> +</head> +<body> + <div id="container"> + <div id="background"></div> + + <ul id="jump_to"> + <li> + <a class="large" href="javascript:void(0);">Jump To …</a> + <a class="small" href="javascript:void(0);">+</a> + <div id="jump_wrapper"> + <div id="jump_page"> + + + <a class="source" href="configurator.html"> + configurator.rb + </a> + + + <a class="source" href="logging.html"> + logging.rb + </a> + + + <a class="source" href="lookout.html"> + lookout.rb + </a> + + + <a class="source" href="master.html"> + master.rb + </a> + + + <a class="source" href="server.html"> + server.rb + </a> + + + <a class="source" href="worker.html"> + worker.rb + </a> + + </div> + </li> + </ul> + + <ul class="sections"> + + <li id="title"> + <div class="annotation"> + <h1>master.rb</h1> + </div> + </li> + + + + <li id="section-1"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-1">¶</a> + </div> + + </div> + + <div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">module</span> <span class="title">Spyglass</span></span> + <span class="class"><span class="keyword">class</span> <span class="title">Master</span></span> + <span class="keyword">include</span> <span class="constant">Logging</span> + + <span class="function"><span class="keyword">def</span> <span class="title">initialize</span><span class="params">(connection, socket)</span></span> + <span class="variable">@connection</span>, <span class="variable">@socket</span> = connection, socket + <span class="variable">@worker_pids</span> = []</pre></div></div> + + </li> + + + <li id="section-2"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-2">¶</a> + </div> + <p>The Master shares this pipe with each of its worker processes. It +passes the writable end down to each spawned worker while it listens +on the readable end. Each worker will write to the pipe each time +it accepts a new connection. If The Master doesn't get anything on +the pipe before <code>Config.timeout</code> elapses then it kills its workers +and exits. </p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="variable">@readable_pipe</span>, <span class="variable">@writable_pipe</span> = <span class="constant">IO</span>.pipe + <span class="keyword">end</span></pre></div></div> + + </li> + + + <li id="section-3"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-3">¶</a> + </div> + <p>This method starts the Master. It enters an infinite loop where it creates +processes to handle web requests and ensures that they stay active. It takes +a connection as an argument from the Lookout instance. A Master will only +be started when a connection is received by the Lookout.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="function"><span class="keyword">def</span> <span class="title">start</span></span> + trap_signals + + load_app + out <span class="string">"Loaded the app"</span></pre></div></div> + + </li> + + + <li id="section-4"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-4">¶</a> + </div> + <p>The first worker we spawn has to handle the connection that was already +passed to us.</p> + + </div> + + <div class="content"><div class='highlight'><pre> spawn_worker(<span class="variable">@connection</span>)</pre></div></div> + + </li> + + + <li id="section-5"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-5">¶</a> + </div> + <p>The Master can now close its handle on the client socket since the +forked worker also got a handle on the same socket. Since this one +is now closed it's up to the Worker process to close its handle when +it's done. At that point the client connection will perceive that +it's been closed on their end.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="variable">@connection</span>.close</pre></div></div> + + </li> + + + <li id="section-6"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-6">¶</a> + </div> + <p>We spawn the rest of the workers.</p> + + </div> + + <div class="content"><div class='highlight'><pre> (<span class="constant">Config</span>.workers - <span class="number">1</span>).times { spawn_worker } + out <span class="string">"Spawned <span class="subst">#{<span class="constant">Config</span>.workers}</span> workers. Babysitting now..."</span> + + loop <span class="keyword">do</span> + <span class="keyword">if</span> timed_out?(<span class="constant">IO</span>.select([<span class="variable">@readable_pipe</span>], <span class="keyword">nil</span>, <span class="keyword">nil</span>, <span class="constant">Config</span>.timeout)) + out <span class="string">"Timed out after <span class="subst">#{<span class="constant">Config</span>.timeout}</span> s. Exiting."</span> + + kill_workers(<span class="symbol">:QUIT</span>) + exit + <span class="keyword">else</span></pre></div></div> + + </li> + + + <li id="section-7"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-7">¶</a> + </div> + <p>Clear the data on the pipe so it doesn't appear to be readable +next time around the loop.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="variable">@readable_pipe</span>.read_nonblock <span class="number">1</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">timed_out?</span><span class="params">(select_result)</span></span> + !select_result + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">spawn_worker</span><span class="params">(connection = <span class="keyword">nil</span>)</span></span> + <span class="variable">@worker_pids</span> << fork { <span class="constant">Worker</span>.new(<span class="variable">@socket</span>, <span class="variable">@app</span>, <span class="variable">@writable_pipe</span>, connection).start } + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">trap_signals</span></span></pre></div></div> + + </li> + + + <li id="section-8"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-8">¶</a> + </div> + <p>The QUIT signal triggers a graceful shutdown. The master shuts down +immediately and lets each worker finish the request they are currently +processing.</p> + + </div> + + <div class="content"><div class='highlight'><pre> trap(<span class="symbol">:QUIT</span>) <span class="keyword">do</span> + verbose <span class="string">"Received QUIT"</span> + + kill_workers(<span class="symbol">:QUIT</span>) + exit + <span class="keyword">end</span> + + trap(<span class="symbol">:CHLD</span>) <span class="keyword">do</span> + dead_worker = <span class="constant">Process</span>.wait + <span class="variable">@worker_pids</span>.delete(dead_worker) + + <span class="variable">@worker_pids</span>.each <span class="keyword">do</span> |wpid| + <span class="keyword">begin</span> + dead_worker = <span class="constant">Process</span>.waitpid(wpid, <span class="constant">Process::WNOHANG</span>) + <span class="variable">@worker_pids</span>.delete(dead_worker) + <span class="keyword">rescue</span> <span class="constant">Errno::ECHILD</span> + <span class="keyword">end</span> + <span class="keyword">end</span> + + spawn_worker + <span class="keyword">end</span> + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">kill_workers</span><span class="params">(sig)</span></span> + <span class="variable">@worker_pids</span>.each <span class="keyword">do</span> |wpid| + <span class="constant">Process</span>.kill(sig, wpid) + <span class="keyword">end</span> + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">load_app</span></span> + <span class="variable">@app</span>, options = <span class="constant">Rack::Builder</span>.parse_file(<span class="constant">Config</span>.config_ru_path) + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span></pre></div></div> + + </li> + + </ul> + </div> +</body> +</html> diff --git a/code/spyglass/docs/public/fonts/aller-bold.eot b/code/spyglass/docs/public/fonts/aller-bold.eot Binary files differnew file mode 100755 index 0000000..1b32532 --- /dev/null +++ b/code/spyglass/docs/public/fonts/aller-bold.eot diff --git a/code/spyglass/docs/public/fonts/aller-bold.ttf b/code/spyglass/docs/public/fonts/aller-bold.ttf Binary files differnew file mode 100755 index 0000000..dc4cc9c --- /dev/null +++ b/code/spyglass/docs/public/fonts/aller-bold.ttf diff --git a/code/spyglass/docs/public/fonts/aller-bold.woff b/code/spyglass/docs/public/fonts/aller-bold.woff Binary files differnew file mode 100755 index 0000000..fa16fd0 --- /dev/null +++ b/code/spyglass/docs/public/fonts/aller-bold.woff diff --git a/code/spyglass/docs/public/fonts/aller-light.eot b/code/spyglass/docs/public/fonts/aller-light.eot Binary files differnew file mode 100755 index 0000000..40bd654 --- /dev/null +++ b/code/spyglass/docs/public/fonts/aller-light.eot diff --git a/code/spyglass/docs/public/fonts/aller-light.ttf b/code/spyglass/docs/public/fonts/aller-light.ttf Binary files differnew file mode 100755 index 0000000..c2c7290 --- /dev/null +++ b/code/spyglass/docs/public/fonts/aller-light.ttf diff --git a/code/spyglass/docs/public/fonts/aller-light.woff b/code/spyglass/docs/public/fonts/aller-light.woff Binary files differnew file mode 100755 index 0000000..81a09d1 --- /dev/null +++ b/code/spyglass/docs/public/fonts/aller-light.woff diff --git a/code/spyglass/docs/public/fonts/novecento-bold.eot b/code/spyglass/docs/public/fonts/novecento-bold.eot Binary files differnew file mode 100755 index 0000000..98a9a7f --- /dev/null +++ b/code/spyglass/docs/public/fonts/novecento-bold.eot diff --git a/code/spyglass/docs/public/fonts/novecento-bold.ttf b/code/spyglass/docs/public/fonts/novecento-bold.ttf Binary files differnew file mode 100755 index 0000000..2af39b0 --- /dev/null +++ b/code/spyglass/docs/public/fonts/novecento-bold.ttf diff --git a/code/spyglass/docs/public/fonts/novecento-bold.woff b/code/spyglass/docs/public/fonts/novecento-bold.woff Binary files differnew file mode 100755 index 0000000..de558b5 --- /dev/null +++ b/code/spyglass/docs/public/fonts/novecento-bold.woff diff --git a/code/spyglass/docs/public/stylesheets/normalize.css b/code/spyglass/docs/public/stylesheets/normalize.css new file mode 100644 index 0000000..73abb76 --- /dev/null +++ b/code/spyglass/docs/public/stylesheets/normalize.css @@ -0,0 +1,375 @@ +/*! normalize.css v2.0.1 | MIT License | git.io/normalize */ + +/* ========================================================================== + HTML5 display definitions + ========================================================================== */ + +/* + * Corrects `block` display not defined in IE 8/9. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section, +summary { + display: block; +} + +/* + * Corrects `inline-block` display not defined in IE 8/9. + */ + +audio, +canvas, +video { + display: inline-block; +} + +/* + * Prevents modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/* + * Addresses styling for `hidden` attribute not present in IE 8/9. + */ + +[hidden] { + display: none; +} + +/* ========================================================================== + Base + ========================================================================== */ + +/* + * 1. Sets default font family to sans-serif. + * 2. Prevents iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -webkit-text-size-adjust: 100%; /* 2 */ + -ms-text-size-adjust: 100%; /* 2 */ +} + +/* + * Removes default margin. + */ + +body { + margin: 0; +} + +/* ========================================================================== + Links + ========================================================================== */ + +/* + * Addresses `outline` inconsistency between Chrome and other browsers. + */ + +a:focus { + outline: thin dotted; +} + +/* + * Improves readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* ========================================================================== + Typography + ========================================================================== */ + +/* + * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, + * Safari 5, and Chrome. + */ + +h1 { + font-size: 2em; +} + +/* + * Addresses styling not present in IE 8/9, Safari 5, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/* + * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/* + * Addresses styling not present in Safari 5 and Chrome. + */ + +dfn { + font-style: italic; +} + +/* + * Addresses styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + + +/* + * Corrects font family set oddly in Safari 5 and Chrome. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, serif; + font-size: 1em; +} + +/* + * Improves readability of pre-formatted text in all browsers. + */ + +pre { + white-space: pre; + white-space: pre-wrap; + word-wrap: break-word; +} + +/* + * Sets consistent quote types. + */ + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +/* + * Addresses inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/* + * Prevents `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* ========================================================================== + Embedded content + ========================================================================== */ + +/* + * Removes border when inside `a` element in IE 8/9. + */ + +img { + border: 0; +} + +/* + * Corrects overflow displayed oddly in IE 9. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* ========================================================================== + Figures + ========================================================================== */ + +/* + * Addresses margin not present in IE 8/9 and Safari 5. + */ + +figure { + margin: 0; +} + +/* ========================================================================== + Forms + ========================================================================== */ + +/* + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/* + * 1. Corrects color not being inherited in IE 8/9. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/* + * 1. Corrects font family not being inherited in all browsers. + * 2. Corrects font size not being inherited in all browsers. + * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome + */ + +button, +input, +select, +textarea { + font-family: inherit; /* 1 */ + font-size: 100%; /* 2 */ + margin: 0; /* 3 */ +} + +/* + * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +button, +input { + line-height: normal; +} + +/* + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Corrects inability to style clickable `input` types in iOS. + * 3. Improves usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/* + * Re-set default cursor for disabled elements. + */ + +button[disabled], +input[disabled] { + cursor: default; +} + +/* + * 1. Addresses box sizing set to `content-box` in IE 8/9. + * 2. Removes excess padding in IE 8/9. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/* + * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. + * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/* + * Removes inner padding and search cancel button in Safari 5 and Chrome + * on OS X. + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* + * Removes inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/* + * 1. Removes default vertical scrollbar in IE 8/9. + * 2. Improves readability and alignment in all browsers. + */ + +textarea { + overflow: auto; /* 1 */ + vertical-align: top; /* 2 */ +} + +/* ========================================================================== + Tables + ========================================================================== */ + +/* + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +}
\ No newline at end of file diff --git a/code/spyglass/docs/server.html b/code/spyglass/docs/server.html new file mode 100644 index 0000000..1f5a12b --- /dev/null +++ b/code/spyglass/docs/server.html @@ -0,0 +1,109 @@ +<!DOCTYPE html> + +<html> +<head> + <title>server.rb</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> + <link rel="stylesheet" media="all" href="docco.css" /> +</head> +<body> + <div id="container"> + <div id="background"></div> + + <ul id="jump_to"> + <li> + <a class="large" href="javascript:void(0);">Jump To …</a> + <a class="small" href="javascript:void(0);">+</a> + <div id="jump_wrapper"> + <div id="jump_page"> + + + <a class="source" href="configurator.html"> + configurator.rb + </a> + + + <a class="source" href="logging.html"> + logging.rb + </a> + + + <a class="source" href="lookout.html"> + lookout.rb + </a> + + + <a class="source" href="master.html"> + master.rb + </a> + + + <a class="source" href="server.html"> + server.rb + </a> + + + <a class="source" href="worker.html"> + worker.rb + </a> + + </div> + </li> + </ul> + + <ul class="sections"> + + <li id="title"> + <div class="annotation"> + <h1>server.rb</h1> + </div> + </li> + + + + <li id="section-1"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-1">¶</a> + </div> + + </div> + + <div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">module</span> <span class="title">Spyglass</span></span> + + <span class="class"><span class="keyword">class</span> <span class="title">Server</span></span> + <span class="keyword">include</span> <span class="constant">Singleton</span> + <span class="keyword">include</span> <span class="constant">Logging</span> + + <span class="function"><span class="keyword">def</span> <span class="title">start</span></span></pre></div></div> + + </li> + + + <li id="section-2"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-2">¶</a> + </div> + <p>Opens the main listening socket for the server. Now the server is responsive to +incoming connections.</p> + + </div> + + <div class="content"><div class='highlight'><pre> sock = <span class="constant">TCPServer</span>.open(<span class="constant">Config</span>.host, <span class="constant">Config</span>.port) + out <span class="string">"Listening on port <span class="subst">#{<span class="constant">Config</span>.host}</span>:<span class="subst">#{<span class="constant">Config</span>.port}</span>"</span> + + <span class="constant">Lookout</span>.instance.start(sock) + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span></pre></div></div> + + </li> + + </ul> + </div> +</body> +</html> diff --git a/code/spyglass/docs/spyglass.html b/code/spyglass/docs/spyglass.html new file mode 100644 index 0000000..3c317a4 --- /dev/null +++ b/code/spyglass/docs/spyglass.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> + +<html> +<head> + <title>Spyglass</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> + <link rel="stylesheet" media="all" href="docco.css" /> +</head> +<body> + <div id="container"> + <div id="background"></div> + + <ul class="sections"> + + + + <li id="section-1"> + <div class="annotation"> + + <div class="pilwrap for-h1"> + <a class="pilcrow" href="#section-1">¶</a> + </div> + <h1>Spyglass</h1> + + </div> + + </li> + + + <li id="section-2"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-2">¶</a> + </div> + <p>This is Spyglass, a Rack web server that rides on Unix designed to be simple and teach +others about Unix programming.</p> +<p>It's namesake comes from the fact that when it boots up it's nothing more than a lone socket +keeping a lookout for incoming connections. </p> +<p>When a connection comes in it spins up a Master +process which preforks some workers to actually handle http requests. If the Master process is +left idle long enough it will shut itself (and it's workers) down and go back to just a lone +listening socket, on the lookout for incoming connections.</p> +<h1>Components</h1> + + </div> + + </li> + + + <li id="section-3"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-3">¶</a> + </div> + <ul> +<li><p><a href="server.html">Server</a> gets the ball rolling. +The role of Server is pretty minimal. It opens the initial listening TCP socket, +then passes that socket onto the Lookout. The Lookout will actually handle reading +from the socket.</p> +</li> +<li><p><a href="lookout.html">Lookout</a> keeps a watch and notifies others when a connection +comes in. +The Lookout is a pretty 'dumb' object. All that it does is listen for incoming +connections on the socket it's given. Once it receives a connection it does a fork(2) +and invokes a Master process. The Master process actually handles the connection.</p> +</li> +<li><p><a href="master.html">Master</a> loads the application and babysits worker processes +that actually talk to clients. +The role of the Master class is to create and babysit worker processes +that will actually handle web requests. The Master itself doesn't know +anything about http, etc. it just knows how to manage processes.</p> +</li> +<li><p><a href="worker.html">Worker</a> parses HTTP, calls the app, and writes back to the client.</p> +</li> +</ul> + + </div> + + <div class="content"><div class='highlight'><pre><span class="keyword">require</span> <span class="string">'singleton'</span> +<span class="keyword">require</span> <span class="string">'socket'</span> +<span class="keyword">require</span> <span class="string">'stringio'</span> + +<span class="keyword">require</span> <span class="string">'rack/server'</span> +<span class="keyword">require</span> <span class="string">'rack/builder'</span> + +<span class="keyword">require</span> <span class="string">'spyglass_parser'</span> +<span class="keyword">require</span> <span class="string">'spyglass/configurator'</span> +<span class="keyword">require</span> <span class="string">'spyglass/logging'</span> +<span class="keyword">require</span> <span class="string">'spyglass/server'</span> +<span class="keyword">require</span> <span class="string">'spyglass/lookout'</span> +<span class="keyword">require</span> <span class="string">'spyglass/master'</span> +<span class="keyword">require</span> <span class="string">'spyglass/worker'</span> + +<span class="class"><span class="keyword">module</span> <span class="title">Spyglass</span></span> + <span class="constant">Version</span> = <span class="string">'0.1.1'</span> +<span class="keyword">end</span></pre></div></div> + + </li> + + </ul> + </div> +</body> +</html> diff --git a/code/spyglass/docs/worker.html b/code/spyglass/docs/worker.html new file mode 100644 index 0000000..a511c99 --- /dev/null +++ b/code/spyglass/docs/worker.html @@ -0,0 +1,305 @@ +<!DOCTYPE html> + +<html> +<head> + <title>worker.rb</title> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> + <link rel="stylesheet" media="all" href="docco.css" /> +</head> +<body> + <div id="container"> + <div id="background"></div> + + <ul id="jump_to"> + <li> + <a class="large" href="javascript:void(0);">Jump To …</a> + <a class="small" href="javascript:void(0);">+</a> + <div id="jump_wrapper"> + <div id="jump_page"> + + + <a class="source" href="configurator.html"> + configurator.rb + </a> + + + <a class="source" href="logging.html"> + logging.rb + </a> + + + <a class="source" href="lookout.html"> + lookout.rb + </a> + + + <a class="source" href="master.html"> + master.rb + </a> + + + <a class="source" href="server.html"> + server.rb + </a> + + + <a class="source" href="worker.html"> + worker.rb + </a> + + </div> + </li> + </ul> + + <ul class="sections"> + + <li id="title"> + <div class="annotation"> + <h1>worker.rb</h1> + </div> + </li> + + + + <li id="section-1"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-1">¶</a> + </div> + + </div> + + <div class="content"><div class='highlight'><pre><span class="keyword">require</span> <span class="string">'time'</span> +<span class="keyword">require</span> <span class="string">'rack/utils'</span></pre></div></div> + + </li> + + + <li id="section-2"> + <div class="annotation"> + + <div class="pilwrap for-h1"> + <a class="pilcrow" href="#section-2">¶</a> + </div> + <h1>Worker</h1> + + </div> + + </li> + + + <li id="section-3"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-3">¶</a> + </div> + + </div> + + <div class="content"><div class='highlight'><pre><span class="class"><span class="keyword">module</span> <span class="title">Spyglass</span></span> + <span class="class"><span class="keyword">class</span> <span class="title">Worker</span></span> + <span class="keyword">include</span> <span class="constant">Logging</span> + + <span class="function"><span class="keyword">def</span> <span class="title">initialize</span><span class="params">(socket, app, writable_pipe, connection = <span class="keyword">nil</span>)</span></span> + <span class="variable">@socket</span>, <span class="variable">@app</span>, <span class="variable">@writable_pipe</span> = socket, app, writable_pipe + <span class="variable">@parser</span> = <span class="constant">Spyglass::HttpParser</span>.new + + handle_connection(connection) <span class="keyword">if</span> connection + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">start</span></span> + trap_signals + + loop <span class="keyword">do</span> + handle_connection <span class="variable">@socket</span>.accept + <span class="keyword">end</span> + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">handle_connection</span><span class="params">(conn)</span></span> + verbose <span class="string">"Received connection"</span></pre></div></div> + + </li> + + + <li id="section-4"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-4">¶</a> + </div> + <p>This notifies our Master that we have received a connection, expiring +it's <code>IO.select</code> and preventing it from timing out.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="variable">@writable_pipe</span>.write_nonblock(<span class="string">'.'</span>)</pre></div></div> + + </li> + + + <li id="section-5"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-5">¶</a> + </div> + <p>This clears any state that the http parser has lying around +from the last connection that was handled.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="variable">@parser</span>.reset</pre></div></div> + + </li> + + + <li id="section-6"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-6">¶</a> + </div> + <p>The Rack spec requires that 'rack.input' be encoded as ASCII-8BIT.</p> + + </div> + + <div class="content"><div class='highlight'><pre> empty_body = <span class="string">''</span> + empty_body.encode!(<span class="constant">Encoding::ASCII_8BIT</span>) <span class="keyword">if</span> empty_body.respond_to?(<span class="symbol">:encode!</span>)</pre></div></div> + + </li> + + + <li id="section-7"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-7">¶</a> + </div> + <p>The Rack spec requires that the env contain certain keys before being +passed to the app. These are the keys that aren't provided by each +incoming request, server-specific stuff.</p> + + </div> + + <div class="content"><div class='highlight'><pre> env = { + <span class="string">'rack.input'</span> => <span class="constant">StringIO</span>.new(empty_body), + <span class="string">'rack.multithread'</span> => <span class="keyword">false</span>, + <span class="string">'rack.multiprocess'</span> => <span class="keyword">true</span>, + <span class="string">'rack.run_once'</span> => <span class="keyword">false</span>, + <span class="string">'rack.errors'</span> => <span class="constant">STDERR</span>, + <span class="string">'rack.version'</span> => [<span class="number">1</span>, <span class="number">0</span>] + }</pre></div></div> + + </li> + + + <li id="section-8"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-8">¶</a> + </div> + <p>This reads data in from the client connection. We'll read up to +10000 bytes at the moment.</p> + + </div> + + <div class="content"><div class='highlight'><pre> data = conn.readpartial(<span class="number">10000</span>)</pre></div></div> + + </li> + + + <li id="section-9"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-9">¶</a> + </div> + <p>Here we pass the data and the env into the http parser. It parses +the raw http request data and updates the env with all of the data +it can withdraw.</p> + + </div> + + <div class="content"><div class='highlight'><pre> <span class="variable">@parser</span>.execute(env, data, <span class="number">0</span>)</pre></div></div> + + </li> + + + <li id="section-10"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-10">¶</a> + </div> + <p>Call the Rack app, goes all the way down the rabbit hole and back again.</p> + + </div> + + <div class="content"><div class='highlight'><pre> status, headers, body = <span class="variable">@app</span>.call(env)</pre></div></div> + + </li> + + + <li id="section-11"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-11">¶</a> + </div> + <p>These are the default headers we always include in a response. We +only speak HTTP 1.1 and we always close the client connection. At +the monment keepalive is not supported.</p> + + </div> + + <div class="content"><div class='highlight'><pre> head = <span class="string">"HTTP/1.1 <span class="subst">#{status}</span>\r\n"</span> \ + <span class="string">"Date: <span class="subst">#{<span class="constant">Time</span>.now.httpdate}</span>\r\n"</span> \ + <span class="string">"Status: <span class="subst">#{<span class="constant">Rack::Utils::HTTP_STATUS_CODES</span>[status]}</span>\r\n"</span> \ + <span class="string">"Connection: close\r\n"</span> + + headers.each <span class="keyword">do</span> |k,v| + head << <span class="string">"<span class="subst">#{k}</span>: <span class="subst">#{v}</span>\r\n"</span> + <span class="keyword">end</span> + conn.write <span class="string">"<span class="subst">#{head}</span>\r\n"</span> + + body.each { |chunk| conn.write chunk } + body.close <span class="keyword">if</span> body.respond_to?(<span class="symbol">:close</span>)</pre></div></div> + + </li> + + + <li id="section-12"> + <div class="annotation"> + + <div class="pilwrap "> + <a class="pilcrow" href="#section-12">¶</a> + </div> + <p>Since keepalive is not supported we can close the client connection +immediately after writing the body.</p> + + </div> + + <div class="content"><div class='highlight'><pre> conn.close + + verbose <span class="string">"Closed connection"</span> + <span class="keyword">end</span> + + <span class="function"><span class="keyword">def</span> <span class="title">trap_signals</span></span> + trap(<span class="symbol">:QUIT</span>) <span class="keyword">do</span> + out <span class="string">"Received QUIT"</span> + exit + <span class="keyword">end</span> + <span class="keyword">end</span> + <span class="keyword">end</span> +<span class="keyword">end</span></pre></div></div> + + </li> + + </ul> + </div> +</body> +</html> |
