380 lines
28 KiB
HTML
380 lines
28 KiB
HTML
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title> <loganGatlin/> </title><link type="image/x-icon" href="resources/favicon.svg" rel="icon"/><style>
|
||
|
/*!
|
||
|
Pure v3.0.0
|
||
|
Copyright 2013 Yahoo!
|
||
|
Licensed under the BSD License.
|
||
|
https://github.com/pure-css/pure/blob/master/LICENSE
|
||
|
*/
|
||
|
/*!
|
||
|
normalize.css v | MIT License | https://necolas.github.io/normalize.css/
|
||
|
Copyright (c) Nicolas Gallagher and Jonathan Neal
|
||
|
*/
|
||
|
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}html{font-family:sans-serif}.hidden,[hidden]{display:none!important}.pure-img{max-width:100%;height:auto;display:block}.pure-g{display:flex;flex-flow:row wrap;align-content:flex-start}.pure-u{display:inline-block;vertical-align:top}.pure-u-1,.pure-u-1-1,.pure-u-1-12,.pure-u-1-2,.pure-u-1-24,.pure-u-1-3,.pure-u-1-4,.pure-u-1-5,.pure-u-1-6,.pure-u-1-8,.pure-u-10-24,.pure-u-11-12,.pure-u-11-24,.pure-u-12-24,.pure-u-13-24,.pure-u-14-24,.pure-u-15-24,.pure-u-16-24,.pure-u-17-24,.pure-u-18-24,.pure-u-19-24,.pure-u-2-24,.pure-u-2-3,.pure-u-2-5,.pure-u-20-24,.pure-u-21-24,.pure-u-22-24,.pure-u-23-24,.pure-u-24-24,.pure-u-3-24,.pure-u-3-4,.pure-u-3-5,.pure-u-3-8,.pure-u-4-24,.pure-u-4-5,.pure-u-5-12,.pure-u-5-24,.pure-u-5-5,.pure-u-5-6,.pure-u-5-8,.pure-u-6-24,.pure-u-7-12,.pure-u-7-24,.pure-u-7-8,.pure-u-8-24,.pure-u-9-24{display:inline-block;letter-spacing:normal;word-spacing:normal;vertical-align:top;text-rendering:auto}.pure-u-1-24{width:4.1667%}.pure-u-1-12,.pure-u-2-24{width:8.3333%}.pure-u-1-8,.pure-u-3-24{width:12.5%}.pure-u-1-6,.pure-u-4-24{width:16.6667%}.pure-u-1-5{width:20%}.pure-u-5-24{width:20.8333%}.pure-u-1-4,.pure-u-6-24{width:25%}.pure-u-7-24{width:29.1667%}.pure-u-1-3,.pure-u-8-24{width:33.3333%}.pure-u-3-8,.pure-u-9-24{width:37.5%}.pure-u-2-5{width:40%}.pure-u-10-24,.pure-u-5-12{width:41.6667%}.pure-u-11-24{width:45.8333%}.pure-u-1-2,.pure-u-12-24{width:50%}.pure-u-13-24{width:54.1667%}.pure-u-14-24,.pure-u-7-12{width:58.3333%}.pure-u-3-5{width:60%}.pure-u-15-24,.pure-u-5-8{width:62.5%}.pure-u-16-24,.pure-u-2-3{width:66.6667%}.pure-u-17-24{width:70.8333%}.pure-u-18-24,.pure-u-3-4{width:75%}.pure-u-19-24{width:79.1667%}.pure-u-4-5{width:80%}.pure-u-20-24,.pure-u-5-6{width:83.3333%}.pure-u-21-24,.pure-u-7-8{width:87.5%}.pure-u-11-12,.pure-u-22-24{width:91.6667%}.pure-u-23-24{width:95.8333%}.pure-u-1,.pure-u-1-1,.pure-u-24-24,.pure-u-5-5{width:100%}.pure-button{display:inline-block;line-height:normal;white-space:nowrap;vertical-align:middle;text-align:center;cursor:pointer;-webkit-user-drag:none;-webkit-user-select:none;user-select:none;box-sizing:border-box}.pure-button::-moz-focus-inner{padding:0;border:0}.pure-button-group{letter-spacing:-.31em;text-rendering:optimizespeed}.opera-only :-o-
|
||
|
</style><style>
|
||
|
@font-face {
|
||
|
font-family: Cascadia;
|
||
|
src: url("/resources/CascadiaMonoPL.woff2");
|
||
|
}
|
||
|
|
||
|
body {
|
||
|
color: #444;
|
||
|
font-size: 1.2em;
|
||
|
}
|
||
|
|
||
|
.link {
|
||
|
text-decoration: none;
|
||
|
color: #1e90ff;
|
||
|
}
|
||
|
|
||
|
.link :visited {
|
||
|
color: #1e90ff;
|
||
|
}
|
||
|
|
||
|
table {
|
||
|
display: block;
|
||
|
margin-top: 1em;
|
||
|
margin-bottom: 1em;
|
||
|
margin-left: auto;
|
||
|
margin-right: auto;
|
||
|
width: 60%;
|
||
|
|
||
|
text-align: left;
|
||
|
border-collapse: collapse;
|
||
|
}
|
||
|
|
||
|
th {
|
||
|
padding: 0.25em 0.25em 0.25em 0.25em;
|
||
|
border: 2px solid;
|
||
|
border-collapse: collapse;
|
||
|
}
|
||
|
|
||
|
td {
|
||
|
padding: 0.25em 0.25em 0.25em 0.25em;
|
||
|
border: 2px solid;
|
||
|
border-collapse: collapse;
|
||
|
}
|
||
|
|
||
|
/* https://css-loaders.com/spinner/ */
|
||
|
/* HTML: <div class="loader"></div> */
|
||
|
.loading {
|
||
|
width: 512px;
|
||
|
padding: 8px;
|
||
|
aspect-ratio: 1;
|
||
|
border-radius: 50%;
|
||
|
background: #25b09b;
|
||
|
--_m:
|
||
|
conic-gradient(#0000 10%,#000),
|
||
|
linear-gradient(#000 0 0) content-box;
|
||
|
-webkit-mask: var(--_m);
|
||
|
mask: var(--_m);
|
||
|
-webkit-mask-composite: source-out;
|
||
|
mask-composite: subtract;
|
||
|
animation: l3 1s infinite linear;
|
||
|
}
|
||
|
@keyframes l3 {to{transform: rotate(1turn)}}
|
||
|
|
||
|
|
||
|
.centered {
|
||
|
display: block;
|
||
|
margin-top: 1em;
|
||
|
margin-bottom: 1em;
|
||
|
margin-left: auto;
|
||
|
margin-right: auto;
|
||
|
width: 50%;
|
||
|
}
|
||
|
|
||
|
.pure-img-responsive { max-width: 100%; height: auto;
|
||
|
}
|
||
|
|
||
|
.cascadia {
|
||
|
font-family: Cascadia;
|
||
|
}
|
||
|
|
||
|
.home-menu {
|
||
|
padding: 0.5em;
|
||
|
text-align: center;
|
||
|
box-shadow: 0 1px 1px rgba(0,0,0, 0.10);
|
||
|
}
|
||
|
.home-menu {
|
||
|
background: #333;
|
||
|
}
|
||
|
|
||
|
.home-menu a {
|
||
|
color: #ccc;
|
||
|
}
|
||
|
|
||
|
.home-menu li a:hover,
|
||
|
.home-menu li a:focus {
|
||
|
animation: menu-hover-anim 0.25s ease-out;
|
||
|
animation-fill-mode: forwards;
|
||
|
background: none;
|
||
|
border: none;
|
||
|
}
|
||
|
|
||
|
|
||
|
.link {
|
||
|
color: #00f;
|
||
|
text-decoration: none;
|
||
|
}
|
||
|
|
||
|
|
||
|
.video {
|
||
|
text-align: center;
|
||
|
}
|
||
|
|
||
|
.video iframe {
|
||
|
width: 32em;
|
||
|
height: 18em;
|
||
|
frameborder: "0";
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Add transition to containers so they can push in and out.
|
||
|
*/
|
||
|
#layout,
|
||
|
#menu,
|
||
|
.menu-link {
|
||
|
-webkit-transition: all 0.2s ease-out;
|
||
|
-moz-transition: all 0.2s ease-out;
|
||
|
-ms-transition: all 0.2s ease-out;
|
||
|
-o-transition: all 0.2s ease-out;
|
||
|
transition: all 0.2s ease-out;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
This is the parent `<div>` that contains the menu and the content area.
|
||
|
*/
|
||
|
#layout {
|
||
|
position: relative;
|
||
|
left: 0;
|
||
|
padding-left: 0;
|
||
|
}
|
||
|
#layout.active #menu {
|
||
|
left: 150px;
|
||
|
width: 150px;
|
||
|
}
|
||
|
|
||
|
#layout.active .menu-link {
|
||
|
left: 150px;
|
||
|
}
|
||
|
/*
|
||
|
The content `<div>` is where all your content goes.
|
||
|
*/
|
||
|
.content {
|
||
|
margin: 0 auto;
|
||
|
padding: 0 2em;
|
||
|
max-width: 800px;
|
||
|
margin-bottom: 50px;
|
||
|
line-height: 1.6em;
|
||
|
text-align: justify;
|
||
|
}
|
||
|
|
||
|
.header {
|
||
|
margin: 0;
|
||
|
color: #444;
|
||
|
text-align: center;
|
||
|
padding: 2.5em 4em 0;
|
||
|
border-bottom: 1px solid #eee;
|
||
|
}
|
||
|
.header h1 {
|
||
|
margin: 0.2em 0;
|
||
|
font-size: 2.5em;
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
.header h2 {
|
||
|
font-weight: 300;
|
||
|
color: #888;
|
||
|
padding: 0;
|
||
|
margin-top: 0;
|
||
|
}
|
||
|
|
||
|
.distinct {
|
||
|
background-color: #444;
|
||
|
color: #fff;
|
||
|
padding: 0.5em 0.5em 0.5em 0.5em;
|
||
|
}
|
||
|
|
||
|
@keyframes card-hover-anim {
|
||
|
to {background-color: #ccc;}
|
||
|
}
|
||
|
@keyframes card-text-hover-anim {
|
||
|
to {color: #222;}
|
||
|
}
|
||
|
|
||
|
.spaced {
|
||
|
padding: 0.5em 0.5em 0.5em 0.5em;
|
||
|
margin: 1em 0em 1em 0em;
|
||
|
}
|
||
|
|
||
|
h1 a svg {
|
||
|
vertical-align: middle;
|
||
|
}
|
||
|
|
||
|
.card {
|
||
|
background-color: #eee;
|
||
|
text-decoration: none;
|
||
|
text-align: left;
|
||
|
}
|
||
|
|
||
|
.card h1 {
|
||
|
color: #444;
|
||
|
}
|
||
|
.card h2 {
|
||
|
color: #777;
|
||
|
}
|
||
|
.card h3 {
|
||
|
color: #999;
|
||
|
}
|
||
|
.card a {
|
||
|
text-decoration: none;
|
||
|
display: block;
|
||
|
}
|
||
|
|
||
|
.card:hover {
|
||
|
animation: card-hover-anim 0.25s ease-out;
|
||
|
animation-fill-mode: forwards;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* .card:hover h3 { */
|
||
|
/* animation: card-text-hover-anim 0.25s ease-out; */
|
||
|
/* animation-fill-mode: forwards; */
|
||
|
/* } */
|
||
|
|
||
|
.card img {
|
||
|
vertical-align: bottom;
|
||
|
float: right;
|
||
|
padding: 0em 0.1em 0em 0.1em;
|
||
|
width: 60px;
|
||
|
}
|
||
|
.card svg {
|
||
|
vertical-align: bottom;
|
||
|
float: right;
|
||
|
padding: 0em 0.1em 0em 0.1em;
|
||
|
width: 3em;
|
||
|
height: auto;
|
||
|
}
|
||
|
|
||
|
|
||
|
.content-subhead {
|
||
|
margin: 50px 0 20px 0;
|
||
|
font-weight: 300;
|
||
|
color: #444;
|
||
|
}
|
||
|
|
||
|
code {
|
||
|
font-family: Cascadia;
|
||
|
}
|
||
|
|
||
|
.code-block {
|
||
|
/* background-color: #fdf6e3; */
|
||
|
background-color: #eee;
|
||
|
color: #002b36;
|
||
|
font-family: Cascadia;
|
||
|
font-size: 0.85em;
|
||
|
padding: 0em 0.5em 0em 0.5em;
|
||
|
margin: 1em 0em 1em 0em;
|
||
|
page-break-inside: avoid;
|
||
|
display: block;
|
||
|
overflow: auto;
|
||
|
word-wrap: break-word;
|
||
|
max-width: 100%;
|
||
|
}
|
||
|
.code-block pre {
|
||
|
display: block;
|
||
|
margin: 0 0 0 0;
|
||
|
padding: 0 0 0 0;
|
||
|
}
|
||
|
|
||
|
.code-block pre code {
|
||
|
font-family: Cascadia;
|
||
|
display: block;
|
||
|
white-space: pre-wrap;
|
||
|
}
|
||
|
.code-block span {
|
||
|
/* background-color: #fdf6e3; */
|
||
|
background-color: #eee;
|
||
|
}
|
||
|
.keyword {
|
||
|
color: #6c71c4;
|
||
|
}
|
||
|
.constant {
|
||
|
color: #cb4b16;
|
||
|
}
|
||
|
.function {
|
||
|
color: #268bd2;
|
||
|
}
|
||
|
.operator {
|
||
|
color: #6c71c4;
|
||
|
}
|
||
|
.punctuation {
|
||
|
color: #586e75;
|
||
|
}
|
||
|
.string {
|
||
|
color: #859900;
|
||
|
}
|
||
|
.type {
|
||
|
color: #2aa198;
|
||
|
}
|
||
|
.property {
|
||
|
color: #586e75;
|
||
|
}
|
||
|
</style></head><body><div class="pure-g"><div class="pure-u-1-1"><div class="header"><h1> Rust HTTP Server </h1><h2> Keywords: Back End - TCP - SSL </h2></div><div class="content"><h2 class="distinct"> Motivation </h2>
|
||
|
As a systems programmer, I want to understand the technologies
|
||
|
I use at a very low level. Modern web stacks abstract away the
|
||
|
process of handling TCP connections, serving SSL certificates,
|
||
|
and parsing HTTP requests. The best way to understand a technology
|
||
|
is to implement it from the ground up, I studied the HTTP 1.1
|
||
|
specification and wrote a server to host my website.
|
||
|
<h2 class="distinct"> Approach </h2>
|
||
|
At the most basic level, the server will translate the request URL
|
||
|
into a local file path, and respond with the file matching that path
|
||
|
if it exists. Request URLs are sanitized to prevent accessing files
|
||
|
outside of the server's directory. Two tables are used for rewriting
|
||
|
and routing URLs. This makes handling URLs predictable and robust.
|
||
|
<div class="code-block">
|
||
|
<pre><code>
|
||
|
<span class="comment">// Snippet from the request handling code</span>
|
||
|
<span class="keyword">pub</span> <span class="keyword">async</span> <span class="keyword function">fn</span> <span class="function">construct_response</span><span class="punctuation bracket">(</span><span class="keyword storage modifier">&</span><span class="variable builtin">self</span><span class="punctuation delimiter">,</span> <span class="variable parameter">request_url</span>: <span class="keyword storage modifier">&</span><span class="type builtin">str</span><span class="punctuation bracket">)</span> <span class="operator">-></span> <span class="type">Response</span> <span class="punctuation bracket">{</span>
|
||
|
<span class="keyword storage">let</span> <span class="variable">sanitized_url</span> <span class="operator">=</span> <span class="function">rewrite_url</span><span class="punctuation bracket">(</span><span class="variable parameter">request_url</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">;</span>
|
||
|
<span class="keyword control conditional">if</span> <span class="variable">sanitized_url</span> <span class="operator">!=</span> <span class="variable parameter">request_url</span> <span class="punctuation bracket">{</span>
|
||
|
<span class="keyword control return">return</span> <span class="type">Self</span><span class="punctuation delimiter">::</span><span class="function">redirect</span><span class="punctuation bracket">(</span><span class="operator">&</span><span class="variable">sanitized_url</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">;</span>
|
||
|
<span class="punctuation bracket">}</span>
|
||
|
|
||
|
<span class="keyword storage">let</span> <span class="variable">routed_url</span> <span class="operator">=</span> <span class="variable builtin">self</span><span class="punctuation delimiter">.</span><span class="function">perform_routing</span><span class="punctuation bracket">(</span><span class="operator">&</span><span class="variable">sanitized_url</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">.</span><span class="keyword control return">await</span><span class="punctuation delimiter">;</span>
|
||
|
<span class="keyword control conditional">if</span> <span class="keyword storage">let</span> <span class="constructor">Some</span><span class="punctuation bracket">(</span><span class="variable">response</span><span class="punctuation bracket">)</span> <span class="operator">=</span>
|
||
|
<span class="variable builtin">self</span><span class="punctuation delimiter">.</span><span class="function">perform_redirecting</span><span class="punctuation bracket">(</span><span class="operator">&</span><span class="variable">routed_url</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">.</span><span class="keyword control return">await</span> <span class="punctuation bracket">{</span>
|
||
|
<span class="keyword control return">return</span> <span class="variable">response</span><span class="punctuation delimiter">;</span>
|
||
|
<span class="punctuation bracket">}</span>
|
||
|
<span class="keyword control conditional">match</span> <span class="variable builtin">self</span><span class="punctuation delimiter">.</span><span class="function">get_resource</span><span class="punctuation bracket">(</span><span class="operator">&</span><span class="variable">routed_url</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">.</span><span class="keyword control return">await</span> <span class="punctuation bracket">{</span>
|
||
|
<span class="constructor">Some</span><span class="punctuation bracket">(</span><span class="variable">bytes</span><span class="punctuation bracket">)</span> <span class="operator">=></span> <span class="type">Response</span><span class="punctuation delimiter">::</span><span class="function">from_data</span><span class="punctuation bracket">(</span><span class="variable">bytes</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="constructor">None</span> <span class="operator">=></span> <span class="type">Self</span><span class="punctuation delimiter">::</span><span class="function">not_found</span><span class="punctuation bracket">(</span><span class="punctuation bracket">)</span><span class="punctuation delimiter">,</span>
|
||
|
<span class="punctuation bracket">}</span>
|
||
|
<span class="punctuation bracket">}</span>
|
||
|
</code></pre>
|
||
|
</div>
|
||
|
I use Cloudflare to manage my domain name, DNS records, and SSL certificates.
|
||
|
Cloudflare automatically caches resources, rewrites HTTP requests to HTTPS,
|
||
|
and limits request sizes. This allows my server to only listen on port 443
|
||
|
and neglect caching server-side.
|
||
|
<h2 class="distinct"> Dependencies </h2>
|
||
|
My goal with this project was to provide a functional server using as few
|
||
|
libraries as possible. The final project directly depends on four crates:
|
||
|
<div class="code-block">
|
||
|
<pre><code>
|
||
|
// Snippet from Cargo<span class="punctuation delimiter">.</span>toml
|
||
|
<span class="punctuation bracket">[</span><span class="type">dependencies</span><span class="punctuation bracket">]</span>
|
||
|
<span class="variable other member">log</span> <span class="operator">=</span> <span class="string">"0.4"</span>
|
||
|
<span class="variable other member">log4rs</span> <span class="operator">=</span> <span class="string">"1.3"</span>
|
||
|
<span class="variable other member">tiny_http</span> <span class="operator">=</span> <span class="string">"0.12"</span>
|
||
|
<span class="variable other member">tokio</span> <span class="operator">=</span> <span class="punctuation bracket">{</span><span class="variable other member">version</span> <span class="operator">=</span> <span class="string">"1"</span><span class="punctuation delimiter">,</span> <span class="variable other member">features</span> <span class="operator">=</span> <span class="punctuation bracket">[</span><span class="string">"full"</span><span class="punctuation bracket">]</span><span class="punctuation bracket">}</span>
|
||
|
</code></pre>
|
||
|
</div>
|
||
|
The first two crates, log and log4rs, provide logging functions which aren't critical to the
|
||
|
server's functionality. The tiny_http crate provides a simple wrapper for the standard
|
||
|
library TCP stream, and performs SSL encryption. It is used as the backbone
|
||
|
for many Rust web frameworks. While I could implement these features myself, I
|
||
|
decided against it for security and browser compatibility reasons. Finally there is
|
||
|
tokio, an async runtime. This is necessary because Rust does not provide a runtime
|
||
|
by default, and building one is out of scope.
|
||
|
</div></div></div></body></html>
|