IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 造https client轮子的记录 -> 正文阅读

[网络协议]造https client轮子的记录

最近我们的服务器需要嵌入HTTP服务,需要支持httpclient和httpserver,httpclient要同时支持https。
我们现在服务器进程之间的网络通信使用的是自有实现,它使用io多路复用技术。因为http只是在tcp之上进行明文传输而已,所以实现也包括了一个简陋的httpclient和httpserver,还基于openssl实现玩具版的https,但基本是不能在生产中使用的。
本来我想着用开源库,但看了下好像与epoll结合也挺麻烦,就先自己手撸轮子试试。虽然过程痛苦,但加深了对相关知识的理解,比如之前写的理解https的验证过程。以下是造轮子过程中遇到的一些问题。

SSL_METHOD

通过SSL_CTX *SSL_CTX_new(const SSL_METHOD *method);创建SSL_CTX对象,method又通过一系列的函数来创建,这些函数的参数都是void,返回值都是const SSL_METHOD *,其中SSLv23_method是通用版本,实际使用的SSL/TLS版本通过与对方协商确定,支持SSLv2,SSLv3,TLSv1,TLSv1.1,TLSv1.2,其他的比如TLSv1_2_method则只支持TLSv1.2协议。
更多细节可通过man SSL_CTX_new了解。

信任server端证书

测试环境webserver使用的是自签名证书,所以httpclient需要跳过证书验证步骤,直接信任webserver发来的证书。
查了一下网上说,需要自己注册一个验证证书的回调函数,我实际使用中发现,不对SSL_CTX对象调用SSL_CTX_load_verify_locations和SSL_CTX_use_certificate_file就不会去验证证书。

HTTP POST body常见数据格式

参考HTTP POST body常见的四种数据格式,我们使用的是application/x-www-form-urlencoded格式,这是浏览器的原生form表单格式,上文中说“如果不设置Content-Type属性,则默认以application/x-www-form-urlencoded方式传输数据”,我猜这个默认是浏览器给加的吧,反正我如果不手动添加“Content-Type: application/x-www-form-urlencoded”的header,那边的webserver不认我的请求。

URL编码

上面的x-www-form-urlencoded格式数据需要进行URL编码,这种编码很简单,需要把英文字母、阿拉伯数字、以及“-_.~”之外的字符都进行编码,空格编码成+,其他字符编码成%后面加字符十六进制数字的格式。
比如上面的application/x-www-form-urlencoded格式的body可能是“title=test&sub=%E5%A4%A7%E5%AE%B6”。

chunk编码

有些webserver返回的http报文中,没有Content-Length头部,取而代之的是Transfer-Encoding: chunked,表示包体长度未知或者把包体拆成几个小块传输。每个 chunk 的格式如下所示:

${chunk-length}\r\n${chunk-data}\r\n
其中,${chunk-length} 表示 chunk 的字节长度,使用 16 进制表示,${chunk-data} 为 chunk 的内容
当 chunk 都传输完,需要额外传输 0\r\n\r\n 表示结束

下面是一个例子(来自HTTP 进阶之 chunked 编码):

HTTP/1.1 200 OK
Date: Wed, 01 Jan 2020 08:46:31 GMT
Connection: keep-alive
Transfer-Encoding: chunked

1\r\n
a\r\n
3\r\n
+-=\r\n
14\r\n
ghijklmnopqrstuvwxyz\r\n
0\r\n\r\n

SSL_connect

客户端调用SSL_connect进行握手,如果socket是非阻塞的,SSL_connect会立即返回-1,此时需要调用SSL_get_error获得具体错误,如果是SSL_ERROR_WANT_READ表示还在握手中,等待下次触发读时再调用SSL_connect,如此往复,直到SSL_connect返回1完成握手。

边沿触发下数据的读取

SSL/TLS是介于TCP与HTTP之间的一层,它本身有buffer,epoll触发读调用SSL_read时,先把数据从socket缓存读入SSL缓存,再从SSL缓存读入用户缓存。
没有SSL的时候,epoll触发读了,调用recv或read从socket缓存读到用户缓存,边沿触发模式下一次没读完的话,主动EPOLL_CTL_MOD EPOLLIN事件,此时只要该socket的缓存还有数据可以读,epoll_wait会返回读就绪。
而改为SSL_read后,如果一下把socket缓存都读到了SSL缓存中,而又没有全读入用户缓存,要是没有新数据来到,epoll_wait就不会返回读就绪,SSL缓存中的数据就只能等对端关闭socket的时候触发读了。
应该判断SSL_read的返回值。正数表示读入字节数目,判断返回值如果比SSL_read的第三个参数(最多读取的字节数)小,表示已读取完毕,否则应继续尝试调用SSL_read,而不能像没有SSL的时候直接返回,因为那样就可能会没机会再触发读了。0或负数需要再调用SSL_get_error返回具体错误,如果是SSL_ERROR_WANT_READ表示SSL缓存中已无数据。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-01-25 10:56:42  更:2022-01-25 10:57:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 10:24:31-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码