Re: Detecting broken inbound TCP connections

Jeff Henrikson <[email protected]> Fri, 1 Jul 2022 15:51:55 -0700
Newsgroups gmane.comp.lang.racket.user,gmane.lisp.scheme.plt
Message-ID <[email protected]>
Hi Tony,

Thanks for offering your interpretation of the racket reference manual.=C2=
=A0=20
Below is my attempt to test whether or not the input port does indeed=20
become closed when the TCP connection is broken.

The broken TCP connection is provided by combining curl with GNU=20
coreutils timeout.

I try testing the input port for closedness two different ways. The=20
default code tests with port-closed-evt. The commented out code tests by=20
calling port-closed? in a loop.=C2=A0 Neither method succeeds in detecting=
=20
the broken TCP connection.

Thanks for any help.

Regards,


Jeff Henrikson


#lang racket

;; Tony Garnock-Jones,=20
https://groups.google.com/g/racket-users/c/H43jr8QuM-4
;;=C2=A0=C2=A0 "... if the connection breaks, the ports will be closed as u=
sual. .=20
. ."

(define (handle-by-event in out k)
 =C2=A0 ; Discard the HTTP request header (up to blank line):
 =C2=A0 (regexp-match #rx"(\r\n|^)\r\n" in)
 =C2=A0 (displayln "about to sleep")
 =C2=A0 (flush-output (current-output-port))
 =C2=A0 (let ((evt (sync/timeout 10 (port-closed-evt in))))
 =C2=A0=C2=A0=C2=A0 (if evt
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (begin
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (displayln "got close evt\n")
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (k))
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (begin
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (displayln "done sleeping")
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (flush-output (current-output-p=
ort))
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (display "HTTP/1.0 200 Okay\r\n=
" out)
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (display "Server: k\r\nContent-=
Type: text/html\r\n\r\n" out)
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (display "<html><body>Hello, wo=
rld!</body></html>" out)
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (k)))))

;; same as handle-by-event but using port-closed? instead of port-closed-ev=
t
(define (handle-by-polling in out k)
 =C2=A0 (define (iter i)
 =C2=A0=C2=A0=C2=A0 (when (> i 0)
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (printf "closed=3D~a\n" (port-c=
losed? in))
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (sleep 1)
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 (iter (- i 1))))
 =C2=A0 ; Discard the request header (up to blank line):
 =C2=A0 (regexp-match #rx"(\r\n|^)\r\n" in)
 =C2=A0 (displayln "about to sleep")
 =C2=A0 (flush-output (current-output-port))
 =C2=A0 (iter 10)
 =C2=A0 (displayln "done sleeping")
 =C2=A0 (flush-output (current-output-port))
 =C2=A0 (display "HTTP/1.0 200 Okay\r\n" out)
 =C2=A0 (display "Server: k\r\nContent-Type: text/html\r\n\r\n" out)
 =C2=A0 (display "<html><body>Hello, world!</body></html>" out)
 =C2=A0 (k))


(define (accept-and-handle listener)
 =C2=A0 (define-values (in out) (tcp-accept listener))
 =C2=A0 ;; alternatively: (handle-by-polling in out (lambda ()
 =C2=A0 (handle-by-event in out (lambda ()
 =C2=A0=C2=A0=C2=A0 (displayln "handle finished")
 =C2=A0 ))
 =C2=A0 (close-input-port in)
 =C2=A0 (close-output-port out))

(define (serve port-no)
 =C2=A0 (define listener (tcp-listen port-no 5 #t))
 =C2=A0 (define (loop)
 =C2=A0=C2=A0=C2=A0 (accept-and-handle listener)
 =C2=A0=C2=A0=C2=A0 (loop))
 =C2=A0 (loop))

(serve 8000)

#|
 =C2=A0=C2=A0=C2=A0 Test with curl and GNU coreutils timeout:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 timeout --foreground 3s curl ht=
tp://127.0.0.1:8000
 =C2=A0=C2=A0=C2=A0 actual output (handle-by-event version):
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 about to sleep
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 done sleeping
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 handle finished
 =C2=A0=C2=A0=C2=A0 expected output=C2=A0 (handle-by-event version):
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 about to sleep
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 got close evt
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 handle finished

 =C2=A0=C2=A0=C2=A0 Tested on:
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 Racket v8.0 cs
 =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 on Ubuntu 20.04.4 LTS
|#


> Hi Jeff,
>
> You can use `tcp-listen` and `tcp-accept` [0] to accept connections.=20
> Once accepted, the connection appears as a matched pair of ports, one=20
> for input and one for output, and if the connection breaks, the ports=20
> will be closed as usual. In some circumstances, you will get an=20
> exception such as "connection reset" (see `exn:fail:network`). If you=20
> need to reliably distinguish closed-by-peer, all-ok from=20
> closed-by-weird-network-weather, your application protocol has to=20
> handle that, there's nothing TCP can do for you there. HTTP does this=20
> via content-length and/or chunking, for example.
>
> Cheers,
> =C2=A0 Tony
>
> [0]: https://docs.racket-lang.org/reference/tcp.html
>
> On 6/30/22 11:34 AM, Jeff Henrikson wrote:
>> Racket users,
>>
>> How do I accept an inbound TCP connection so that once I am=20
>> processing it, if the connection is broken by the client or network,=20
>> my program promptly finds out?
>>
>> Regards,
>>
>>
>> Jeff Henrikson
>>

--=20
You received this message because you are subscribed to the Google Groups "=
Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to racket-users+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/[email protected]
To view this discussion on the web visit https://groups.google.com/d/msgid/=
racket-users/e3445bfa-8d75-bbf5-1900-226a67e71599%40gmail.com.