Bài 60.630 phút đọc
Level 6: Máy chủ HTTP nhỏ
Sau bài này bạn sẽ làm được
- Phân tích yêu cầu HTTP: phương thức, đường dẫn, tiêu đề
- Phục vụ tệp tĩnh với đúng Content-Type và mã trạng thái
- Chống duyệt đường dẫn một cách bắt buộc
- Kiểm thử bằng trình duyệt thật và công cụ đo tải
Máy chủ HTTP nhỏ là dự án đầu tiên nói giao thức của một hệ thống thật: bạn phân tích một yêu cầu HTTP, tìm tệp, và trả lời đúng chuẩn để trình duyệt thật hiển thị được. Nó ghép Phần 16, 17, và bài học bảo mật của Phần 20.
#Mục tiêu
terminal
curl -i http://localhost:8080/index.html
HTTP/1.1 200 OK Content-Type: text/html Content-Length: 137 Connection: keep-alive <!doctype html><html>...
| Chức năng | Kỹ thuật | Bài |
|---|---|---|
| Nhận kết nối, đọc yêu cầu | socket, accept, recv | 46.3, 46.6 |
| Phân tích HTTP | Tách dòng, phân cách bằng CRLF | 46.5 |
| Phục vụ tệp tĩnh | open, đọc, gửi kèm Content-Type | Chương 41 |
| Đồng thời | epoll hoặc bể luồng | 46.8, 46.9 |
| Chống duyệt đường dẫn | realpath, kiểm tra tiền tố | 59.3 |
| Ghi log | Common Log Format | 56.4 |
#Phân tích HTTP
Cấu trúc một yêu cầu HTTP
GET /index.html HTTP/1.1\r\n <- dong yeu cau: phuong thuc, duong dan, phien ban
Host: localhost:8080\r\n <- cac dong tieu de
User-Agent: curl/7.68\r\n
Accept: */*\r\n
\r\n <- DONG RONG: het tieu de
(than, neu la POST)
/* Ba phan tach nhau ro rang:
1. Dong yeu cau: mot dong, ba truong cach nhau bang dau cach.
2. Tieu de: nhieu dong "Ten: Gia tri", toi khi gap DONG RONG.
3. Than: co Content-Length byte, chi POST/PUT co.
HTTP dung CA HAI cach danh ranh gioi cua Bai 46.5:
- tieu de: phan cach bang dong (doc toi dong rong)
- than: tien to do dai (Content-Length noi bao nhieu byte) */Đọc tới dòng trống
Đọc vào bộ đệm tích luỹ cho tới khi gặp\r\n\r\n. Đó là ranh giới hết phần tiêu đề. Đừng giả định cả yêu cầu tới trong một lầnrecv.Tách dòng yêu cầu
Ba trường: phương thức (GET), đường dẫn (/index.html), phiên bản (HTTP/1.1).Đọc các tiêu đề
Mỗi dòngTen: Gia tri. LấyContent-Lengthnếu có để biết độ dài thân.Đọc thân theo Content-Length
Với POST, đọc đúng số byte màContent-Lengthnói, không đọc "cho tới khi đóng".
#Phục vụ tệp tĩnh
| Đuôi tệp | Content-Type |
|---|---|
| .html | text/html |
| .css | text/css |
| .js | application/javascript |
| .png | image/png |
| .jpg | image/jpeg |
| .json | application/json |
| khác | application/octet-stream |
Dòng trạng thái và tiêu đề tối thiểu
/* Tra loi thanh cong: */
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 137\r\n <- BAT BUOC dung, so byte cua than
Connection: keep-alive\r\n
\r\n
<than tep>
/* Cac ma trang thai phai co: */| Mã | Khi nào | Lý do |
|---|---|---|
| 200 | Tệp tồn tại, đọc được | OK |
| 301 | Thư mục không có dấu / cuối | Chuyển hướng thêm dấu / |
| 400 | Yêu cầu sai cú pháp | Bad Request |
| 403 | Tệp tồn tại nhưng không có quyền đọc | Forbidden |
| 404 | Tệp không tồn tại | Not Found |
| 405 | Phương thức không hỗ trợ | Method Not Allowed |
| 500 | Lỗi bên trong server | Internal Server Error |
#Chống duyệt đường dẫn
Ghép thẳng
char duong_dan[512];
snprintf(duong_dan, sizeof duong_dan, "%s%s", THU_MUC_GOC, path);
FILE *t = fopen(duong_dan, "rb");
/* path = "/../../etc/passwd" -> doc /etc/passwd */
Chuẩn hoá rồi kiểm tra
char ghep[PATH_MAX], thuc[PATH_MAX];
snprintf(ghep, sizeof ghep, "%s%s", THU_MUC_GOC, giai_ma_url(path));
if (!realpath(ghep, thuc)) return tra_404();
if (!nam_trong_goc(thuc)) return tra_403();
FILE *t = fopen(thuc, "rb");
/* Da chuan hoa va kiem tra: khong the thoat ra ngoai goc. */
#Yêu cầu
Tự làm thử
- Phân tích yêu cầu HTTP/1.1: phương thức, đường dẫn, phiên bản, tiêu đề, thân.
- Phục vụ tệp tĩnh với đúng
Content-Type. - Mã trạng thái 200, 301, 400, 403, 404, 405, 500.
Keep-AlivevàContent-Lengthđúng chuẩn.- Liệt kê thư mục khi truy cập một thư mục.
- Chống duyệt đường dẫn, bắt buộc.
- Ghi log dạng Common Log Format.
- Xử lý đồng thời bằng epoll hoặc bể luồng.
- Tệp cấu hình: cổng, thư mục gốc, tệp mặc định.
- Kiểm thử bằng trình duyệt thật,
curl, vàab -n 10000 -c 100.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Máy chủ HTTP là dự án đầu tiên kiểm thử bằng trình duyệt thật,
curl, vàab. - HTTP dùng cả hai cách đánh ranh giới: tiêu đề phân cách bằng dòng, thân bằng
Content-Length. Content-Lengthphải chính xác từng byte, nếu không trình duyệt treo với keep-alive.- Chống duyệt đường dẫn là bắt buộc: chuẩn hoá bằng
realpathrồi kiểm tra tiền tố, không cấm chuỗi... - Xong khi trình duyệt hiển thị đúng,
abchạy sạch, và/../../etc/passwdtrả 403.