HTTP协议的学习,我之前避开了最基础最概念的东西,写到现在,我发现若是以前从来没接触过服务端,没了解过抓包,还不知道ABNF的一些核心规则。根本没法继续学下去
网络协议的概念实在太多,不动手实操一下,不仅记不住还难以理解。今天我们就与HTTP协议,进行一次亲密接触~
今天我在这儿就借着Form
表单提交发生的HTTP传输,来说下如何搭建Web服务器,以及如何抓包
关于ABNF的语法之后会结合实践,专门写一篇ABNF核心规则是如何描述HTTP的
今天我们就先来说一下Web服务的搭建吧,搭建服务器对应的代码,我已经放到了GitHub仓库上,文章中就仅截取片段代码了。GitHub Repo地址点击跳转,也可以直接clone
到你们自己本地
https://github.com/AdolphKevin/http-study-go.git
刚好我们大多数读者也正在学习Golang,我们就用Golang来搭建一个我们自己的服务端,用来与客户端交互,最后我们再用wireshark
切身体会一下HTTP协议的传输过程
我在这选择的框架是beego
,原因就是搭建Web
服务器快,而且文档都是中文文档,有兴趣深入了解框架的可以更方便的学习,最最重要的一点,用go get
命令安装时不会被墙
我们就先把环境搭建起来,在这需要安装的东西有
beego
bee
wireshark
如果还没有安装过go
的,可以看我之前的文章Vscode搭建go开发环境
beego
以及bee
的安装
$ go get -u github.com/astaxie/beego
$ go get -u github.com/beego/bee
我在这唯一值得提一点的事情就是bee
的环境配置,这点网上虽然有,但是一堆坑。文档也没明说,所以我在这说一下
Mac/Linux下的bee配置
不是把下面的内容全部放到Terminal
里面执行的,具体的执行步骤写在注释里了
# 打开配置文件
Vim ~/.bash_profile
## 加入下面两行
export GOPATH=/Users/naonao/go
export PATH=${PATH}:${GOPATH}/bin
## 重启配置文件
source ~/.bash_profile
不知道GOPATH
的路径在哪里,可以执行下go env
看看
在这我都说声抱歉,因为在外面出差,Windows下我没电脑搭建,所以没法写具体的操作步骤了,各位网上找找吧。思路就是将GOPATH下的bin文件夹,加入环境变量里
搭建Web服务器
配置好环境变量后,我们可以快速的搭建自己的Web服务器
Mac或者Linux直接在Terminal
里在GOPATH/src
目录选执行bee new '项目名称'
即可
## 进入GOPATH/src目录
cd ${GOPATH}/src
## 新建项目
bee new httpProject
在Windows下就在GOPATH/src
目录下打开cmd
执行bee new httpProject
即可
执行完之后可以在GOPATH
的src
目录下看到新建了一个非常标准的MVC
项目
默认的新建项目肯定不能符合我们的需求,所以我们要稍加改进
我们先在router/router.go
中的init
方法里添加一个路由,用来处理请求
beego.Router("/sendhttp", &controllers.MainController{})
再到controllers
里的default.go
文件中,添加如下代码,熟悉MVC
的读者大人肯定知道控制器接受用户的输入并调用模型和视图去完成用户的需求
所以我们得完善一下控制器收到请求后的处理,在这里我加了一个GET
请求与POST
请求
// Get 请求方法
func (c *MainController) Get() {
c.Data["Website"] = "beego.me"
c.Data["Email"] = "NaoNao@gmail.com"
c.TplName = "index.html"
}
// Post 请求方法
func (c *MainController) Post() {
c.Data["Website"] = "NaoNao"
c.Data["Email"] = "AdolfYin@gmail.com"
c.TplName = "naonao.html"
}
代码的意思就是GET请求返回index.html
页面,POST请求返回naonao.html
页面
所以我们还需要再到view
里,添加index.html
以及naonao.html
文件
index.html
文件里的完整内容大家去github
上看好了,因为贴这种代码极其影响阅读体验。index.html地址
html
里的关键代码如下,就是四种表单的提交
<p>GET方法默认提交</p>
<form action="/sendhttp" method="GET">
</form>
<p>POST方法默认提交</p>
<form action="/sendhttp" method="POST">
</form>
<p>POST方法以application/x-www-form-urlencoded提交</p>
<form action="/sendhttp" method="POST" enctype="application/x-www-form-urlencoded">
</form>
<p>POST方法,以multipart/form-data提交</p>
<form action="/sendhttp" method="POST" enctype="multipart/form-data">
待会我们再用wireshark
抓包工具来看,这四种不同的提交方式到底有何不同
好啦,说到这,我们已经完成了环境的搭建,Web服务器的搭建以及请求和响应方式
不过在运行Web服务器和抓包之前,我得再说点HTTP里form
表单的一些基础知识,不然待会说抓包的时候,你可能会一脸懵逼
Form表单与HTTP的关系
关于form
表单,它有三个核心的属性,分别是action
,method
,enctype
,这三个属性,都是与发起HTTP协议时相关联的
-
action
:提交表单时,发起一个HTTP请求,action
用来表示请求的URI -
method
:发起HTTP请求时,请求的方法是什么,比如是用GET
还是POST
-
enctype
:在POST
请求方法下,对表单内容在请求包体中的编码格式,默认为application/x-www-form-urlencoded
action
与method
上面这样简单一提,我想大家心里都清楚怎么使用了,我就说下enctype
的属性好了
在application/x-www-form-urlencoded
方式下,我们的数据会被编码成以&
分隔的键-值对,同时以=
分隔键和值,也就是我们常说的key=value
的形式
除了上面这种默认形式,还有multipart/form-data
方式,这种方式提交的表单,我们的数据分隔之后,会以boundary
开头,最后以last boundary
结尾。其中每一个部分的描述,皆有HTTP
头部描述子包体,比如Content-Type
。主要应用一种比较复杂的结构体编码
我们再重点看下multipart/form-data
的提交方式
Content-Type
来指明这是一个多表述包体,待会在wireshark
中抓包我们就能看到
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryWiz1KWG5b5uvEUJR\r\n
这种提交方式下,每一个文本输入,每一个单选框/复选框,每一个文件,都是作为一个独立的资源表述的。如果不清楚HTTP的表述,可以看我之前分享的文章HTTP:资源是怎么协商、表述的?
而boundary
分隔符的格式在ABNF语法中的定义为0*69<bchars> bcharsnospace
这是什么意思呢?这表示通过0~69(0*69)个字符(bchars)作为分隔符,也就是说最多不能超过70个字符
而这字符支持的格式(bcharsnospace),可以为DIGT
/ALPHA
/'
/(
/)
/+
/-
/_
/,
/.
//
/:
/=
/?
其中DIGT是数字的意思,ALPHA代表字母
所以各位自己抓包的时候,可以看看你们自己看到的分隔符是长啥样的,偷懒的话,就回过头去看看我抓的吧
开始抓包实践
好啦,了解了form
表单的HTTP关键属性,热身运动终于结束了。我们现在来运行我们的Web服务,来看一下我们的简单演示
现在我们可以在${GOPATH}/src/http-study-go
目录下,使用go run main.go
指令,即可运行我们刚刚搭建的Web服务器
用浏览器打开localhost:8080
,即可访问我们刚刚搭建的网站
接着我们打开wireshark
,找到我们本机的局域网环境。如果不知道是哪一个,可以在打开wireshark
后,打开我们自己刚刚搭建的网站localhost:8090
,有网络波动图形的,就是我们需要监视的网络
比如我下面截图的这个,(注意:每台电脑上的名称不一样,不要盲目抄作业)
想看看浏览器输入URI后发生了什么事情的,可以在找我们的本机网络后,用wireshark
监视着,再打开浏览器输入localhost:8080
打开我们自己的Web服务器。这样我们就能很清楚的看到TCP的三次握手(如下图)
以后人家问你浏览器输入URI按下回车后发生了什么事情,心里也有数了,看再多文章都不如自己动手试一试,当然了,这个不是今天的重点,今天就只提一下可以怎么看
继续回到之前的内容,我们来看看form
表单的提交是如何使用HTTP协议的
GET提交
我们先来看看GET提交是怎么一回事
我们先打开Chrome浏览器的开发者工具(F12),在使用GET
方式提交表单,看看Network
面板里是什么数据
我们可以看到发送了一个GET请求,将我们在表单中输入的内容变成了QueryString的形式请求了URI
我们点击一下旁边的view Source
,可以看到我们表单里填写的数据,其实是以UTF-8的编码格式进行的传输
我们再来看看wireshark
中的抓包情况,看看是如何表现的,先找到我们刚刚发出的请求,点击即可查看HTTP请求的内容
我们可以看到比Chrome中更详细的信息,最下面的蓝色区域,对应的是请求URI的Refere内容,左侧的蓝色区域是标准的ABNF语法,有兴趣的可以去了解了解
POST提交
好了,我们再来看看不指定enctype
的POST
提交又发生了什么,
在这儿我就不贴Chrome里的Network面板内容了,直接展示wireshark
中的内容了。需要注意的地方我都用红线圈出来了
可以看到,跟GET
提交相比,POST
多了一个包体,点开Content-Length
,我们可以看到请求的URI里已经没有参数了,并且Content-Type
里展示了我们的内容编码方式
默认为application/x-www-form-urlencoded
,后面的\r\n
是换行符的意思
第三种POST
提交并指定enctype=application/x-www-form-urlencoded
的提交方式我在这就不多说了
我们重点来看看enctype=mulipart/form-data
这种方式的提交
因为我们采用mulipart
的方式请求,在请求中含有大量包体,在Chrome的Network面板中是看不到,所以我还是直接放wireshark
抓取到的报文内容吧
在wireshark
中找到我们请求的HTTP报文,我们可以看到,请求内容多了两个
在Content-Type
中,多了一个boundary
,boundary
后面的分隔符是以/r/n
结尾的,并且在Content-Length
中,最后我们可以看到分隔符是以--/r/n
来结尾
多的另外一个东西,往下翻我们可以看到MIME multipart Media Encapsulation
,这是包体中包含的资源文件
点开后我们可以发现,首先会有一个First boundary
,里面的分隔符是以/r/n
结尾,表示所有资源的开始。到最后会有一个Last boundary
是以--/r/n
结尾,表示所有资源的结束
其中每一个资源都有着一个分隔符,每一个在分隔符后面都是以\r\n
结尾,直到最后一个资源,在\r\n
的前面多了2个-
,意味着整个请求包体的结束
点开其中的资源文件,会发现每一个资源文件都包含了Content-Disposition
这个字段,因为这个是必须要有的,表示这个资源的name
如果是非文本框之类的东西,还会有一个Contenty-Type
用于表示这个资源的类型
以我提交的文本文件来说一下,图中我圈出来了
可以看到Content-Type
告诉我,上传的是一个text/plain
的文件,点开下面的text data
,可以看到里面传输的内容为NaoNaoChiYu
所有的资源文件传输完后,最后会以一个Last boundary
的分隔符并加上--/r/n
来结尾
这就是一个完整的表单提交发生的HTTP传输
写在最后
最后给大家留一个问题。如果Form
表单中的字段,没有name
,只有id
,资源中的Content-Disposition
内容是什么?是否还会有这个内容?如果没有的话,服务器还能接收到这个资源文件么?
好了,今天借着说Form表单提交的方式,给大家说了下怎么搭建自己的Web服务,以及如何抓包
之后我会补一篇HTTP用ABNF语法表述的入门文章,因为我说过,我会以实践的方式来写HTTP协议的系列,所以后面的文章会有更多的内容牵扯抓包
如果看不懂ABNF语法,那看抓包的内容真的就是看天书了
马上月底了,下一篇文章就不写技术干货了,分享点别的内容。
毕竟收租时间到了,要开赞赏了。写技术干货都没人看,没人看就没人交租了…在外面出差过着9 11 7的日子,我还保持了稳定的每周二更新…这俩月我自己都佩服我自己了
有疑问加站长微信联系(非本文作者)