如何在 tclhttpd 中正确计算 Content-length?
How to calculate Content-length properly in tclhttpd?
我的 Tcl 源文件是 utf-8。 Tclhttpd 不会正确发送国家字符,因此我对其进行了一些修改。但是,我也发送二进制文件,如 jpg 图像,有时二进制块出现在我的其他 utf-8 HTML 中。我很难计算出正确的 Content-length 来准确匹配浏览器接收到的内容(否则某些尾随字符会破坏 next-request headers 或浏览器每次请求等待 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
}
}
控制台上的输出是:
content=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
string length=11 type=text/html;charset=utf-8
stringblength=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 gen 8016}
HttpdRespondHeader 17
结果Content-Length:17太多了,浏览器一直在等待。如果我能事先知道,puts
将从我的字符串中生成多少字节,剩下的就很容易了。有办法吗?
对于通过 HTTP 传输的数据,content length should be the number of bytes in the data 在线路上观察到 。使用 Httpd_ReturnData
时,您需要确保向其提供要传输的 binary 数据; 它不会为您处理数据编码。
要发送具有一定长度的二进制数据其实很容易,您可以:
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_ReturnFile
比 Httpd_ReturnData
更高效,因为它可以使用高效的数据传输技术移动数据。如果发送文本文件,您需要注意正确描述文件的编码。到目前为止,最简单的方法是按照约定,例如确定系统上的所有文本文件都是 UTF-8...
您几乎不应该使用 string bytelength
,因为它以 Tcl 的内部编码(一种轻微非规范化的几乎 UTF-8)之一的单位报告。 it returns 的度量只有在你做一些非常奇怪的事情时才是正确的,比如生成需要知道缓冲区大小的 C 代码,这些缓冲区包含将被馈送到 Tcl 实现中的字符串,这与你正在做的很不一样(在使用 Tcl 的 20 多年里,我只做过一次这种事情;我从未听说过其他合法用途)。我认为它被弃用正是因为它在被太多人使用时有一堆细微的错误。
我的 Tcl 源文件是 utf-8。 Tclhttpd 不会正确发送国家字符,因此我对其进行了一些修改。但是,我也发送二进制文件,如 jpg 图像,有时二进制块出现在我的其他 utf-8 HTML 中。我很难计算出正确的 Content-length 来准确匹配浏览器接收到的内容(否则某些尾随字符会破坏 next-request headers 或浏览器每次请求等待 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
}
}
控制台上的输出是:
content=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 string length=11 type=text/html;charset=utf-8 stringblength=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 gen 8016} HttpdRespondHeader 17
结果Content-Length:17太多了,浏览器一直在等待。如果我能事先知道,puts
将从我的字符串中生成多少字节,剩下的就很容易了。有办法吗?
对于通过 HTTP 传输的数据,content length should be the number of bytes in the data 在线路上观察到 。使用 Httpd_ReturnData
时,您需要确保向其提供要传输的 binary 数据; 它不会为您处理数据编码。
要发送具有一定长度的二进制数据其实很容易,您可以:
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_ReturnFile
比 Httpd_ReturnData
更高效,因为它可以使用高效的数据传输技术移动数据。如果发送文本文件,您需要注意正确描述文件的编码。到目前为止,最简单的方法是按照约定,例如确定系统上的所有文本文件都是 UTF-8...
您几乎不应该使用 string bytelength
,因为它以 Tcl 的内部编码(一种轻微非规范化的几乎 UTF-8)之一的单位报告。 it returns 的度量只有在你做一些非常奇怪的事情时才是正确的,比如生成需要知道缓冲区大小的 C 代码,这些缓冲区包含将被馈送到 Tcl 实现中的字符串,这与你正在做的很不一样(在使用 Tcl 的 20 多年里,我只做过一次这种事情;我从未听说过其他合法用途)。我认为它被弃用正是因为它在被太多人使用时有一堆细微的错误。