Artifacts Associated With Ticket 48eddca89e
Ticket change [45d0b0632d] (rid 3981) by anonymous on 2025-05-22 09:14:23:
- foundin initialized to:
1.8, commit ca1a846290, but affects older and newer commits as well
- icomment:
There is a subtle bug when trying to connect to a server not running via http::geturl using tcltls. tcltls waits for the connection and loops endlessly not giving the Tcl event loop the chance to handle timer events for a timeout. Example code to reproduce: ``` package require http; package require tls; set tok [http::geturl https://localhost:8143/info -timeout 3000] # use port with no service listening here! http::status $tok; ``` The problem is fixed with the following patch. Another way to correct the issue might be to change the Tcl http package to set the socket to async even when waiting for the initial connection but this seems to be a bigger effort. I consider and endless loop in a Tcl command as problematic so I decided to avoid it. Keep in mind that tcltls properly aborts the loop for async sockets during the connection phase. ``` diff -rcN vanilla/tcltls-ca1a846290/generic/tlsIO.c tcltls-1.8-ca1a846290/generic/tlsIO.c *** vanilla/tcltls-ca1a846290/generic/tlsIO.c Thu Jan 2 19:05:36 2025 --- tcltls-1.8-ca1a846290/generic/tlsIO.c Thu May 22 10:46:27 2025 *************** *** 222,235 **** dprintf("bioShouldRetry = %d", bioShouldRetry); if (err <= 0) { ! if (rc == SSL_ERROR_WANT_CONNECT || rc == SSL_ERROR_WANT_ACCEPT) { ! bioShouldRetry = 1; ! } else if (rc == SSL_ERROR_WANT_READ) { ! bioShouldRetry = 1; ! statePtr->want |= TCL_READABLE; ! } else if (rc == SSL_ERROR_WANT_WRITE) { ! bioShouldRetry = 1; ! statePtr->want |= TCL_WRITABLE; } } --- 222,250 ---- dprintf("bioShouldRetry = %d", bioShouldRetry); if (err <= 0) { ! /* ! * Problem here when socket is not async and still not ! * connected, avoid to loop endlessly. This happens to be the case ! * the the destination IP is reachable but no service responds on ! * the requested port. Tcl http does not put the socket in async ! * mode until connected so we need to allow the Tcl event loop to ! * catch the timeoout and sits on a vwait for the http token. ! * Test case which yields hang in endless loop here without this patch is: ! * package require http; ! * package require tls; ! * set tok [http::geturl https://localhost:8143/info -timeout 3000] ! * http::status $tok; ! */ ! if(statePtr->flags & TLS_TCL_ASYNC) { ! if (rc == SSL_ERROR_WANT_CONNECT || rc == SSL_ERROR_WANT_ACCEPT) { ! bioShouldRetry = 1; ! } else if (rc == SSL_ERROR_WANT_READ) { ! bioShouldRetry = 1; ! statePtr->want |= TCL_READABLE; ! } else if (rc == SSL_ERROR_WANT_WRITE) { ! bioShouldRetry = 1; ! statePtr->want |= TCL_WRITABLE; ! } } } ``` - login: "anonymous"
- mimetype: "text/x-markdown"
- severity initialized to: "Critical"
- status initialized to: "Open"
- title initialized to:
http:geturl https://localhost:8143/info hangs when server not running
- type initialized to: "Code Defect"
- foundin initialized to:
Ticket change [8ed6d326bf] (rid 4102) by bohagan on 2025-10-11 21:49:34:
- icomment:
fixed in [b342c5f3023b3344] and [10199abaf9e00249].
- login: "bohagan"
- mimetype: "text/x-fossil-plain"
- priority changed to: "Immediate"
- resolution changed to: "Open"
- icomment:
Ticket change [e26df122f7] (rid 4111) by bohagan on 2025-10-11 22:05:29:
- login: "bohagan"
- mimetype: "text/x-markdown"
- resolution changed to: "Fixed"
Ticket change [12b29daa4d] (rid 4245) by bohagan on 2026-01-22 02:37:29:
- icomment: "No re-occurrence of the issue has been reported."
- login: "bohagan"
- mimetype: "text/x-markdown"
- status changed to: "Closed"
Ticket change [7732bc8568] (rid 4258) by anonymous on 2026-02-04 09:06:13:
- foundin changed to:
2.0+, 1.8, commit ca1a846290, but affects older and newer commits as well
- icomment:
The problem still occurs with V2.0 (tcltls-20260122101940-ba2ee7744c). The delivered patch has not bee integrated into the source. Here is an updated version of the patch (starting around tlsIO.c:230). ``` if (ret <= 0) { /* * Problem here when socket is not async and still not * connected, avoid to loop endlessly. This happens to be the case * the the destination IP is reachable but no service responds on * the requested port. Tcl http does not put the socket in async * mode until connected so we need to allow the Tcl event loop to * catch the timeout and sit on a vwait for the http token. * Test case which yields hang in endless loop here without this patch is: * package require http; * package require tls; * ::http::register https 443 [list ::tls::socket -autoservername true] * set tok [http::geturl https://localhost:8143/info -timeout 3000] * http::status $tok; */ if(statePtr->flags & TLS_TCL_ASYNC) { if (rc == SSL_ERROR_WANT_CONNECT || rc == SSL_ERROR_WANT_ACCEPT) { bioShouldRetry = 1; } else if (rc == SSL_ERROR_WANT_READ) { bioShouldRetry = 1; statePtr->want |= TCL_READABLE; } else if (rc == SSL_ERROR_WANT_WRITE) { bioShouldRetry = 1; statePtr->want |= TCL_WRITABLE; } } } ``` This fixes the described problem. Please integrate the patch in to 2.0+. - login: "anonymous"
- mimetype: "text/x-markdown"
- resolution changed to: "Open"
- status changed to: "Open"
- foundin changed to:
Ticket change [735b6ddee6] (rid 4262) by bohagan on 2026-02-06 03:21:45:
- icomment:
The issue was fixed as noted in the commits listed previously, just not exactly as you suggested. See lines 230-253 in tlsIO.c: if (ret <= 0) { if (rc == SSL_ERROR_WANT_CONNECT || rc == SSL_ERROR_WANT_ACCEPT) { bioShouldRetry = 1; } else if (rc == SSL_ERROR_WANT_READ) { bioShouldRetry = 1; statePtr->want |= TCL_READABLE; } else if (rc == SSL_ERROR_WANT_WRITE) { bioShouldRetry = 1; statePtr->want |= TCL_WRITABLE; } } if (bioShouldRetry) { dprintf("The I/O did not complete -- but we should try it again"); if (statePtr->flags & TLS_TCL_ASYNC) { dprintf("Returning EAGAIN so that it can be retried later"); *errorCodePtr = EAGAIN; return 0; } else { dprintf("Doing so now"); continue; } } ------------------- I just tested it with the below script on both Windows and Linux and it worked on both. See below: ------------------- Script package require Tcl package require http package require tls ::http::register https 443 ::tls::socket set token [::http::geturl https://localhost:8143/info -timeout 3000]; # use port with no service listening here! http::status $token ------------------- OpenSUSE 16.0 Brian@Blizzard:~> /usr/bin/tclsh % package require Tcl 8.6.17 % package require http 2.9.8 % package require tls 2.0 % ::http::register https 443 ::tls::socket 443 ::tls::socket % set token [::http::geturl https://localhost:8143/info -timeout 3000]; # use port with no service listening here! connect failed connection refused % http::status $token can't read "token": no such variable % exit Brian@Blizzard:~> /opt/tcl9/bin/tclsh9.0 % package require Tcl 9.0.3 % package require http 2.10.1 % package require tls 2.0 % ::http::register https 443 ::tls::socket 443 ::tls::socket {} 0 0 % set token [::http::geturl https://localhost:8143/info -timeout 3000]; # use port with no service listening here! connect failed: connection refused % http::status $token can't read "token": no such variable % ------------------- Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\Users\Brian>c:\TCL\bin\tclsh % package require Tcl 8.6.14 % package require http 2.9.8 % package require tls 2.0 % ::http::register https 443 ::tls::socket 443 ::tls::socket % set token [::http::geturl https://localhost:8143/info -timeout 3000]; # use port with no service listening here! connect failed connection refused % http::status $token can't read "token": no such variable % exit C:\Users\Brian>c:\TCL9\bin\tclsh % package require Tcl 9.0.3 % package require http 2.10.1 % package require tls 2.0 % ::http::register https 443 ::tls::socket 443 ::tls::socket {} 0 0 % set token [::http::geturl https://localhost:8143/info -timeout 3000]; # use port with no service listening here! connect failed: connection refused % http::status $token can't read "token": no such variable % - login: "bohagan"
- mimetype: "text/plain"
- icomment: