Posted to tcl by aspect at Tue Jul 08 05:26:25 GMT 2014view raw
- The bad combination is tls::socket + [fconfigure -blocking 0] + [chan push]. The result is [flush] raising EINVAL on first attempt to write to the socket.
- Here's a minimal test case:
- puts Tcl:[info patchlevel]
- puts tls:[package require tls]
- namespace eval nopchan {
- proc initialize args {info procs}
- proc finalize args {}
- proc clear args {}
- proc write {h data} {
- puts [list ::nopchan::write $data]
- return $data
- }
- namespace export *
- namespace ensemble create
- }
- set sock [tls::socket google.com 443]
- chan push $sock nopchan
- chan configure $sock -blocking 0
- puts -nonewline $sock "GET"
- puts "Calling flush"
- flush $sock
- # flush: error writing "sockb41ef0": invalid argument
- Reproduced with tcl trunk + tls trunk, both with and without dgp's patches at http://core.tcl.tk/tcl/info/c31ca233cacd7d6184877c54182f4b3a6ccb30f1
- Building tls with -NDEBUG yields interesting results. All failing cases reduce to this:
- Calling flush
- ::nopchan::write GET
- BIO_write(0xdc11b0, 15)
- WaitForConnect(0xdc11b0)
- BioWrite(0xdc1b30, <buf>, 150) [0xdc13b0]
- [0xdc13b0] BioWrite(150) -> 150 [0.0]
- BioCtrl(0xdc1b30, 0x6, 0x0, 0xdc1830)
- BioRead(0xdc1b30, <buf>, 5) [0xdc13b0]
- [0xdc13b0] BioRead(5) -> -1 [0.11]ERR(5, 11)
- Output(15) -> -1
- error writing "sockdc1330": invalid argument
- Trying to decouple WaitForConnect, I made a test case using [socket -async].
- # using same nopchan from above:
- proc test {} {
- set sock [tls::socket -async google.com 443]
- chan push $sock nopchan
- fconfigure $sock -blocking 0
- fileevent $sock writable [info coroutine]
- yield
- puts "CONNECTED"
- fileevent $sock writable {}
- puts -nonewline $sock GET
- puts "Calling flush"
- flush $sock
- }
- fconfigure stdout -buffering line
- coroutine coro test
- vwait forever
- This exhibits exactly the same behaviour on tls trunk - including WaitForConnect only being called after [write]:
- Tcl:8.6.1
- tls:1.6.3
- TlsWatchProc(0x4)
- CONNECTED
- TlsWatchProc(0x0)
- Calling flush
- nopchan::write GET
- BIO_write(0x28c8c30, 3)
- WaitForConnect(0x28c8c30)
- BioWrite(0x28c94b0, <buf>, 150) [0x28c8d30]
- [0x28c8d30] BioWrite(150) -> 150 [0.0]
- BioCtrl(0x28c94b0, 0x6, 0x0, 0x28c91b0)
- BioRead(0x28c94b0, <buf>, 5) [0x28c8d30]
- [0x28c8d30] BioRead(5) -> -1 [0.11]ERR(5, 11)
- Output(3) -> -1TlsWatchProc(0x0)
- error flushing "sock28c8cb0": invalid argument
- But with dgp's patches, it succeeds:
- Tcl:8.6.1
- tls:1.6.3
- TlsWatchProc(0x4)
- WaitForConnect(0x28ccc10)
- BioWrite(0x28cd490, <buf>, 150) [0x28ccd10]
- [0x28ccd10] BioWrite(150) -> 150 [0.0]
- BioCtrl(0x28cd490, 0x6, 0x0, 0x28cd190)
- BioRead(0x28cd490, <buf>, 5) [0x28ccd10]
- [0x28ccd10] BioRead(5) -> -1 [0.11]ERR(5, 11)
- : WaitForConnect(0x28ccc10)
- : BioRead(0x28cd490, <buf>, 5) [0x28ccd10]
- : [0x28ccd10] BioRead(5) -> -1 [0.11]ERR(5, 11)
- (lines marked : repeated several hundred times)
- WaitForConnect(0x28ccc10)
- BioRead(0x28cd490, <buf>, 5) [0x28ccd10]
- [0x28ccd10] BioRead(5) -> 5 [0.0]
- BioRead(0x28cd490, <buf>, 1) [0x28ccd10]
- [0x28ccd10] BioRead(1) -> 1 [0.0]
- BioRead(0x28cd490, <buf>, 5) [0x28ccd10]
- [0x28ccd10] BioRead(5) -> 5 [0.0]
- BioRead(0x28cd490, <buf>, 60) [0x28ccd10]
- [0x28ccd10] BioRead(60) -> 60 [0.0]
- BioCtrl(0x28cd490, 0x7, 0x0, 0x28cd190)
- BioCtrl(0x28cd490, 0xb, 0x0, 0x0)BIO_CTRL_FLUSH
- R0! CONNECTED
- TlsWatchProc(0x0)
- Calling flush
- nopchan::write GET
- BIO_write(0x28ccc10, 3)
- BioWrite(0x28cd490, <buf>, 28) [0x28ccd10]
- [0x28ccd10] BioWrite(28) -> 28 [0.0]
- BIO_write(0x28ccc10, 3) -> [3]
- Output(3) -> TlsWatchProc(0x0)
- .. possibly another clue, if it's not clear from the above, is that it's only the first attempt to flush on a newly-connected, async tls channel that fails. The following code which connects before making the socket async, succeeds:
- set sock [tls::socket google.com 443]
- puts -nonewline $sock G
- flush $sock ;# succeeds - socket is not async
- fconfigure $sock -blocking 0
- puts -nonewline $sock ET
- flush $sock ;# succeeds - socket is fully connected