【问题标题】:How to calculate Content-length properly in tclhttpd?如何在 tclhttpd 中正确计算 Content-length?
【发布时间】:2019-11-06 10:18:21
【问题描述】:

我的 Tcl 源文件是 utf-8 格式。 Tclhttpd 不能正确发送国家字符,所以我对其进行了一些修改。但是,我也发送二进制文件,如 jpg 图像,有时二进制块存在于我的其他 utf-8 HTML 中。我很难计算出正确的 Content-length 以完全匹配浏览器接收到的内容(否则一些尾随字符会破坏下一个请求标头,或者浏览器会等待每个请求 30 秒,直到超时)。

也就是说,请问puts $socket 向套接字写入了多少字节?

我发现了一个特殊的 11 字节序列,它搞砸了计数:

proc dump3 string {
    binary scan $string c* c
    binary scan $string H* hex
    return [sdump $string]\n$c\n$hex
};#dump3
proc Httpd_ReturnData {sock type content {code 200} {close 0}} {
    global Httpd
    upvar #0 Httpd$sock data
    #...skip non-pertinent code...
set content \x4f\x4e\xc2\x00\x03\xff\xff\x80\x00\x3c\x2f
#content=ONÂÿÿ�</
#79 78 -62 0 3 -1 -1 -128 0 60 47
#4f4ec20003ffff80003c2f
puts content=[dump3 $content]
puts utf8=[dump3 [encoding convertto utf-8 $content]]

    if {[catch {
puts "string length=[string length $content] type=$type"
puts "stringblength=[string bytelength $content]"
    set len [string length $content]
    if [string match -nocase *utf-8* $type] {
        fconfigure $sock -encoding utf-8
        set len [string bytelength $content]
    }
puts "len=$len fcon=[fconfigure $sock]"
    HttpdRespondHeader $sock $type $close $len $code
    HttpdSetCookie $sock
    puts $sock ""
    if {$data(proto) != "HEAD"} {
        ##fconfigure $sock -translation binary -blocking $Httpd(sockblock)
        ##native: -translation {auto crlf} 
        fconfigure $sock -translation lf -blocking $Httpd(sockblock)
        puts -nonewline $sock $content
    }
    Httpd_SockClose $sock $close
    } err]} {
    HttpdCloseFinal $sock $err
    }
}

控制台的输出是:

内容=ONÂÿÿ� 79 78 -62 0 3 -1 -1 -128 0 60 47 4f4ec20003ffff80003c2f utf8=ONÃ�ÿÿÂ� 79 78 -61 -126 0 3 -61 -65 -61 -65 -62 -128 0 60 47 4f4ec3820003c3bfc3bfc280003c2f 字符串长度=11 类型=文本/html;字符集=utf-8 字符串长度=17 len=17 fcon=-blocking 0 -buffering full -buffersize 16384 -encoding utf-8 -eofchar {{} {}} -translation {auto crlf} -peername {128.0.0.71 128.0.0.71 55305} -sockname {128.0.0.8第 8016 代} HttpdRespondHeader 17

结果 Content-Length: 17 太多了,浏览器一直在等待。如果我能事先知道,puts 将从我的字符串中产生多少字节,剩下的就很容易了。有什么办法吗?

【问题讨论】:

    标签: utf-8 tcl content-length http-content-length


    【解决方案1】:

    对于通过 HTTP 传输的数据,content length should be the number of bytes in the data 在网络上观察到。使用Httpd_ReturnData 时,您需要确保向其提供要传输的二进制 数据; 它不会为您处理数据编码。

    要发送一定长度的二进制数据实际上很简单,你可以这样做:

    set binaryData [...]
    Httpd_ReturnData $sock "application/octet-stream" $binaryData
    # There are many other binary encodings; that's just the most universal one
    # Choose the right one for your application, of course
    

    要发送有长度的文本数据,你需要用encoding convertto做更多的工作:

    set textData [...]
    Httpd_ReturnData $sock "text/plain; charset=utf-8" \
            [encoding convertto utf-8 $textData]
    # Similarly, text/plain is a decent fallback here too
    

    (是的,如果您选择不同的编码,那么您应该在两个地方都提到这一点。您可能应该对当今时代的所有文本内容使用 UTF-8。)

    如果您可以从文件中提取数据,则应该这样做; Httpd_ReturnFileHttpd_ReturnData 更高效,因为它可以使用高效的数据传输技术移动数据。如果发送文本文件,您需要小心正确地描述文件的编码。到目前为止,最简单的方法是按照惯例,例如确定系统上的所有文本文件都是 UTF-8...


    您实际上不应该使用string bytelength,因为它以 Tcl 的一种内部编码(一种轻度反规范化的几乎 UTF-8)的单位报告。它返回的度量只有在你做一些非常奇怪的事情时才是正确的,比如生成需要知道缓冲区大小的 C 代码,其中包含将被馈送到 Tcl 实现中的字符串,这与你正在做的事情非常不同(我已经在使用 Tcl 的 20 多年中只做过一次这种事情;我从未听说过其他合法用途)。我相信它已被弃用,正是因为它在被太多人使用时存在许多微妙的错误。

    【讨论】:

    • tl;dr: 始终将二进制数据实际提供给Httpd_ReturnFile,即使数据被描述为客户端的文本。
    • 好的,所以我使用 set len [string length [encoding convertto utf-8 $content]] 而不是 set len [string bytelength $content],它似乎有效。但是我没有更改客户端代码,而是再次修改了 tclhttpd 本身,以保持客户端代码不变。感谢 Donal 的深刻见解!
    猜你喜欢
    • 2018-08-27
    • 2013-07-29
    • 1970-01-01
    • 2021-11-28
    • 2021-01-08
    • 1970-01-01
    • 2010-09-07
    • 2021-09-15
    • 1970-01-01
    相关资源
    最近更新 更多