记一次 http 400 -- invalid charset in header content

187 阅读2分钟

在一次项目发版之后,发现线上定时任务必现 400!当时一脸懵逼,依稀记得改动并不涉及涉及任何 http client。当时的第一反应便是 debug,尴尬的是一开始搞错排查方向 -- 一味的查看 response 的 message,问题是 400 的错误连请求都没有发出去 !后来寻思着会不会是 client 对异常明细做了包装 ( 这种包装也是常有的事情 ),转而就用 postman ( 也正是这个转变找打到了问题的本质 ): image.png 按照错误仔细观看发现 header sourceUrl 存在中文,如错误所示,看起来是字符集的问题,**但按理来说字符集理应是乱码而非报错?!**那看起来只能从 ( HTTP 协议 ) RFC 下手 ,经过一顿猛如虎的 Google,找到一篇:rfc7230 -- Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing 其中关于 Header Field 的定义 ( 为了便于理解有所变动 ):

3.2.  Header Fields

   Each header field consists of a case-insensitive field name followed
   by a colon (":"), optional leading whitespace, the field value, and
   optional trailing whitespace.

     header-field   = field-name ":" OWS field-value OWS

     field-name     = token
     field-value    = *( field-content / obs-fold )
     field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
     field-vchar    = VCHAR / obs-text
                    ; VCHAR (any visible [USASCII] character)

     obs-fold       = CRLF 1*( SP / HTAB )
                    ; obsolete line folding
                    ; see Section 3.2.4

     OWS            = *( SP / HTAB )
                    ; HTAB (horizontal tab)
                    ; SP (space)

     obs-text       = %x80-FF

   The field-name token labels the corresponding field-value as having
   the semantics defined by that header field.  For example, the Date
   header field is defined in Section 7.1.1.2 of [RFC7231] as containing
   the origination timestamp for the message in which it appears.

在上述的规范中,可以看到:field-value 需要为 ASCII 中的可见字符,对应到具体的 http client 如:在 okhttp 中这么一个校验: image.png

在对 value 的判断中,value 的字符集需要在 0x20-0x7E 之间,二者就正对应 ASCII 的可视字符范围!最后回到问题场景本身,发现是在脚手架中的增了红色框框一行导致: image.png

那么处理就很简单了,用如下代码处理下非 ASCII 字符:

// 其中的 charset 可以为任意有效的 charset,只需要双端对其
// 输出:%E4%B8%AD%E6%96%87
URLEncoder.encode("中文", "UTF-8") 

在写底层组件的时候,特别需要注意兼容性问题,为了避免字符集代码的不可预期问题 ( 即 unsafe characters ),一个良好的操作是进行 ASCII / BASE64 编码!