You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: cli-files/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -148,7 +148,7 @@ If you smash through this, here's some fun/tricky extensions:
148
148
149
149
### go-cat
150
150
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.
152
152
153
153
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`.
Copy file name to clipboardExpand all lines: http-auth/README.md
+71-26Lines changed: 71 additions & 26 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# Servers & HTTP requests
2
2
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.
4
4
5
5
Timebox: 6 days
6
6
@@ -12,13 +12,13 @@ Learning objectives:
12
12
- Define URL, header, body and content-type
13
13
- Accept parameters in via GET in the query string
14
14
- 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
18
16
- Write tests for the above
19
17
20
18
## Project
21
19
20
+
### Making an HTTP server
21
+
22
22
[Create a new go module](https://go.dev/doc/tutorial/create-module) in this `http-auth` directory: `go mod init http-auth`.
23
23
24
24
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
28
28
Here's a basic server that you'll build from:
29
29
30
30
```go
31
+
package main
32
+
33
+
import"net/http"
34
+
31
35
funcmain() {
32
36
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
33
37
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
44
48
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.
45
49
46
50
```
47
-
> curl -i http://localhost:8080/
51
+
> curl -i 'http://localhost:8080/'
48
52
HTTP/1.1 200 OK
49
53
Date: Sat, 25 Jun 2022 11:17:17 GMT
50
54
Content-Length: 25
@@ -61,32 +65,34 @@ HTTP requests are sent from a client to a server. They come in various types suc
61
65
62
66
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.
63
67
68
+
### Status codes
69
+
64
70
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.
65
71
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:
67
73
68
74
-`/200` -> 200 OK
69
75
-`/404` -> 404 Not found
70
76
-`/500` -> 500 Internal Server Error
71
77
72
78
```
73
-
> curl -i http://localhost:8080/200
79
+
> curl -i 'http://localhost:8080/200'
74
80
HTTP/1.1 200 OK
75
81
Date: Sat, 25 Jun 2022 11:16:17 GMT
76
82
Content-Length: 3
77
83
Content-Type: text/plain; charset=utf-8
78
84
79
85
200
80
86
81
-
> curl -i http://localhost:8080/500
87
+
> curl -i 'http://localhost:8080/500'
82
88
HTTP/1.1 500 Internal Server Error
83
89
Date: Sat, 25 Jun 2022 11:16:30 GMT
84
90
Content-Length: 21
85
91
Content-Type: text/plain; charset=utf-8
86
92
87
93
Internal server error
88
94
89
-
> curl -i http://localhost:8080/404
95
+
> curl -i 'http://localhost:8080/404'
90
96
HTTP/1.1 404 Not Found
91
97
Content-Type: text/plain; charset=utf-8
92
98
X-Content-Type-Options: nosniff
@@ -98,10 +104,12 @@ Content-Length: 19
98
104
99
105
Use `http.NotFoundHandler()` for the `404` error.
100
106
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")`
102
110
103
111
```
104
-
> curl -i http://localhost:8080/
112
+
> curl -i 'http://localhost:8080/'
105
113
HTTP/1.1 200 OK
106
114
Content-Type: text/html
107
115
Date: Sun, 24 Jul 2022 09:42:30 GMT
@@ -110,10 +118,20 @@ Content-Length: 42
110
118
<!DOCTYPE html><html><em>Hello, world</em>
111
119
```
112
120
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
+
113
131
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`.
Again, take a look at the response in different clients.
144
+
145
+
### Query parameters
146
+
125
147
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.
126
148
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
+
127
151
```
128
-
> curl -i http://localhost:8080\?foo=bar
152
+
> curl -i 'http://localhost:8080?foo=bar'
129
153
HTTP/1.1 200 OK
130
154
Content-Type: text/html
131
155
Date: Sun, 24 Jul 2022 09:55:33 GMT
@@ -143,8 +167,7 @@ Content-Length: 96
143
167
Try putting some HTML into the query params or body. You'll see that it is interpreted as HTML:
(Make sure to take a look at this one in a browser!)
186
+
162
187
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.
163
188
164
189
"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)`:
Next you're going to add a URL that can only be accessed if you know a username and secret password.
195
224
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!
197
226
198
227
Go's `http` library comes with some Basic Auth support built-in, so be sure to use it to make the following work:
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=`.
221
250
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:
223
252
224
253
```
225
254
> AUTH_USERNAME=admin AUTH_PASSWORD=long-memorable-password go run .
226
255
```
227
256
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
+
228
261
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).
229
262
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.
231
264
232
265
```
233
-
> ab -n 10000 -c 100 http://localhost:8080/
266
+
> ab -n 10000 -c 100 'http://localhost:8080/'
234
267
235
268
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
236
269
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)
287
320
100% 53 (longest request)
288
321
```
289
322
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
+
290
327
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.
If it is working, you will see `Non-2xx responses` and `Failed requests` in your ApacheBench output:
319
356
320
357
```
321
-
> ab -n 100 -c 100 http://localhost:8080/limited
358
+
> ab -n 100 -c 100 'http://localhost:8080/limited'
322
359
...
323
360
324
361
Document Path: /limited
@@ -355,3 +392,11 @@ Percentage of the requests served within a certain time (ms)
355
392
99% 5
356
393
100% 5 (longest request)
357
394
```
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