forked from redis/redis
-
Notifications
You must be signed in to change notification settings - Fork 0
/
homepage.html
123 lines (116 loc) · 4.65 KB
/
homepage.html
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>Redis over HTTP</title>
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
body{
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;
max-width: 48rem;
padding: 2rem;
margin: auto;
}
pre{
background-color: #f6f8fa;
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
padding: 16px;
line-height: 1.2;
overflow: auto;
border: none !important;
}
</style>
</head>
<body>
<h1 id="redis-over-http">Redis over HTTP</h1>
<p>This page was served directly from a modified version of Redis. There are no web servers sitting in between.</p>
<h2 id="try-it-out">Try it out</h2>
You can directly edit the variables in following script:
<pre class="prettyprint">
<script style="display: block;" contenteditable="true" id="editable-script">
window.MY_REDIS_COMMAND = "SET"; // https://redis.io/commands
window.MY_KEY = "my-custom-website";
window.MY_ARGS = ""; // optional
window.MY_FINAL_ARG = `
<h1>Hello, world!</h1>
`;
</script>
</pre>
<script>
document.getElementById("editable-script").onkeyup = function(e) {
eval(e.target.innerText); // Security risks acknowledged and ignored
let link = document.getElementById("set-page")
link.href = "/" + window.MY_KEY;
link.innerText = "/" + window.MY_KEY;
}
</script>
Here is the exact script your browser will execute:
<pre class="prettyprint">
<script style="display: block;">
function execute(event) {
// Build the URL
let url = `${window.location.href}${window.MY_KEY}`;
if (window.MY_ARGS !== "") {
url += window.MY_ARGS
}
// Send the HTTP request
fetch(url, {
"method": window.MY_REDIS_COMMAND,
"body": window.MY_FINAL_ARG,
})
// Parse the response and display it on the page
.then((response) => response.text())
.then((text) => {
document.getElementById("result-box").innerText = text;
})
}
</script>
</pre>
<button onclick="execute()">Execute</button>
<pre id="result-box">> Redis output will appear here</pre>
If you've SET a key, you can view the page by clicking here: <a href="/my-custom-website" id="set-page">/my-custom-website</a>
<br/><br/><br/>
<hr>
<br/>
<h2 id="how-does-it-work">How does it work?</h2>
<p>Redis normally executes commands by parsing its own protocol called RESP: <a href="https://redis.io/topics/protocol" class="uri">https://redis.io/topics/protocol</a></p>
<p>But the protocol also support "Inline Commands", which look like this:</p>
<pre>GET somekey
SET somekey somevalue
</pre>
<p>This just so happens to be close enough to HTTP:</p>
<pre>GET /somekey HTTP/1.1
</pre>
<p>With a little abuse of the HTTProtocol, we can use unsupported verbs like SET and we can add spaces to the path:</p>
<pre>SET /somekey "somevalue" HTTP/1.1
</pre>
<p>Then all we have to do is change the Redis source code to remove the extra bits like "HTTP/1.1" and the preceding slash. Now we can use curl to talk to redis!</p>
<pre>curl localhost:6379/somekey
curl -X SET "localhost:6379/somekey \"somevalue\""
</pre>
<p>But the output will be normal RESP unless we also change how Redis generates responses. All we need to do is replace some control characters (like length of strings, error characters) with their HTTP equivalent.</p>
<p>Redis normally responds with this:</p>
<pre>$6\r\somevalue\r\n
</pre>
<p>So we change that to:</p>
<pre>HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
\r\n
somevalue
</pre>
<p>Now we can serve entire webpages from Redis directly (such as this one)!</p>
<h2 id="limitations">Limitations</h2>
<ul>
<li>To simplify the parsing code, request bodies are limited to 16128 bytes. My poor C skills are to blame for this.</li>
<li>Some commands can't be sent, like ones that take one argument. This is because the method (command) and URI (arguments) are both required in the HTTP spec.</li>
<li>Some commands are explictly removed, such as SHUTDOWN.</li>
</ul>
<h2 id="see-the-code">See the code</h2>
<a href="https://github.com/mac-chaffee/redis-over-http">https://github.com/mac-chaffee/redis-over-http</a>
</body>
</html>