M bulletin.rkt => bulletin.rkt +24 -9
@@ 38,7 38,7 @@
(define transitions
(hasheq 'committing 'voting
'voting 'summing
- 'summing 'closed))
+ 'summing 'done))
(define (transition [trans #f])
(set! state (if trans trans (hash-ref transitions state))))
@@ 170,15 170,22 @@
#:code 403)))
(define (sums request)
- (if (eq? (length (hash-keys _sums))
- (hash-count _peers))
- (response/jsexpr
- _sums)
- (response/jsexpr
- "The sums are not yet available"
- #:code 403)))
+ (if (eq? state 'summing)
+ (if (eq? (length (hash-keys _sums))
+ (hash-count _peers))
+ (begin
+ (transition)
+ (response/jsexpr _sums))
+ (response/jsexpr
+ "The sums are not yet available"
+ #:code 403))
+ (if (eq? state 'done)
+ (response/jsexpr _sums)
+ (response/jsexpr
+ "Too early to request sums"
+ #:code 403))))
- (define-values (dispatcher url-generator)
+ (define-values (dispatch-route url-generator)
(dispatch-rules
[("candidates") #:method "get" candidates]
[("register") #:method "post" register ]
@@ 194,6 201,14 @@
(url? any/c . -> . can-be-response?)
(response/full 400 #"Bad Request" (current-seconds) #f empty empty))
+ (define/contract (dispatcher req)
+ (request? . -> . any)
+ (if (eq? state 'closed)
+ (response/jsexpr
+ "Election closed / cancelled"
+ #:code 410)
+ (dispatch-route req)))
+
(serve/servlet
dispatcher
#:port 1984
M client.rkt => client.rkt +34 -9
@@ 53,7 53,12 @@
(let loop ([wait 0])
(sleep wait)
(with-handlers ([exn:fail:network:http:error?
- (λ (ex)
+ (λ ([ex : exn:fail:network:http:error])
+ ; 410 indicates the election is closed/cancalled
+ (when (eqv? (exn:fail:network:http:error-code ex)
+ 410)
+ (newline)
+ (raise ex))
(break-enabled #t)
(display ".")
(flush-output)
@@ 64,11 69,14 @@
[point : Integer]
[bulletin : requester]
[token : JSExpr])
- (displayln "retrieving candidates...")
+ (newline)
+
+ (display "retrieving candidates...")
(define candidates (cast (json-response-body
(retry-request (get bulletin "/candidates")))
(Listof String)))
(define cand-count (cast (length candidates) Positive-Integer))
+ (newline)
(display "waiting for voting to commence...")
(define peer-count (cast
@@ 291,12 299,26 @@
(for ([r rank])
(displayln (format "~a) ~a" i (list-ref candidates r))))))
+(define (wait-for-open-election [bulletin : requester])
+ (let retry : Any ([wait 0])
+ (sleep wait)
+ (with-handlers
+ ([exn:fail:network:http:error?
+ (λ ([ex : exn:fail:network:http:error])
+ (break-enabled #t)
+ (when (eqv? 410 (exn:fail:network:http:error-code ex))
+ (retry 3)))])
+ (get bulletin "/candidates"))))
+
(module+ main
(define point (gen))
(define bulletin
(update-port (update-host json-requester "localhost") 1984))
+ (displayln "waiting for the election to open...")
+ (wait-for-open-election bulletin)
+
(define token
(let ([username (readline "Username: ")]
[password (get-pass "Password: ")])
@@ 315,12 337,15 @@
; the bulletin can have multiple elections
(let loop ()
- (with-handlers ([exn:fail?
- (λ ([ex : exn:fail])
- (displayln (exn-message ex))
- (displayln
- "Assuming election was cancelled; starting over")
- (break-enabled #t)
- (loop))])
+ (with-handlers
+ ([exn:fail:network:http:error?
+ (λ ([ex : exn:fail:network:http:error])
+ (break-enabled #t)
+ (if (eqv? 410 (exn:fail:network:http:error-code ex))
+ (begin
+ (displayln
+ "Election was closed; awaiting for it to reopen...")
+ (wait-for-open-election bulletin))
+ (raise ex)))])
(vote-in-election point bulletin token))
(loop)))