#lang racket
(require openssl/mzssl)

(define pem (build-path (collection-path "openssl")
			"test.pem"))

(define errs? #f)
(define (test expect f . args)
  (printf "~s ~s => " f args)
  (define v (apply f args))
  (printf "~s\n" v)
  (unless (equal? expect v)
    (printf "BUT EXPECTED ~s\n" expect)
    (set! errs? #t)))

(define-syntax-rule (err/rt-test e exn?)
  (test 'ok values (with-handlers ([exn? (lambda (x) 'ok)])
                     e)))

(define (test-ssl limit buffer? close?)
  ;; Test SSL communication using a limited pipe.
  ;; (Using a pipe limited to a small buffer helps make sure
  ;; that race conditions and deadlocks are eliminated in the
  ;; implementation.)
  (let-values ([(r1 w2) (make-pipe limit)]
	       [(r2 w1) (make-pipe limit)])
    (define t1
      (thread (lambda ()
		(let-values ([(r w) (ports->ssl-ports
				     r1 w1
				     #:mode 'connect 
				     #:close-original? close? 
				     #:shutdown-on-close? #t)])
		  (unless buffer?
		    (file-stream-buffer-mode w 'none))
		  (test 5 write-bytes #"abcde" w)
		  (when buffer?
		    (flush-output w))
		  (test "hello" read-string 5 r)
		  (test eof read-string 5 r)
                  (close-input-port r)
                  (close-output-port w)))))
    (define t2
      (thread (lambda ()
		(define ctx (ssl-make-server-context))
		(ssl-load-certificate-chain! ctx pem)
		(ssl-load-private-key! ctx pem)
		(let-values ([(r w) (ports->ssl-ports
				     r2 w2
				     #:context ctx
				     #:mode 'accept 
				     #:close-original? close? 
				     #:shutdown-on-close? #t)])
		  (test #"abcde" read-bytes 5 r)
		  (test 5 write-string "hello" w)
		  (close-output-port w)))))
    (thread-wait t1)
    (thread-wait t2)
    ;; Check that ports were closed or not:
    (if close?
	(begin
	  (err/rt-test (read-byte r1) exn:fail?)
	  (err/rt-test (write-byte 0 w1) exn:fail?))
	(let ([v (random 256)]
	      [v2 (random 256)])
	  (test (void) write-byte v w2)
	  (test v read-byte r1)
	  (test (void) write-byte v2 w1)
	  (test v2 read-byte r2)))
    (void)))

(test-ssl 1 #t #t)
(test-ssl 10 #t #t)
(test-ssl 100 #t #t)
(test-ssl 1 #f #t)
(test-ssl 10 #f #t)
(test-ssl 100 #f #t)

(test-ssl 100 #t #f)
(test-ssl 100 #f #f)

(newline)
(when errs?
  (error "There were test failures"))
(printf "All tests passed.\n")


(module+ test
  (module config info
    (define random? #t)))
