Index: README.md ================================================================== --- README.md +++ README.md @@ -26,10 +26,11 @@ * [How To Compile wapptclsh - Or Not](/doc/trunk/docs/compiling.md) * [Limitations of Wapp](/doc/trunk/docs/limitations.md) * [Example Applications](/file/examples) * [Real-World Uses Of Wapp](/doc/trunk/docs/usageexamples.md) * [Debugging Hints](/doc/trunk/docs/debughints.md) + * [Wapp does not send unnecesary CRs](/doc/trunk/docs/crlf.md) Simple Live Demos ----------------- * ADDED docs/crlf.md Index: docs/crlf.md ================================================================== --- /dev/null +++ docs/crlf.md @@ -0,0 +1,24 @@ +Wapp Does Not Send CRLF +======================= + +Wapp uses \\n line endings, not \\r\\n. This is deliberate and in blatant +defiance of RFC-2616. The lead developer of Wapp believes that that CRLF +line endings are a harmful anachronism and need to be abolished. + +Even though RFC-2616 requires CRLF line endings, it does recommend that clients +also accept \\n line endings. All known HTTP clients abide by this recommendation, and +so Wapp's refusal to play by the rules is harmless. And it reduces bandwidth +slightly. It is a good thing. The omission of unnecessary \\r characters is +a feature of Wapp, not a bug. + +~~~ pikchr +sin45 = sin(3.141592653/4) +C: circle "CRLF" big big bold thick fit +C2: circle thick thick radius C.radius at C.c color red + line thick thick from (C.x-sin45*C.radius,C.y-sin45*C.radius) \ + to (C.x+sin45*C.radius,C.y+sin45*C.radius) color red +T1: text "CRLF-free" bold fit with .s at 1mm above C.n +T2: text "Zone" bold fit with .n at 1mm below C.s + box ht dist(T1.s,T2.n)+lineht*1.2 wid C2.width+lineht*0.5 \ + fill yellow thick thick radius 3mm at C.c behind C +~~~ Index: wapp.tcl ================================================================== --- wapp.tcl +++ wapp.tcl @@ -588,19 +588,19 @@ if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { dict set wapp $nm [wappInt-decode-url [lindex $qsplit 1]] } } } elseif {[string match multipart/form-data* $ctype]} { - regexp {^(.*?)\r\n(.*)$} [dict get $wapp CONTENT] all divider body + regexp {^(.*?)\n(.*)$} [dict get $wapp CONTENT] all divider body set ndiv [string length $divider] while {[string length $body]} { set idx [string first $divider $body] set unit [string range $body 0 [expr {$idx-3}]] set body [string range $body [expr {$idx+$ndiv+2}] end] - if {[regexp {^Content-Disposition: form-data; (.*?)\r\n\r\n(.*)$} \ + if {[regexp {^Content-Disposition: form-data; (.*?)\n\r?\n(.*)$} \ $unit unit hdr content]} { - if {[regexp {name="(.*)"; filename="(.*)"\r\nContent-Type: (.*?)$}\ + if {[regexp {name="(.*)"; filename="(.*)"\r?\nContent-Type: (.*?)$}\ $hdr hr name filename mimetype] && [regexp {^[a-z][a-z0-9]*$} $name]} { dict set wapp $name.filename \ [string map [list \\\" \" \\\\ \\] $filename] dict set wapp $name.mimetype $mimetype @@ -747,36 +747,36 @@ # If the page handler invokes "wapp-reply-code ABORT" then close the # TCP/IP connection without sending any reply wappInt-close-channel $chan return } elseif {$chan=="stdout"} { - puts $chan "Status: $rc\r" + puts $chan "Status: $rc" } else { - puts $chan "HTTP/1.1 $rc\r" - puts $chan "Server: wapp\r" - puts $chan "Connection: close\r" + puts $chan "HTTP/1.1 $rc" + puts $chan "Server: wapp" + puts $chan "Connection: close" } if {[dict exists $wapp .reply-extra]} { foreach {name value} [dict get $wapp .reply-extra] { - puts $chan "$name: $value\r" + puts $chan "$name: $value" } } if {[dict exists $wapp .csp]} { set csp [dict get $wapp .csp] regsub {\n} [string trim $csp] { } csp - puts $chan "Content-Security-Policy: $csp\r" + puts $chan "Content-Security-Policy: $csp" } set mimetype [dict get $wapp .mimetype] - puts $chan "Content-Type: $mimetype\r" + puts $chan "Content-Type: $mimetype" if {[dict exists $wapp .new-cookies]} { foreach {nm val} [dict get $wapp .new-cookies] { if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { if {$val==""} { - puts $chan "Set-Cookie: $nm=; HttpOnly; Path=/; Max-Age=1\r" + puts $chan "Set-Cookie: $nm=; HttpOnly; Path=/; Max-Age=1" } else { set val [wappInt-enc-url $val] - puts $chan "Set-Cookie: $nm=$val; HttpOnly; Path=/\r" + puts $chan "Set-Cookie: $nm=$val; HttpOnly; Path=/" } } } } if {[string match text/* $mimetype]} { @@ -785,12 +785,12 @@ catch {wappInt-gzip-reply reply chan} } } else { set reply [dict get $wapp .reply] } - puts $chan "Content-Length: [string length $reply]\r" - puts $chan \r + puts $chan "Content-Length: [string length $reply]" + puts $chan "" puts -nonewline $chan $reply flush $chan wappInt-close-channel $chan } @@ -798,11 +798,11 @@ # proc wappInt-gzip-reply {replyVar chanVar} { upvar $replyVar reply $chanVar chan set x [zlib gzip $reply] set reply $x - puts $chan "Content-Encoding: gzip\r" + puts $chan "Content-Encoding: gzip" } # This routine runs just prior to request-handler dispatch. The # default implementation is a no-op, but applications can override # to do additional transformations or checks.