;;; http-client-bench.lsp — sequential HTTP load generator in Scheme
;;;
;;; Makes N requests to 127.0.0.1:PORT and reports elapsed wall time
;;; + requests/sec. Uses only the six tcp-* primitives, so it runs
;;; identically in Python, C, and asm.
;;;
;;; The earlier tests/web-benchmark.sh used curl — each curl fork+exec
;;; costs ~2 ms, swamping actual server work. This client keeps every
;;; request in-process: that irreducible cost disappears, so the real
;;; server throughput shows up.
;;;
;;; Usage:
;;;   python3 lumbda.py --fast examples/http-client-bench.lsp
;;;   ./c/lumbda examples/http-client-bench.lsp
;;;   ./asm/lumbda < examples/http-client-bench.lsp
;;;
;;; Override N or PORT by pre-setting *n-requests* / *port* before load.

(define *host* "127.0.0.1")
(define *port* 8080)
(define *n-requests* 500)
(define *path* "/bench")

(define *request*
  (string-append
    "GET " *path* " HTTP/1.0\r\n"
    "Host: " *host* "\r\n"
    "Connection: close\r\n\r\n"))

;;; Pass snap as arg so heap-restore can rewind per-request
;;; allocations on asm without invalidating the client loop's
;;; closure env.

(define (one-request)
  (let ((sock (tcp-connect *host* *port*)))
    (if sock
        (begin
          (tcp-send sock *request*)
          (let ((resp (tcp-recv sock 8192)))
            (tcp-close sock)
            (if (and resp (> (string-length resp) 0)) 1 0)))
        0)))

(define (client-loop n ok snap)
  (if (= n 0)
      ok
      (let ((got (one-request)))
        (heap-restore snap)
        (client-loop (- n 1) (+ ok got) snap))))

(define t0 (current-time-ms))
(define ok (client-loop *n-requests* 0 (heap-snapshot)))
(define t1 (current-time-ms))

(define elapsed-ms (- t1 t0))
(define rps
  (if (> elapsed-ms 0)
      (quotient (* *n-requests* 1000) elapsed-ms)
      0))

(display "target   : http://") (display *host*) (display ":") (display *port*)
(display *path*) (newline)
(display "requests : ") (display *n-requests*) (newline)
(display "ok       : ") (display ok) (newline)
(display "elapsed  : ") (display elapsed-ms) (display " ms") (newline)
(display "rps      : ") (display rps) (newline)
