之前我们已经看了请求报文的获取和响应报文的生成,我们这次来整合一下请求和响应报文,可以简单搞一个“框架”了。
问题回顾
响应报文生问题
我们之前测试的时候,计划让其返回<h1>hello world</h1>
,结果实际上返回的是<h1>hello world</h
,少了2个字节,而浏览器默认给修复了,所以我们在浏览器没有看出异常。
我们通过抓包来分析,我们发现,我们在学习报文的地方就搞错了。
我们查看我们之前画的响应报文
正确的响应报文和请求报文
上面是有问题的,在我们 首部行 和 响应主体 之间,不是\r\n\r\n
进行分割的,而是\r\n
进行分割的,所以说,正确的响应报文应该是这样的:
而请求报文亦然
好,此时我们修改响应报文代码,应当为
之前是responseByte = append(responseByte, []byte(CRLF2)...)
,我们将其修改为responseByte = append(responseByte, []byte(CRLF)...)
func buildResponsePack(rp responsePackages)([]byte) { responseByte := make([]byte,0) // 构建状态行 // 版本 responseByte = append(responseByte, []byte(rp.HttpVersion+" ")...) // 状态码 responseByte = append(responseByte, []byte(strconv.Itoa(int(rp.StatusCode))+" ")...) // 短语 responseByte = append(responseByte, []byte(rp.StatusMsg+CRLF)...) // 首部信息 for k,v := range rp.responseHeader { responseByte = append(responseByte, []byte(k+": ")...) responseByte = append(responseByte, []byte(v+CRLF)...) } // 添加响应主体 if 0 < len(rp.Body) { // 新增 Content-Length responseByte = append(responseByte, []byte("Content-Length: "+strconv.Itoa(len(rp.Body))+"\r\n")...) } responseByte = append(responseByte, []byte(CRLF)...) responseByte = append(responseByte, rp.Body...) fmt.Printf("生成响应报文\n%s\n\n\n",string(responseByte)) return responseByte }
这样修改的话,我们再次请求就不会遇到问题了。
整合请求报文和响应报文
我们已经将相关代码已经放到gitee
: gitee.com/pdudo/Sampl…
我们整合后的demo
大概是这样的
package main import "gitee.com/pdudo/SampleHttp" func main() { SampleHttp.Route("GET","/pdudo", func(info *SampleHttp.HttpInfo) { info.SetHttpStatusCode(200) info.Write([]byte("<h1>hello world</h1>")) }) SampleHttp.Route("GET","/123", func(info *SampleHttp.HttpInfo) { info.SetHttpStatusCode(301) info.SetResponseHeader("Location","https://juejin.cn") }) SampleHttp.StartServer("0.0.0.0:8082") }
其中,SampleHttp.StartServer
是启动http
服务器,SampleHttp.Route
是定义路由,其接收三个参数,第一个是请求的方法,第二个是路由,第三个则是函数,函数中需要传入参数*SampleHttp.HttpInfo
,这个不是闭包,仅仅是将函数作为参数传递。
将函数作为参数传递
将函数形参作为参数传递不是闭包哦!
无形参参数传递
我们看个函数案例
假设我们想获取函数的执行时间,我们可以将函数作为参数传递
例如
我们定义了一个x
函数,其中形参是一个函数,名称叫做fun
,我们仅需要在x
函数中获取一个时间,然后执行fun
函数,最后再获取执行时间就可以,我们并不需要关心fun
是啥,我们仅知道它是一个无形参无返回值的函数就可以了。
这样,我们在调用x
的时候,就可以再其形参部分写函数了,我们假设函数中就写一个sleep
30秒,我们可以执行下程序看下。
有形参的函数作为参数传递
我们还是先看demo
我们定义了一个函数x
,其形参是一个函数,函数又有一个形参,为int
类型的,那么我们想在x
函数中调用这个函数,我们至少得声明一个int
类型的变量,然后再调用。
记录路由
我们将路由信息定义为map[string]func(info *HttpInfo)
形式,即,定义了一个key
为string
类型的map,其value
为info *HttpInfo
,再次基础上,我们给没种方法都定义了一个路由map
,目前仅支持GET
和POST
。
在记录路由的时候,我们根据其方法将函数存入不同的map
中,例如
这样的话,key
是我们的URL
,value
则是我们的函数。
服务器工作流程
在项目中,我们调用StartServer
函数
在函数中,会监听我们定义的地址,并且开始工作,我们收到请求后,我们会定义一个新的HttpInfo
,然后开一个协程去处理该请求
在上述函数中,我们会拆解http
协议并且放入我们HttpInfo
变量中,而后根据请求协议,例如GET
、POST
等,我们会按照请求的URL
作为map
的key
去取函数值,若取到的话,我们就执行该函数,然后将HttpInfo
数据组合,发回客户端。至此,流程结束。
总结
在文章中,我们总结了前面学习报文的不足,我们重新梳理了请求报文和响应报文代码,而后,我们根据前2篇文章,我们定义了一个属于自己的“框架”,用于自定义路由等,总的而言,将这几篇文章,作为入门http
协议来看的话,是非常不错的,赞。