this stumped me for quite a minute. this procedure sometimes needs to halt execution to get input, like a server requesting input, or the client asking for permission to follow cross site redirects. the problem is if the get procedure is thought of as being part of the net interface, and compartmentalized from the program loop, actually doing that would require heavy use of continuations to go back and forth across the boundary i -do- think that the way i ultimately want to go in the end is using continuations to halt execution, catch it in the user interface to get input, and then continue. however, its a lot simpler and more immediate to change where i'm drawing the line in the separation of concerns. the continuations-based approach is enough of a diversion that i haven't managed to get anything done for the last couple of hours.
49 lines
1.4 KiB
Racket
49 lines
1.4 KiB
Racket
#lang racket
|
|
|
|
(provide request)
|
|
|
|
(require openssl)
|
|
(require net/url-string)
|
|
|
|
;; sends a request to a gemini server, and returns the status, header,
|
|
;; and the input port for the rest of the body.
|
|
;; this procedure will fail if the response is malformed, however, it
|
|
;; is not up to it to validate the contents of the response.
|
|
(define (request url-str)
|
|
(define url (string->url url-str))
|
|
(define-values (c-in c-out)
|
|
(ssl-connect (url-host url)
|
|
(or (url-port url) 1965)))
|
|
|
|
(write-string url-str c-out)
|
|
(write-string "\r\n" c-out)
|
|
|
|
(define-values (status header)
|
|
(read-response c-in))
|
|
|
|
(values status header c-in))
|
|
|
|
(define (read-response (c-in (current-input-port)))
|
|
(define maxlen 1027)
|
|
|
|
(let ([header (peek-string maxlen 0 c-in)])
|
|
|
|
(if (not (string-contains? header "\r\n"))
|
|
(error "header exceeds maximum length")
|
|
|
|
(let ([header (read-line c-in 'return-linefeed)])
|
|
(define-values (status meta)
|
|
(let ([status-meta (string-split header " ")])
|
|
(values (car status-meta)
|
|
(string-join (cdr status-meta)))))
|
|
|
|
(cond
|
|
[(> (string-length status) 2)
|
|
(error "status code exceeds maximum length")]
|
|
|
|
[(andmap (compose not char-numeric?) (string->list status))
|
|
(error "status code is not numeric")]
|
|
|
|
[else
|
|
(values (string->number status) meta)])))))
|