写个网关还是很能练手的,这不在对接新的项目时,就遇到题头的报警。

_, err = fmt.Fprintf(w, string(str))
if err != nil {fmt.Println(err.Error())
}

一开始,没想到这块会报错,而是直接忽略的,这样就造成了测试时啥也没返回,但是上游是有数据返回的,最后追踪到这里。

于是,便开始万能的断点调试了,发现 w 也就是 http.ResponseWriter 有两个属性是对应此错误的,分别是 written 和 contentLength。其中 contentLength 是接口返回值中 body 的长度,在初始化时值为 -1。

// net/http/server.go// Read next request from connection.
func (c *conn) readRequest(ctx context.Context) (w *response, err error) {……w = &response{conn:          c,cancelCtx:     cancelCtx,req:           req,reqBody:       req.Body,handlerHeader: make(Header),contentLength: -1,closeNotifyCh: make(chan bool, 1),// We populate these ahead of time so we're not// reading from req.Header after their Handler starts// and maybe mutates it (Issue 14940)wants10KeepAlive: req.wantsHttp10KeepAlive(),wantsClose:       req.wantsClose(),}……}

而题头的错误,也定义此文件中。

// net/http/server.go// ErrContentLength is returned by ResponseWriter.Write calls
// when a Handler set a Content-Length response header with a
// declared size and then attempted to write more bytes than
// declared.
ErrContentLength = errors.New("http: wrote more than the declared Content-Length")

如果返回体的 header 头直接有 Content-Length ,那么便直接读取的此值。

// net/http/server.gofunc (w *response) WriteHeader(code int) {……if cl := w.handlerHeader.get("Content-Length"); cl != "" {v, err := strconv.ParseInt(cl, 10, 64)if err == nil && v >= 0 {w.contentLength = v} else {w.conn.server.logf("http: invalid Content-Length of %q", cl)w.handlerHeader.Del("Content-Length")}}……
}

如果是返回体 header 有 Transfer-Encoding: chunked ,那么 contentLength 便为 -1。

接下来便进入了最后的判断处。

// net/http/server.go// either dataB or dataS is non-zero.
func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err error) {……w.written += int64(lenData) // ignoring errors, for errorKludgeif w.contentLength != -1 && w.written > w.contentLength {return 0, ErrContentLength}……
}

这里,如果 contentLength 不是初始值(也就是返回体 header 头 Content-Length 有值),且 written 的值大于 contentLength 的值,便会报 wrote more than the declared Content-Length。

到此,就知道原因和如何解决了。

之所以出现这个问题,是因为,网关接收转发的返回值已经有了 Content-Length 值,而我的网关会在外面包一层,这样就会出现 written 的值大于 contentLength 的 case 了。想要规避,也很简单,让下游返回的头中加上 Transfer-Encoding:chunked ,这样便不会传 Content-Length 的响应头了。于是,在走上述判断的时便因条件不足而跳过了。

最后的最后,上述解决方案还是有有待提高,还是自身网关健壮的好,因此需要考虑目前出现这情况下,不让三方修改代码,而自身兼容考虑好此种情形。

strByte, _ = json.Marshal(ret)
str := string(strByte)
w.Header().Set("Content-Length", strconv.Itoa(len(str)))
w.WriteHeader(http.StatusOK)

之所以这么做是因为,这样可以更新 contentLength 值,当然了,具体更新是第四行代码,也就是熟读代码并掌握实现后如何为己所用了。这里贴出完整的代码。

