Как правильно рассчитать Content-length в tclhttpd? - PullRequest
0 голосов
/ 06 ноября 2019

Мои исходные файлы Tcl находятся в utf-8. Tclhttpd не будет отправлять национальные символы должным образом, поэтому я немного изменил его. Однако я также отправляю двоичные файлы, такие как изображения jpg, и иногда в моем HTML-коде utf-8 присутствуют двоичные фрагменты. У меня возникают трудности с вычислением правильной длины содержимого, чтобы точно соответствовать тому, что получает браузер (в противном случае некоторые завершающие символы забивают заголовки следующего запроса или браузер продолжает ждать 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 из моей строки сделает, остальное будет легко. Есть ли способ?

1 Ответ

1 голос
/ 06 ноября 2019

Для данных, передаваемых по HTTP, длина контента должна быть числом байтов в данных , как отмечено в проводе . При работе с 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_ReturnFile более эффективен, чем Httpd_ReturnData, поскольку он может перемещать данные, используя эффективные методы передачи данных. При отправке текстового файла, вы должны быть осторожны, чтобы правильно описать кодировку файла. Безусловно, самый простой способ сделать это - по соглашению, например, решить, что все текстовые файлы в вашей системе имеют формат UTF-8 ...


Вы практически никогда не должны использовать string bytelength, так как это сообщаетв единицах, которые являются одним из внутренних кодировок Tcl (слегка денормализованный почти-UTF-8). Мера, которую он возвращает, верна только тогда, когда вы делаете что-то очень странное, например, генерируете код на C, который должен знать размеры буфера, содержащие строки, которые будут переданы в реализацию Tcl, а это совсем не то, что вы делаете (делал подобные вещи только один раз за более чем 20 лет использования Tcl; я никогда не слышал о другом законном использовании). Я считаю, что он устарел именно потому, что в нем слишком много мелких ошибок в том, как его используют слишком многие люди.

...