- http 请求报文的组成:
- 请求行 request line
- 请求头 request header
- 空行 Blank Line
- 请求体 request body
- 请求行
- 示例:
GET /index.html HTTP/1.1
- 包含的信息:
- 请求方法
GET
- 请求路径
/index.html
- http 版本
HTTP/1.1
- 请求方法
- 示例:
- 请求头
- 包含了一系列的头部字段。
- 每一个字段都是由字段名和字段值组成,两者之间用冒号分隔。
- 每一个字段都描述了关于请求的一些附加信息
- 一些常见的请求头字段:
Host
用于指定请求资源的服务器和端口号User-Agent
描述客户端的软件和软件版本Accept
客户端能处理的媒体类型Content-Length
请求主体的长度,单位为字节
- 空行
- 请求头部和消息主体之间有一个空行。
- 空行是一个必须的分隔,它标示了头部字段的结束。
- 请求体
- 不是所有的 HTTP 请求都有主体。例如,GET 请求通常没有主体,但 POST 请求可能会带有主体,该主体包含了表单数据或 JSON 数据等。
- 请求主体中的数据格式通常由 Content-Type 头部指定。
- http 请求报文主要包括请求行、请求头部、一个空行和可能存在的请求主体
- 注意,对于一个合法的请求报文而言,前 3 者都是必须的,请求主体可以为空。
- 尤其注意请求头结束后要有一个空行,这个空行是不能省略的,即便是在请求体为空的情况下。
// server.js
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(`{"msg": "hello world"}`);
});
server.listen(8888, () => {
console.log('Server running on port 8888');
})
基于 nodejs 的 http 模块,实现一个本地服务
// client.js
const net = require('net');
const client = net.createConnection({ port: 8888 }, () => {
// 'connect' listener
console.log('connected to server!');
client.write('GET / HTTP/1.1\r\n');
client.write('Host: localhost:8888\r\n');
client.write('\r\n');
});
client.on('data', (data) => {
console.log('\n\n[data from server]:\n\n');
console.log(data.toString())
client.end(); // close the connection after receiving data
});
client.on('end', () => {
console.log('disconnected from server');
});
/*
connected to server!
[data from server]:
HTTP/1.1 200 OK
Content-Type: application/json
Date: Mon, 14 Aug 2023 02:03:53 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
16
{"msg": "hello world"}
0
disconnected from server
*/
基于 nodejs 的 net 模块,手写 http 请求报文,请求本地服务
对于请求报文的书写,有很多种方式,只要满足 http 请求报文的格式要求即可。
// 下面这些写法即可
client.write('GET / HTTP/1.1\r\n');
client.write('Host: localhost:8888\r\n');
client.write('\r\n');
client.write(`GET / HTTP/1.1\r\nHost: localhost:8888\r\n\r\n`);
client.write(`GET / HTTP/1.1\r
Host: localhost:8888\r
\r
`)
需要注意的是:
- 模板字符串中我们如果使用了换行,相当于在结尾默认插入了一个字符
\n
,不要忘记补上回车符\r
。 - 请求头信息结束位置,不要忘记加上空行
- http 响应报文的组成:
- 响应行 response line
- 响应头 response header
- 响应体 response body
// server_net.js
const net = require('net');
const server = net.createServer(socket => {
socket.on('data', data => {
// 这里仅仅简单地判断了数据是否包含 GET 方法来做出响应,实际应用应该有完整的 HTTP 解析过程。
if (data.toString().startsWith('GET')) {
const response = `HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"msg": "hello world"}`;
socket.write(response);
socket.end(); // 关闭连接
}
});
socket.on('error', err => {
console.error('Socket error:', err);
});
});
server.listen(8888, () => {
console.log('Server running on port 8888');
});
基于 nodejs 的 net 模块,实现一个本地服务。
- 解析 http 请求
- 手写响应报文
// client.js
const net = require('net');
const client = net.createConnection({ port: 8888 }, () => {
// 'connect' listener
console.log('connected to server!');
client.write('GET / HTTP/1.1\r\n');
client.write('Host: localhost:8888\r\n');
client.write('\r\n');
});
client.on('data', (data) => {
console.log('\n\n[data from server]:\n\n');
console.log(data.toString())
client.end(); // close the connection after receiving data
});
client.on('end', () => {
console.log('disconnected from server');
});
/*
connected to server!
[data from server]:
HTTP/1.1 200 OK
Content-Type: application/json
{"msg": "hello world"}
disconnected from server
*/
也可以用浏览器直接请求 http://localhost:8888/,获取到的内容如下: