HTTP笔记

157 阅读8分钟

HTTP 协议是什么

HTTP 协议(HyperText Transfer Protocol,超文本传输协议)是一种用于在 Web 上进行数据交换的应用层协议。它是基于 TCP/IP 协议的,也就是说,它利用 TCP 来建立可靠的连接,然后在 TCP 的基础上发送和接收 HTTP 消息。HTTP 是无状态的,也就是说,每个请求和响应之间没有关联,服务器不会记住客户端的状态。HTTP 是可扩展的,也就是说,它可以通过添加新的标头(header)或方法(method)来增加新的功能。

HTTP 协议里有什么

HTTP 协议里主要有两种类型的消息:请求(request)和响应(response)。请求是由客户端(通常是浏览器)发出的,用来向服务器请求一个资源(如一个 HTML 文档、一张图片、一个表单等)。响应是由服务器发出的,用来回应客户端的请求,并提供所请求的资源或者错误信息。

一个 HTTP 请求由以下几个部分组成:

  • 请求行(request line),包含了请求方法(如 GET、POST 等)、请求目标(如一个 URL)、协议版本(如 HTTP/1.1)等信息。
  • 请求头(request header),包含了一些与请求相关的元数据(metadata),如客户端类型、内容类型、内容长度、缓存控制等。
  • 空行(empty line),用来分隔请求头和请求体。
  • 请求体(request body),包含了一些与请求相关的数据,如表单数据、文件数据等。不是所有的请求都有请求体,比如 GET 请求就没有。

一个 HTTP 响应由以下几个部分组成:

  • 状态行(status line),包含了协议版本(如 HTTP/1.1)、状态码(如 200、404 等)、状态描述(如 OK、Not Found 等)等信息。
  • 响应头(response header),包含了一些与响应相关的元数据,如服务器类型、内容类型、内容长度、缓存控制等。
  • 空行(empty line),用来分隔响应头和响应体。
  • 响应体(response body),包含了一些与响应相关的数据,如 HTML 文档、图片数据等。不是所有的响应都有响应体,比如 204 No Content 响应就没有。

下面是一个 HTTP 请求和响应的示例:

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html

HTTP/1.1 200 OK
Server: Apache/2.4.41
Content-Type: text/html
Content-Length: 1024

<html>
<head>
<title>Example Page</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>

HTTP 请求流程

HTTP 请求流程大致如下:

  • 客户端通过 DNS 解析服务器的域名,得到服务器的 IP 地址。
  • 客户端向服务器发起 TCP 连接,建立三次握手。
  • 客户端发送 HTTP 请求消息给服务器。
  • 服务器接收并解析 HTTP 请求消息,并根据请求目标查找相应的资源。
  • 服务器发送 HTTP 响应消息给客户端。
  • 客户端接收并解析 HTTP 响应消息,并根据响应状态码和内容类型决定如何处理响应体。
  • 客户端关闭 TCP 连接,或者保持连接以便复用。

HTTP 协议的不足与展望

HTTP 协议虽然简单易用,但也存在一些不足之处,主要有以下几点:

  • HTTP 是明文传输的,没有加密机制,容易被窃听、篡改或者伪造。
  • HTTP 是无状态的,不能记住客户端的状态,需要借助 Cookie 或 Session 等机制来实现有状态的会话。
  • HTTP 是基于请求-响应模式的,客户端必须主动发起请求,服务器才能回应,不能实现服务器主动推送数据给客户端。
  • HTTP 是基于文本的,传输效率不高,而且格式固定,不易扩展。
  • HTTP/1.1 是基于 TCP 的,需要建立连接才能传输数据,而 TCP 连接的建立和维护需要消耗资源和时间,而且 TCP 本身也有一些缺陷,如慢启动、队头阻塞等。

为了解决 HTTP 协议的不足,人们提出了一些改进的方案,如 HTTPS、HTTP/2、HTTP/3 等。

  • HTTPS(HTTP Secure)是在 HTTP 的基础上加入了 SSL/TLS 层,用来对数据进行加密、认证和完整性保护。HTTPS 可以提高 HTTP 的安全性,但也会增加计算开销和延迟。
  • HTTP/2 是对 HTTP/1.1 的改进版本,主要采用了二进制格式、多路复用、头部压缩、服务器推送等技术,用来提高 HTTP 的性能、效率和安全性。HTTP/2 仍然基于 TCP 传输。
  • HTTP/3 是对 HTTP/2 的改进版本,主要的变化是将底层传输层从 TCP 改为 QUIC(Quick UDP Internet Connections),用来解决 TCP 的一些缺陷,并进一步提高 HTTP 的性能和安全性。QUIC 是基于 UDP 的,但是实现了可靠性、拥塞控制、流控制等功能,并且支持加密和多路复用。

HTTP 框架的设计与实现

分层设计

