golang net/http包 http请求的字节码读取与解析。

栏目: Go · 发布时间: 6年前

内容简介:先配置Header最长读取时间、req最长读取时间、req最大读取长度默认6M。RFC7230禁止\r\n参数,Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符。但go net/http包放宽了这个要求。先构建newTextprotoReader,由于缓冲区是对象复用的,用完后要defer put。共完以以下解析任务:

c.readRequest(ctx)读请求

先配置Header最长读取时间、req最长读取时间、req最大读取长度默认6M。

RFC7230禁止\r\n参数,Url中只允许包含英文字母(a-zA-Z)、数字(0-9)、-_.~4个特殊字符以及所有保留字符。但go net/http包放宽了这个要求。

读出Request

先构建newTextprotoReader,由于缓冲区是对象复用的,用完后要defer put。共完以以下解析任务:

  • 协议第一行
  • URL
  • Header
  • Body

TextprotoReader数据结构,将字节码Reader转成文本Reader。

type Reader struct 
    R   *bufio.Reader
    dot *dotReader
    buf []byte // a re-usable buffer for readContinuedLineSlice
}
type dotReader struct {
    r     *Reader
    state int
}
//struct之间相互引用

第一步,从第一行解析出method uri prototype。

第二步解析URL。url.URL数据结构:

type URL struct {
    Scheme     string 
    Opaque     string    
    User       *Userinfo 
    Host       string    
    Path       string    
    RawPath    string   
    ForceQuery bool      
    RawQuery   string    
    Fragment   string   
}

解析Scheme,协议前缀(小写)。有查询参数?,则配置url.ForceQuery url.RawQuery。有认证信息///...//,则解析url.User url.Host。最后配置url.Path和url.RawPath,如果Path==RawPath,则RawPath=""。

第三步解析MIMEHeader。

第四步readTransfer。重新配置如下参数:RequestMethod ProtoMajor ProtoMinor Header Trailer ContentLength Close。对于Body,如果encodings支持chunked,读取流用chunkedReader包裹。默认情况用LimitedReader,无body赋空的struct{}。

Request检查并补全

以下情况返回非空err,示得到正确的请求:

  • 请求太大,超过了readLimitSize。
  • 不支持的0.x和2+的HTTP协议版本。
  • 请求Header中没有Host、超过1个或不合法。合法字符集为httpguts.validHostByte。
  • Header的Key或Value不合法。Key的要求必须一httpguts.isTokenTable字符集,Value的要求不可以是控制字符集。

最后配置req.ctx req.RemoteAddr req.TLS body.doEarlyClose = true。

构建Response并包装chunkWriter

构建Response:

w = &response{
        conn:          c,
        cancelCtx:     cancelCtx,
        req:           req,
        reqBody:       req.Body,
        handlerHeader: make(Header),
        contentLength: -1,
        closeNotifyCh: make(chan bool, 1),

        wants10KeepAlive: req.wantsHttp10KeepAlive(),
        wantsClose:       req.wantsClose(),
    }

其中closeNotifyCh必须在构建时初始化,没有content所以先置contentLength为-1。

配置w.cw并被w.w包裹。w.cw缓冲默认大小2M。

请求未正确获取的情况

获取Request可能出现如下错误:

  • 请求超过readLimitSize,返回431错误,并刷新conn的读写缓冲。
  • basRequest错误,返回400错误。包括网络原因导致的读取错误。

w.finishRequest()

先上响应数据结构:

type response struct {
    conn             *conn 
    req              *Request 
    reqBody          io.ReadCloser
    cancelCtx        context.CancelFunc
    wroteHeader      bool               
    wroteContinue    bool               
    wants10KeepAlive bool               
    wantsClose       bool               

    w  *bufio.Writer 
    cw chunkWriter

    handlerHeader Header
    calledHeader  bool 

    written       int64 
    contentLength int64 
    status        int   

    closeAfterReply bool

    requestBodyLimitHit bool

    trailers []string

    handlerDone atomicBool 

    dateBuf   [len(TimeFormat)]byte
    clenBuf   [10]byte
    statusBuf [3]byte

    closeNotifyCh  chan bool
    didCloseNotify int32 
}

response字段可以分类为:大对象、缓冲、KV对或bool型的状态参数。

大对象有:

  • conn为TCP连接指针
  • req为对应的请求指针
  • reqBody为req.Body
  • cancelCtx为连接上下文context

状态字段:

  • requestBodyLimitHit,读req.Body使用了maxBytesReader保护机制,当请求信息过大达到上限,就会在这个参数上反映。
  • 4个Header参数:
    • wroteHeader响应Header写入完成
    • wroteContinue响应加入了100 Continue
    • wants10KeepAlive为Connection "keep-alive"连接重用参数
    • wantsClose为Connection "close"连接重用参数
  • 3个数据: dateBuf clenBuf statusBuf对应Date Content-Length status cod
  • 3个Body相关参数:contentLength written status三个字段用来校验响应内容是否写入完整,如果未写完则不对此连接进行复用。定位到上一篇文章serve()方法中有提到。
  • trailers:指写完响应之后,还需要向TCP连接写入的数据
  • handlerDone 表示handler是否存在
  • handlerHeader calledHeader保存Header

缓冲区字段

chunkWriter数据结构:

type chunkWriter struct {
    res *response
    header Header
    wroteHeader bool
    chunking bool 
}

chunkWriter包裹了Response,功能之一是完成Header设置,包括Content-Type Content-Length chunk-header。bufio.Writer是chunkWriter是缓冲包裹。

逻辑流

共完成三层缓冲写入:resp.w->resp.cw->conn.bufw

handler将响应写入到response.w。

调用w.w.Flush()将w写入到cw,注意到Flush()操作,如果未刷空缓存并报错,触发拷贝操作。报错不会退回已写出的数据。

copy(b.buf[0:b.n-n], b.buf[n:b.n]

进而调用cw.Write(),根据cw.chunking参数。

  • 使能,则全部数据写入到TCP缓冲。
  • 否则返回实际向TCP缓冲的写入量。

putBufioWriter(w.w)清空resp.w缓冲,如果池化放回sync.pool。

根据chunkWriter的定义,w.cw.close()负责cw的结束工作:写入换行符和resp.trailers数据。

最后刷新TCP缓冲w.conn.bufw.Flush(),完成响应包发送。并正确关闭request。


以上所述就是小编给大家介绍的《golang net/http包 http请求的字节码读取与解析。》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Persuasive Technology

Persuasive Technology

B.J. Fogg / Morgan Kaufmann / 2002-12 / USD 39.95

Can computers change what you think and do? Can they motivate you to stop smoking, persuade you to buy insurance, or convince you to join the Army? "Yes, they can," says Dr. B.J. Fogg, directo......一起来看看 《Persuasive Technology》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具