This commit is contained in:
parent
3825011bbf
commit
2c2ea539b1
4 changed files with 44 additions and 113 deletions
|
@ -1,18 +1,17 @@
|
|||
---
|
||||
title: 超文本传输协议(HTTP)快速入门
|
||||
date: 2023-03-21
|
||||
tags:
|
||||
- 网络
|
||||
- 网络协议
|
||||
- HTTP
|
||||
date: 2023-03-21 00:00:00
|
||||
---
|
||||
|
||||
- 请求一个页面
|
||||
- 用词约定
|
||||
- 从零开始的HTTP服务器生涯
|
||||
- GET和POST
|
||||
- 常见HTTP状态码和头简介
|
||||
- 从HTTP/1.1到HTTP/3
|
||||
- 参考资料和扩展阅读
|
||||
- FAQ
|
||||
|
||||
- [请求一个页面](#请求一个页面)
|
||||
- [用词约定](#用词约定)
|
||||
- [从零开始的HTTP服务器生涯](#从零开始的HTTP服务器生涯)
|
||||
- [GET和POST](#GET和POST)
|
||||
- [参考资料和扩展阅读](#参考资料和扩展阅读)
|
||||
|
||||
<!--more-->
|
||||
|
||||
|
@ -243,7 +242,7 @@ HTTP头部指的是结束标志(空行)以及之前所有部分,而HTTP头
|
|||
|
||||
我们已经了解HTTP请求和响应的结构,从这里开始,我们将使用Python来编写一个简单的HTTP服务器。如果你不会Python,可以看看[Python文档网站上的教程](https://docs.python.org/zh-cn/3/tutorial/index.html)。我们只会使用许多编程语言都具备的概念和特性,用其它编程语言实现应该也不会有太大障碍。
|
||||
|
||||
另外提一句,所有代码都在Fedora 37上使用Python 3.11运行。
|
||||
另外提一句,所有代码都在Fedora 37上使用Python 3.11测试,但是程序并没有使用特殊的特性,在其它平台和Python上多半也能正常使用。
|
||||
|
||||
你可能没有从零开始写HTTP服务器的经验,让我来为我们将要完成的代码划分几个部分:
|
||||
|
||||
|
@ -715,7 +714,7 @@ HTML不在本文范围,敬请参阅MDN Web Docs相关页面。
|
|||
|
||||
如果我们使用HTML的form元素进行请求,POST请求主体使用的格式与我们之前查询键值对的格式相同。键是form元素内input元素name属性的值。
|
||||
|
||||
> 绝大部分情况下,你应该用GET方法展示数据、POST方法只用来记录数据。在刷新POST方法返回的页面时,浏览器需要重新提交请求,并会询问用户是否要这样做(因为这样做很可能会导致不需要的副作用)。大部分情况下,这不是用户想体验的麻烦。
|
||||
> 绝大部分情况下,你应该用GET方法展示数据、POST方法只用来记录数据。在刷新POST方法返回的页面时,浏览器需要重新提交请求,并会询问用户是否要这样做(因为这样做可能会导致不需要的副作用)。大部分情况下,这不是用户想体验的麻烦。
|
||||
> 如果你使用HTML的form直接处理POST提交,你可以在处理POST方法完成后返回[HTTP 303 See Other状态码](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/303)并在`Location`头中指定重新请求的地址,让浏览器用GET方法重新请求并展示相应页面。
|
||||
> 我们接下来就是采用类似方法。
|
||||
|
||||
|
@ -848,125 +847,56 @@ DEFAULT_PAGE_HTML = """<!doctype html>
|
|||
|
||||
我们的代码尚不完善,你可以试试按以下方向改进。
|
||||
|
||||
#### 处理并发连接
|
||||
- 处理并发连接
|
||||
|
||||
当前的代码只能依照顺序一个一个处理连接,你使用多线程或者Python的asyncio让我们的服务器能够并发处理连接。
|
||||
当前的代码只能依照顺序一个一个处理连接,你使用多线程或者Python的asyncio让我们的服务器能够并发处理连接。
|
||||
|
||||
参考资料:
|
||||
参考资料:
|
||||
|
||||
- [Python文档:threading模块](https://docs.python.org/zh-cn/3/library/threading.html#module-threading)
|
||||
- [Python文档:asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html#module-asyncio)
|
||||
- [Python文档:threading模块](https://docs.python.org/zh-cn/3/library/threading.html#module-threading)
|
||||
- [Python文档:asyncio](https://docs.python.org/zh-cn/3/library/asyncio.html#module-asyncio)
|
||||
|
||||
#### 连接复用
|
||||
- 连接复用
|
||||
|
||||
当前代码在回应请求之后就直接关闭连接,你可以支持HTTP/1.1式的连接复用来提高连接使用效率。
|
||||
当前代码在回应请求之后就直接关闭连接,你可以支持HTTP/1.1式的连接复用来提高连接使用效率。
|
||||
|
||||
参考资料:
|
||||
参考资料:
|
||||
|
||||
- [MDN Web Docs:HTTP Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive)
|
||||
- [MDB Web Docs:HTTP Connection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection)
|
||||
- [MDN Web Docs:HTTP Keep-Alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Keep-Alive)
|
||||
- [MDB Web Docs:HTTP Connection](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection)
|
||||
|
||||
#### 增强兼容性
|
||||
- 增强兼容性
|
||||
|
||||
我们目前的代码作了一些假设,你可以改进代码以提高对不同客户端的兼容性。
|
||||
我们目前的代码作了一些假设,你可以改进代码以提高对不同客户端的兼容性。
|
||||
|
||||
参考资料:
|
||||
- 支持[HTTP Transfer-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding),我们的代码中没有支持这种传输方式
|
||||
|
||||
- [MDN Web Docs: HTTP Transfer-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding)
|
||||
- 包装和抽象
|
||||
|
||||
#### 包装和抽象
|
||||
你可以将HTTP服务器包装起来,甚至进一步包装成可以使用的框架,为用户提供可用的API。例如:
|
||||
|
||||
你可以将HTTP服务器包装起来,甚至进一步包装成可以使用的框架,为用户提供可用的API。例如:
|
||||
````python
|
||||
from mymodule import MyServer
|
||||
|
||||
````python
|
||||
from mymodule import MyServer
|
||||
|
||||
def handle_index(request):
|
||||
def handle_index(request):
|
||||
return request.ok(template="index.html")
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == "__main__":
|
||||
server = MyServer({
|
||||
"/": handle_index
|
||||
})
|
||||
server.run()
|
||||
````
|
||||
````
|
||||
|
||||
你还可以试试阅读[Tornado的代码](https://github.com/tornadoweb/tornado),这是一个Python异步Web框架。
|
||||
你还可以试试阅读[Tornado的代码](https://github.com/tornadoweb/tornado),这是一个Python异步Web框架。
|
||||
|
||||
#### 支持Cookie
|
||||
- 支持Cookie
|
||||
|
||||
HTTP是一个无状态协议:不同的响应-请求之间没有联系。HTTP Cookie是一项在不同响应-请求之间保留数据的技术。设计一个需要使用Cookie的功能,并在我们的HTTP服务器中实现它。
|
||||
HTTP是一个无状态协议:不同的响应-请求之间没有联系。HTTP Cookie是一项在不同响应-请求之间保留数据的技术。设计一个需要保留数据的功能,并在我们的HTTP服务器中实现它。
|
||||
|
||||
参考资料:
|
||||
参考资料:
|
||||
|
||||
- [MDN Web Docs:Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
|
||||
|
||||
## 常见HTTP状态码和头简介
|
||||
|
||||
我们已经明白了HTTP请求-响应的大致框架,接下来我们要往里面补充一些内容。这里简单列出一些常见HTTP状态码和HTTP头。
|
||||
|
||||
要进一步了解HTTP的详细信息,可以访问[MDN Web Docs上的HTTP页面](https://developer.mozilla.org/en-US/docs/Web/HTTP)。
|
||||
|
||||
### 常见HTTP状态码
|
||||
|
||||
HTTP状态码按信息类别组织:
|
||||
|
||||
- `1XX` 继续
|
||||
- `2XX` 成功
|
||||
- `3XX` 跳转
|
||||
- `4XX` 客户端错误
|
||||
- `5XX` 服务器错误
|
||||
|
||||
比较常见的状态码:
|
||||
|
||||
- `200 OK` 请求成功
|
||||
- `204 No Content` 请求成功,但是没有响应主体
|
||||
- `303 See Other` 要求客户端以GET请求访问`Location`头中的地址,这个跳转是暂时的
|
||||
- `304 Not Modified` 资源没有改变,这个跟条件请求(Conditional Requests)和HTTP缓存相关。
|
||||
- `400 Bad Request` 请求有错误
|
||||
- `401 Unauthorized` 未验证的请求
|
||||
- `403 Forbidden` 请求被服务器拒绝
|
||||
- `404 Not Found` 找不到资源
|
||||
- `500 Internal Server Error` 内部服务器错误
|
||||
- `504 Gateway Timeout` 网关超时
|
||||
|
||||
### `Content-Length`和`Transfer-Encoding: chunked`
|
||||
|
||||
我们在前面已经了解到`Content-Length`的值是主体长度,接收者可以利用这个主体长度读取主体。HTTP同样也提供了在主体长度不确定时的传输方法,要使用这个传输方法,将头`Transfer-Encoding`的值设置为`chunked`。指定这个传输方法时不再需要设置`Content-Length`。
|
||||
|
||||
顾名思义,这种方法是“按块传输”,主体可以包含一个或多个块。每个块以十六进制的块大小开头,加上`\r\n`,再加上块内容。发送结束时,发送一个大小为0的块来标识发送已结束。
|
||||
|
||||
### `Host`
|
||||
|
||||
这个头用于给服务器提供访问的域名。 所有请求都应该带上这个头。
|
||||
|
||||
### `User-Agent`
|
||||
|
||||
代理用户访问网站的客户端就叫做"User Agent",用户代理。这个头可以给服务器提供客户端信息,但是正在被逐步淘汰,取而代之的是Client Hint标准。
|
||||
|
||||
### `Accept`
|
||||
|
||||
客户端可以接受的数据类型。
|
||||
|
||||
### `Accept-Encoding`
|
||||
|
||||
客户端可以接受的数据编码,通常是压缩编码。
|
||||
|
||||
### `Accept-Language`
|
||||
|
||||
客户端偏好的语言。
|
||||
|
||||
### `Location`
|
||||
|
||||
在一些状态码中标识下一个目标的位置。
|
||||
|
||||
## 从HTTP/1.1到HTTP/3
|
||||
|
||||
本文之前内容主要基于HTTP/1.1。如今,HTTP已经改进出HTTP/2和HTTP/3。这些新版本协议更加高效,更加适合我们当下的使用场景。虽然HTTP/2和HTTP/3与HTTP/1.1相比变化很大,但是基本概念并没有什么变动。
|
||||
|
||||
敬请阅读:[Web.dev: Introduction to HTTP/2](https://web.dev/performance-http2/)。
|
||||
|
||||
敬请阅读:[Couldflare: What is HTTP/3?](https://www.cloudflare.com/learning/performance/what-is-http3/)。
|
||||
- [MDN Web Docs:Using HTTP cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
|
||||
|
||||
## 参考资料和扩展阅读
|
||||
|
||||
|
@ -975,6 +905,7 @@ HTTP状态码按信息类别组织:
|
|||
- [Wikipedia: URL encoding](https://en.wikipedia.org/wiki/URL_encoding)
|
||||
- [Wikipedia: Telnet](https://en.wikipedia.org/wiki/Telnet)
|
||||
|
||||
### HTTP有关的内容太多了,我记不住
|
||||
本文内容主要基于HTTP/1.1。如今,HTTP已经改进出HTTP/2和HTTP/3。这些新版本协议更加高效,更加适合我们当下的使用场景。虽然HTTP/2和HTTP/3与HTTP/1.1相比变化很大,但是基本概念并没有什么变动。
|
||||
|
||||
你不需要记住全部内容,只要大概看看知道能这么做就可以了。人类记忆过程中,忘记是一个正常阶段,不清楚的地方直接查资料就可以了。理论物理学家也不需要记得光在真空中传播的速度。
|
||||
- [Web.dev: Introduction to HTTP/2](https://web.dev/performance-http2/)
|
||||
- [Couldflare: What is HTTP/3?](https://www.cloudflare.com/learning/performance/what-is-http3/)
|
Loading…
Reference in a new issue