Subject: bug#70645: Reliable HTTPS networking

From: Ludovic Courtès <[email-masked]>
Date: Wed, 26 Feb 2025 16:15:05 +0100

Christopher Baines <[email-masked]> skribis:

> As described in the GnuTLS documentation on Asynchronous operation,
> GNUTLS_NONBLOCK should be passed to gnutls_init, and the Guile
> equivalent is passing connection-flag/nonblock to make-session.
>
> Additionally, error/again or error/interrupted should lead to a retry of
> the handshake, after waiting for the appropriate I/O on the port.  As
> record-get-direction is new in Guile-GnuTLS, specifically check if this
> is defined.
>
> * module/web/client.scm (tls-wrap): Call make-session with
> connection-flag/nonblock if the port is non-blocking, and handle waiting
> for I/O when performing the handshake.

[...]

> +  (let ((session
> +         (apply
> +          make-session
> +          (cons connection-end/client
> +                (if (zero? (logand O_NONBLOCK (fcntl port F_GETFL)))
> +                    '()
> +                    ;; If the port is non-blocking, tell GnuTLS
> +                    (list connection-flag/nonblock)))))

You can avoid ‘cons’ here:

  (apply make-session connection-end/client
         (if … '() (list …)))

> +          (cond ((and
> +                  (or (eq? err error/again)
> +                      (eq? err error/interrupted))
> +                  (module-defined? (resolve-interface '(gnutls))
> +                                   'record-get-direction)) ; Guile-GnuTLS >= 4.0.0
> +                 (if (= 0 (record-get-direction session))
> +                     ((current-read-waiter) port)
> +                     ((current-write-waiter) port))

Should EINTR (error/interrupted) really be treated in the same way as
EAGAIN?  That looks fishy.

Also, this only addresses handshake, but what about ‘error/again’ raise
while transferring data over the session record port?

Thanks,
Ludo’ “better late than never”.