View Ticket
2025-05-22
09:14 New ticket [48eddca89e] http:geturl https://localhost:8143/info hangs when server not running. artifact: 45d0b0632d user: anonymous

Ticket Hash: 48eddca89ea79b5cb6477d5f9f46cc890873cf92
Title: http:geturl https://localhost:8143/info hangs when server not running
Status: Open Type: Code Defect
Severity: Critical Priority:
Subsystem: Resolution:
Last Modified: 2025-05-22 09:14:23
1.7 days ago
Created: 2025-05-22 09:14:23
1.7 days ago
Version Found In: 1.8, commit ca1a846290, but affects older and newer commits as well
User Comments:
anonymous added on 2025-05-22 09:14:23:

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;
!               }
            }
        }