HTTP 框架是一种用来简化和规范 Web 应用开发的软件工具。HTTP 框架通常采用分层设计,将 Web 应用分为不同的层次或组件,每个层次或组件负责处理不同的功能或逻辑。分层设计可以提高 Web 应用的可维护性、可扩展性和可重用性。

一般来说,HTTP 框架可以分为以下几个层次或组件:

  • 网络层(Network Layer),负责处理底层的网络通信,如 TCP 连接、HTTP 请求和响应等。网络层通常由操作系统或标准库提供,或者使用第三方库封装。
  • 中间件层(Middleware Layer),负责处理一些通用的或跨越多个请求的功能或逻辑,如身份验证、日志记录、错误处理、缓存、压缩等。中间件层通常由框架提供一些内置的或可选的中间件组件,或者允许开发者自定义或引入第三方的中间件组件。
  • 路由层(Routing Layer),负责将不同的请求路径或方法映射到不同的处理函数或控制器(Handler or Controller)。路由层通常由框架提供一个灵活的路由机制,支持静态路由、动态路由、正则路由等,并且允许开发者自定义路由规则或参数

应用层设计

应用层(Application Layer),负责处理具体的业务逻辑,如数据处理、模板渲染、视图返回等。应用层通常由开发者编写一些处理函数或控制器(Handler or Controller),并通过路由层将它们与不同的请求路径或方法关联起来。处理函数或控制器通常接收一个请求对象(Request)和一个响应对象(Response)作为参数,并根据请求对象中的信息进行业务逻辑的处理,然后通过响应对象返回结果或错误给客户端。

一个典型的处理函数或控制器的示例如下:

public class HelloController {

    // 处理 GET /hello 请求
    @Get("/hello")
    public void hello(Request request, Response response) {
        // 从请求对象中获取参数
        String name = request.getParameter("name");
        // 从请求对象中获取会话
        Session session = request.getSession();
        // 从会话中获取属性
        String role = session.getAttribute("role");
        // 判断角色是否合法
        if ("admin".equals(role)) {
            // 设置响应状态码为 200 OK
            response.setStatus(200);
            // 设置响应内容类型为 text/plain
            response.setContentType("text/plain");
            // 设置响应内容为 Hello, name
            response.setContent("Hello, " + name);
        } else {
            // 设置响应状态码为 403 Forbidden
            response.setStatus(403);
            // 设置响应内容类型为 text/plain
            response.setContentType("text/plain");
            // 设置响应内容为 You are not authorized
            response.setContent("You are not authorized");
        }
    }
}

中间件设计

中间件(Middleware),是一种在网络层和应用层之间执行的组件,它可以对请求或响应进行一些预处理或后处理,以实现一些通用的或跨越多个请求的功能或逻辑,如身份验证、日志记录、错误处理、缓存、压缩等。中间件通常由框架提供一些内置的或可选的中间件组件,或者允许开发者自定义或引入第三方的中间件组件。

一个典型的中间件的示例如下:

public class LoggerMiddleware implements Middleware {

    // 处理请求之前执行的方法
    @Override
    public void beforeHandle(Request request, Response response) {
        // 获取请求方法和路径
        String method = request.getMethod();
        String path = request.getPath();
        // 打印日志信息
        System.out.println("[" + method + "] " + path);
    }

    // 处理请求之后执行的方法
    @Override
    public void afterHandle(Request request, Response response) {
        // 获取响应状态码和内容长度
        int status = response.getStatus();
        int length = response.getContentLength();
        // 打印日志信息
        System.out.println("[" + status + "] " + length);
    }
}

路由设计

路由(Routing),是一种将不同的请求路径或方法映射到不同的处理函数或控制器(Handler or Controller)的机制,它可以让开发者根据不同的业务需求定义不同的路由规则或参数,以实现不同的业务逻辑。路由通常由框架提供一个灵活的路由机制,支持静态路由、动态路由、正则路由等,并且允许开发者自定义路由规则或参数。

一个典型的路由的示例如下:

public class Router {

    // 存储路由规则和处理函数的映射关系
    private Map<String, Handler> routes;

    // 构造方法,初始化路由映射
    public Router() {
        routes = new HashMap<>();
    }

    // 注册一个 GET 请求的路由规则和处理函数
    public void get(String path, Handler handler) {
        routes.put("GET:" + path, handler);
    }

    // 注册一个 POST 请求的路由规则和处理函数
    public void post(String path, Handler handler) {
        routes.put("POST:" + path, handler);
    }

    // 根据请求对象找到对应的处理函数,如果没有找到,返回 null
    public Handler findHandler(Request request) {
        // 获取请求方法和路径
        String method = request.getMethod();
        String path = request.getPath();
        // 根据方法和路径拼接路由键
        String key = method + ":" + path;
        // 从路由映射中查找处理函数
        return routes.get(key);
    }
}