func (w *response) WriteHeader(code int) {if w.conn.hijacked() {caller := relevantCaller()w.conn.server.logf("http: response.WriteHeader on hijacked connection from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line)return}if w.wroteHeader {caller := relevantCaller()w.conn.server.logf("http: superfluous response.WriteHeader call from %s (%s:%d)", caller.Function, path.Base(caller.File), caller.Line)return}checkWriteHeaderCode(code)w.wroteHeader = truew.status = codeif w.calledHeader && w.cw.header == nil {w.cw.header = w.handlerHeader.Clone()}if cl := w.handlerHeader.get("Content-Length"); cl != "" {v, err := strconv.ParseInt(cl, 10, 64)if err == nil && v >= 0 {w.contentLength = v} else {w.conn.server.logf("http: invalid Content-Length of %q", cl)w.handlerHeader.Del("Content-Length")}}
}

解决 golang 中 wrote more than the declared Content-Length相关推荐

  1. 解决vscode中golang插件依赖安装失败问题

    解决vscode中golang插件依赖安装失败问题 参考文章: (1)解决vscode中golang插件依赖安装失败问题 (2)https://www.cnblogs.com/feiquan/p/11 ...

  2. golang中包互相引用的解决方法

    golang中包互相引用的解决方法 参考文章: (1)golang中包互相引用的解决方法 (2)https://www.cnblogs.com/chukuang2004/p/6930898.html ...

  3. Ubuntu18.04配置无人驾驶赛车游戏TORCS(解决找不到GL/glut.h、AL/alut.h和libXmu,以及编译中出现‘isnan’was not declared等)

    Ubuntu18.04配置无人驾驶赛车游戏平台TORCS 一.资源下载TORCS<The Open Racing Car Simulator> 二.环境搭建 三.运行游戏与测试 菜菜的博主 ...

  4. 解决 golang json 中 invalid character ‘\r‘ in string literal 报错

    type Demo struct {Content string `json:"content"` }func main() {var demo Demostr := " ...

  5. Golang中Buffer高效拼接字符串以及自定义线程安全Buffer

    本文原创文章,转载注明出处,博客地址 https://segmentfault.com/u/to... 第一时间看后续精彩文章.觉得好的话,顺手分享到朋友圈吧,感谢支持. Go中可以使用"+ ...

  6. vendor自动恢复_解决 vendor 中存在大小写变更问题

    前言 go mod (vendor) 是 golang 中依赖管理的一个工具,而 git 是个版本管理工具,两者结合本该是相得益彰事情,然而在某些情况下并不是如此 在某个项目中,需要更新 k8s.io ...

  7. golang中并发sync和channel

    golang中并发sync和channel chenbaoke · 2014-12-08 13:00:01 · 19151 次点击 · 预计阅读时间 5 分钟 · 不到1分钟之前 开始浏览 这是一个创 ...

  8. golang中的sync.WaitGroup

    golang中的sync.WaitGroup Posted on 2015/04/09刚才看golang的sync的包,看见一个很有用的功能.就是WaitGroup. 先说说WaitGroup的用途: ...

  9. onclick如何调用含参函数_在 golang 中如何调用私有函数(绑定隐藏的标识符)

    名字在 golang 中的重要性和在其他任何一种语言是一样的.他们甚至含有语义的作用:在一个包的外部某个名字的可见性是由这个名字首字母是否是大写来决定的. 有时为了更好的组织代码或者在其他包使用某些隐 ...

最新文章

  1. js获取html代码中所有图片地址
  2. 【SSH】——Hibernate三种状态之间的转化
  3. 编写函数实现员工信息录入和输出_编写我的第一个Linux 内核模块“hello_module”...
  4. why is pc important for university students?
  5. java 十六进制转十进制_JAVA知识-分析JAVA中的重点和难点
  6. 【转载】ORM的概念
  7. Ext.state.Manager.setProvider(new Ext.state.CookieProvider())
  8. trigger_name 的命名规范
  9. 拒绝无用的长篇大论!仅12张图片,最全的中台精华都在这里了
  10. 远哥跟你说 Spring的 classpath 通配符加载配置文件
  11. linux改变所属用户组
  12. PHP如果某商品下的所有货品库存都为0,则下架该商品
  13. 怎么使用计算机操作鼠标,鼠标操作怎么用?电脑鼠标操作图文教程
  14. 耿建超英语语法---陈述句(2)
  15. SSM整合步骤(超详细)
  16. 使用存储过程返回结果集
  17. R语言,导入XLSX的Excel数据 多sheet
  18. 动物识别专家系统c语言代码,动物识别专家系统(C++版)
  19. ORACLE一次大量数据删除导致问题的处理
  20. Mac终端提示:You have not agreed to the Xcode license agreements.

热门文章

  1. TCP长连接及连接管理
  2. 图像的二值化之python+opencv
  3. word不能读出html表格,WORD里面表格不能自动跳到下一页解决方案
  4. 一个“精神病”人的世界观
  5. Linux服务器操作系统快速删除大量/大文件
  6. form表单提交和ajax表单提交
  7. macOS Big Sur发布了!适用于所有兼容的Mac机型!
  8. 【日拱一卒行而不辍20220926】自制操作系统
  9. excel自定义功能区图标_将您自己的图标添加到Excel自定义功能区选项卡
  10. TM1668兼用VK1668 SOP24/SSOP24 应用于VCR.DVD 等产品的显示驱动