Skip to content

Commit c615c0f

Browse files
authored
More review feedback (#5)
1 parent 4d0823b commit c615c0f

File tree

2 files changed

+72
-27
lines changed

2 files changed

+72
-27
lines changed

cli-files/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ If you smash through this, here's some fun/tricky extensions:
148148

149149
### go-cat
150150

151-
This one we're going to make in a different, so we can see how to use tools to initialise go projects more quickly.
151+
This one we're going to make in a different way, so we can see how to use tools to initialise go projects more quickly.
152152

153153
We'll use the [cobra-cli](https://github.com/spf13/cobra-cli/blob/main/README.md) to initialise a new project. There's a guide on that page to installing it, but it's likely `go install github.com/spf13/cobra-cli@latest`.
154154

http-auth/README.md

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Servers & HTTP requests
22

3-
In this project you're going to learn about long-lived processes, some simple networking and the basics of HTTP.
3+
In this project you're going to learn about long-lived processes, some networking and the fundamentals of HTTP.
44

55
Timebox: 6 days
66

@@ -12,13 +12,13 @@ Learning objectives:
1212
- Define URL, header, body and content-type
1313
- Accept parameters in via GET in the query string
1414
- Accept data via a POST request
15-
- Setup authentication via a basic HTTP auth
16-
- Switch to using JWTs
17-
- Accept multiple forms of authentication
15+
- Setup authentication via basic HTTP auth
1816
- Write tests for the above
1917

2018
## Project
2119

20+
### Making an HTTP server
21+
2222
[Create a new go module](https://go.dev/doc/tutorial/create-module) in this `http-auth` directory: `go mod init http-auth`.
2323

2424
Create empty main package `main.go` and main function. Check it's all working by running the app: `go run .`.
@@ -28,6 +28,10 @@ The main library you'll be working with is built-in to Go: `net/http`. Import it
2828
Here's a basic server that you'll build from:
2929

3030
```go
31+
package main
32+
33+
import "net/http"
34+
3135
func main() {
3236
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
3337
w.Write([]byte("Hello, world"))
@@ -44,7 +48,7 @@ curl is a tool for transfering data from or to a server. It's very useful for te
4448
Using `curl -i` will show you how the server responds, including the response "headers" and "body". The headers contain metadata about the response, such as what type of data is being sent back.
4549

4650
```
47-
> curl -i http://localhost:8080/
51+
> curl -i 'http://localhost:8080/'
4852
HTTP/1.1 200 OK
4953
Date: Sat, 25 Jun 2022 11:17:17 GMT
5054
Content-Length: 25
@@ -61,32 +65,34 @@ HTTP requests are sent from a client to a server. They come in various types suc
6165

6266
HTTP responses — data sent back to a "client" from a "server" as a result of an HTTP request — can use a set of [standard codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) to indicate the status of the server or something about the request.
6367

68+
### Status codes
69+
6470
You're going to make a server that responds to `GET` requests with some of the common ones when a client makes a request to the appropriate URL: 200, 404, 500.
6571

66-
Update your go code so that each of the following works. Notice how the URL matches the code that is returned:
72+
Update your go code so that each of the following paths works. Notice how the URL matches the code that is returned:
6773

6874
- `/200` -> 200 OK
6975
- `/404` -> 404 Not found
7076
- `/500` -> 500 Internal Server Error
7177

7278
```
73-
> curl -i http://localhost:8080/200
79+
> curl -i 'http://localhost:8080/200'
7480
HTTP/1.1 200 OK
7581
Date: Sat, 25 Jun 2022 11:16:17 GMT
7682
Content-Length: 3
7783
Content-Type: text/plain; charset=utf-8
7884
7985
200
8086
81-
> curl -i http://localhost:8080/500
87+
> curl -i 'http://localhost:8080/500'
8288
HTTP/1.1 500 Internal Server Error
8389
Date: Sat, 25 Jun 2022 11:16:30 GMT
8490
Content-Length: 21
8591
Content-Type: text/plain; charset=utf-8
8692
8793
Internal server error
8894
89-
> curl -i http://localhost:8080/404
95+
> curl -i 'http://localhost:8080/404'
9096
HTTP/1.1 404 Not Found
9197
Content-Type: text/plain; charset=utf-8
9298
X-Content-Type-Options: nosniff
@@ -98,10 +104,12 @@ Content-Length: 19
98104

99105
Use `http.NotFoundHandler()` for the `404` error.
100106

101-
HTTP requests can return more than just plan text. Next, make the index page at `/` returns some HTML to a `GET` request. Make sure the `Content-Type` response header is set: `w.Header().Add("Content-Type", "text/html")`
107+
### The Content-Type header
108+
109+
HTTP requests can return more than just plan text. Next, make the index page at `/` returns some HTML in response to a `GET` request. Make sure the `Content-Type` response header is set: `w.Header().Add("Content-Type", "text/html")`
102110

103111
```
104-
> curl -i http://localhost:8080/
112+
> curl -i 'http://localhost:8080/'
105113
HTTP/1.1 200 OK
106114
Content-Type: text/html
107115
Date: Sun, 24 Jul 2022 09:42:30 GMT
@@ -110,10 +118,20 @@ Content-Length: 42
110118
<!DOCTYPE html><html><em>Hello, world</em>
111119
```
112120

121+
Curl is just one client we can use to make HTTP requests. Take a moment to try out two more that you've already used:
122+
1. A web browser - open up http://localhost:8080/ in Chrome.
123+
2. Postman - make a GET request to http://localhost:8080/ and see the output.
124+
125+
All three of these are clients that know how to speak HTTP, but they do different things with the response data because they have different goals.
126+
127+
The goal of the Content-Type header is to tell the client how it may want to render the response to the user. Try changing the Content-Type header back to `text/plain`, and see what Chrome does with the same response body.
128+
129+
### Methods: GET and POST
130+
113131
Now make the index page accept `POST` requests with some HTML, and return that HTML. You'll need to check the request method: `request.Method`.
114132

115133
```
116-
> curl -i -d "<em>Hi</em>" http://localhost:8080/
134+
> curl -i -d "<em>Hi</em>" 'http://localhost:8080/'
117135
HTTP/1.1 200 OK
118136
Content-Type: text/html
119137
Date: Sun, 24 Jul 2022 09:43:20 GMT
@@ -122,10 +140,16 @@ Content-Length: 32
122140
<!DOCTYPE html><html><em>Hi</em>
123141
```
124142

143+
Again, take a look at the response in different clients.
144+
145+
### Query parameters
146+
125147
HTTP requests can also supply "query parameters" in the URL: `/blog-posts?after=2022-05-04`. Make the handler at `/` output the query parameters as a list. Having the output spaced over multiple lines is optional, but done here for readability.
126148

149+
Note that when running commands in a terminal, some characters have special meaning by default, and need escaping - `?` is one of those characters. We've been using single-quotes (`'`s) around all of our URLs because it stops the terminal from making these characters behave specially.
150+
127151
```
128-
> curl -i http://localhost:8080\?foo=bar
152+
> curl -i 'http://localhost:8080?foo=bar'
129153
HTTP/1.1 200 OK
130154
Content-Type: text/html
131155
Date: Sun, 24 Jul 2022 09:55:33 GMT
@@ -143,8 +167,7 @@ Content-Length: 96
143167
Try putting some HTML into the query params or body. You'll see that it is interpreted as HTML:
144168

145169
```
146-
> curl -i http://localhost:8080\?foo=\<strong\>bar\</strong
147-
\>
170+
> curl -i 'http://localhost:8080?foo=<strong>bar</strong>'
148171
HTTP/1.1 200 OK
149172
Content-Type: text/html
150173
Date: Sun, 24 Jul 2022 09:57:20 GMT
@@ -159,12 +182,14 @@ Content-Length: 113
159182
</ul>
160183
```
161184

185+
(Make sure to take a look at this one in a browser!)
186+
162187
This isn't good! This kind of thing can lead to security issues. Search for "XSS attack" to find out more. Let's fix it.
163188

164189
"Escape" the string any time you take some input (data in `POST` or query parameters) and output it back. You'll need to investigate `html.EscapeString(v)`:
165190

166191
```
167-
> curl -i http://localhost:8080\?foo=\<strong\>bar\</strong\>
192+
> curl -i 'http://localhost:8080?foo=<strong>bar</strong>'
168193
HTTP/1.1 200 OK
169194
Content-Type: text/html
170195
Date: Sun, 24 Jul 2022 10:08:08 GMT
@@ -180,7 +205,7 @@ Content-Length: 125
180205
```
181206

182207
```
183-
> curl -i -d "<em>Hi</em>" http://localhost:8080/
208+
> curl -i -d "<em>Hi</em>" 'http://localhost:8080/'
184209
HTTP/1.1 200 OK
185210
Content-Type: text/html
186211
Date: Sun, 24 Jul 2022 10:08:21 GMT
@@ -191,22 +216,26 @@ Content-Length: 46
191216
&lt;em&gt;Hi&lt;/em&gt;
192217
```
193218

219+
Take a look at this in a browser too.
220+
221+
### Authentication
222+
194223
Next you're going to add a URL that can only be accessed if you know a username and secret password.
195224

196-
Add an endpoint `/authenticated` that requires the use of HTTP Basic auth. It should return a `401 Unauthorized` status code with a `WWW-Authenticate` header if basic auth is not present or does not match a username and password of your choice. Once Basic Auth is provided, it should respond successful!
225+
Add an endpoint `/authenticated` that requires the use of [HTTP Basic auth](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). It should return a `401 Unauthorized` status code with a `WWW-Authenticate` header if basic auth is not present or does not match a username and password of your choice. Once Basic Auth is provided, it should respond successful!
197226

198227
Go's `http` library comes with some Basic Auth support built-in, so be sure to use it to make the following work:
199228

200229
```
201-
> curl -i http://localhost:8080/authenticated
230+
> curl -i 'http://localhost:8080/authenticated'
202231
HTTP/1.1 401 Unauthorized
203232
Www-Authenticate: Basic realm="localhost", charset="UTF-8"
204233
Date: Sun, 24 Jul 2022 14:12:35 GMT
205234
Content-Length: 0
206235
```
207236

208237
```
209-
> curl -i http://localhost:8080/authenticated -H 'Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ='
238+
> curl -i 'http://localhost:8080/authenticated' -H 'Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ='
210239
HTTP/1.1 200 OK
211240
Content-Type: text/html
212241
Date: Sun, 24 Jul 2022 14:13:04 GMT
@@ -219,18 +248,22 @@ Hello username!
219248

220249
You can generate the `dXNl...` text [using this website](https://opinionatedgeek.com/Codecs/Base64Encoder). This is "base64 encoded" which you can search for to find a bit more about. Enter `username:password` to get `dXNlcm5hbWU6cGFzc3dvcmQ=`.
221250

222-
It's not a good idea to put secrets like passwords into code. So remove any hard-coded usernames and passwords for basic auth, and use `os.Getenv(...)` so that this works:
251+
It's not a good idea to put secrets like passwords into code (and base64 encoding text doesn't hide it, it just stores it in a different format). So remove any hard-coded usernames and passwords for basic auth, and use `os.Getenv(...)` so that this works:
223252

224253
```
225254
> AUTH_USERNAME=admin AUTH_PASSWORD=long-memorable-password go run .
226255
```
227256

257+
For bonus points, use [a library](https://github.com/joho/godotenv) to support dotenv files, and set your AUTH_USERNAME and AUTH_PASSWORD in a `.env` file.
258+
259+
### Handling load
260+
228261
Next you're going to test how many requests your server can support, and add basic [rate limiting](https://www.cloudflare.com/en-gb/learning/bots/what-is-rate-limiting).
229262

230-
[Follow this guide](https://www.datadoghq.com/blog/apachebench/) to install and use ApacheBench, which will test to see how many requests your server can handle
263+
[Follow this guide](https://www.datadoghq.com/blog/apachebench/) to install and use ApacheBench, which will test to see how many requests your server can handle.
231264

232265
```
233-
> ab -n 10000 -c 100 http://localhost:8080/
266+
> ab -n 10000 -c 100 'http://localhost:8080/'
234267
235268
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
236269
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
@@ -287,6 +320,10 @@ Percentage of the requests served within a certain time (ms)
287320
100% 53 (longest request)
288321
```
289322

323+
If a server receives too many requests at once, it can break (e.g. it may cause the system to run out of memory).
324+
325+
The fact that some of our requests took much longer than others, even though they were doing the same work, suggests that our server was getting stressed. We can see that in the "Percentages of the requests served within a certain time" section - half of the requests took less than 7ms, but the slowest took 53ms - more than 7 times slower.
326+
290327
It's better to protect your server from being asked to handle too many requests than to have it fall over! So use the `rate` library to reject excess requests (> X per second) with a `503 Service Unavailable` error on a `/limited` endpoint.
291328

292329
```
@@ -302,13 +339,13 @@ import "golang.org/x/time/rate"
302339
Then create a limiter:
303340

304341
```go
305-
lim := rate.NewLimiter(100, 30)
342+
limiter := rate.NewLimiter(100, 30)
306343
```
307344

308345
And use it:
309346

310347
```go
311-
if lim.Allow() {
348+
if limiter.Allow() {
312349
// Respond as normal!
313350
} else {
314351
// Respond with an error
@@ -318,7 +355,7 @@ if lim.Allow() {
318355
If it is working, you will see `Non-2xx responses` and `Failed requests` in your ApacheBench output:
319356

320357
```
321-
> ab -n 100 -c 100 http://localhost:8080/limited
358+
> ab -n 100 -c 100 'http://localhost:8080/limited'
322359
...
323360
324361
Document Path: /limited
@@ -355,3 +392,11 @@ Percentage of the requests served within a certain time (ms)
355392
99% 5
356393
100% 5 (longest request)
357394
```
395+
396+
Notice that all of our requests took about the same time this time around, and none were much slower - this shows that our server wasn't getting stressed.
397+
398+
One of the things we find in real life is that failure is inevitable. Computers lose power, servers get overloaded and slow down or stop working all together, networks break, etc. Our job as engineers isn't to _prevent_ failure, it's to try to make our systems behave as well as possible _depite_ failure.
399+
400+
In this exercise, we chose to make some of our requests fail fast, so that all of the requests that we _did_ process, got processed well (none were really slow, and our server didn't get overloaded).
401+
402+
Through this course, you will learn a lot more about ways we can give users a better experience by controlling _when_ and _how_ things fail.

0 commit comments

Comments
 (0)