;;-*-mode:lisp-*- ;; ;; name: http-stress.scm ;; purpose: simple random referencing of a list of url's as ;; fast as we can from a set of forked() processes. ;; Author: George Carrette GJC@DELPHI.COM ;; Usage: (get-random-url-test n-cycles) for a single process test. ;; (fork-random-url-test n-forks n-cycles) ;; (http-get-test url) ;; (http-set-basic-authorization username password) ;; (http-put-test url content-type content) ;; (main-fork-random-url-test n-forks n-cycles) ;; (continuos-loop-test time-limit) ;; ;; Example: ;; siod -v5 http-stress.scm "(get-random-url-test 10)" ;; ;; $Id: http-stress.scm,v 1.4 1998/02/10 15:15:11 gjc Exp $ ;; ;; Revision History ;; 16-MAY-95. GJC Initial Creation, rework of previous hacks, adding forking. ;; 17-MAY-95. GJC Add headers for user-agent,authorization,accept. ;; 18-MAY-95. GJC generalize to do POST operations and handle content. ;; ;; ;; Problems: if a file is being loaded with the fork calls in them ;; the the input stream on the file gets random data in it. ;; usually resulting in unbound variable error. ;; The 'work-around' for this is to put a (quit) in the test ;; script file. The problem goes away if the wait call is ;; avoided. ;; Note: Need to run this with siod compiled with profiling ;; so we can perhaps see what routines are taking most of the ;; cpu time. Is it s-drain? If we had a profile-all subrs mode ;; in siod that would be cute. (define (srandom-now) (srandom (fmod (* 1.0e4 (realtime)) 1.0e5))) (define *http-server-host* "www.foobar.com") (define *http-server-port* 80) (define *http-user-agent* "DELPHI-http-stress/1.0") (define *http-user-authorization* nil) (define *http-accept* "*/*, image/gif, image/jpeg") (define *http-extra-headers* nil) (define *http-sleep* 0) (define *http-host-header* 't) (define *http-url-list* '("/" "/foo/" "/bar/")) (define *main-url-verify* nil) (define (http-set-basic-authorization username password) (cond ((or (not username) (not password)) (set! *http-user-authorization* nil)) ('else (set! *http-user-authorization* (string-append "Basic " (base64encode (string-append username ":" password))))))) (define (http-get-test url) (http-test url "GET" nil nil)) (define (http-put-test url content-type content) (http-test url "POST" content-type content)) (define (http-open url operation content-type content) (let ((s (s-open *http-server-host* *http-server-port*)) (response-headers nil) (line nil)) (s-puts (string-append operation " " url " HTTP/1.0\r\n") s) (cond ((not *http-host-header*)) ((string? *http-host-header*) (s-puts "Host: " s) (s-puts *http-host-header* s) (s-puts "\r\n" s)) ('else (s-puts "Host: " s) (s-puts *http-server-host* s) (cond ((not (= 80 *http-server-port*)) (s-puts ":" s) (s-puts (number->string *http-server-port*) s))) (s-puts "\r\n" s))) (if *http-accept* (s-puts (string-append "Accept: " *http-accept* "\r\n") s)) (if *http-user-agent* (s-puts (string-append "User-Agent: " *http-user-agent* "\r\n") s)) (if *http-user-authorization* (s-puts (string-append "Authorization: " *http-user-authorization* "\r\n") s)) (mapcar (lambda (h) (s-puts (car h) s) (s-puts ": " s) (s-puts (cadr h) s) (s-puts "\r\n" s)) *http-extra-headers*) (if content-type (s-puts (string-append "Content-type: " content-type "\r\n") s)) (if content (s-puts (string-append "Content-length: " (number->string (string-length content)) "\r\n") s)) (s-puts "\r\n" s) (if content (s-puts content s)) (s-force-output s) (if (> *http-sleep* 0) (sleep *http-sleep*)) (while (and (set! line (s-gets s)) (not (or (equal? line "\r\n") (equal? line "\n")))) (set! response-headers (cons (string-trim line) response-headers))) (cons s (nreverse response-headers)))) (define (http-test url operation content-type content) (let ((l (http-open url operation content-type content)) (s nil)) (set! s (car l)) (if (> (verbose) 6) (let ((c nil)) (while (set! c (s-getc s)) (putc c))) (s-drain s)) (s-close s) (cdr l))) (define (http url operation content-type content) (let ((l (http-open url operation content-type content)) (s nil) (line nil) (result nil)) (set! s (car l)) (while (set! line (s-gets s)) (set! result (cons line result))) (s-close s) (nconc (cdr l) (cons nil (nreverse result))))) (define (get-random-url) (http-get-test (nth (random (length *http-url-list*)) *http-url-list*))) (define (get-random-url-test n) (srandom-now) (let ((j 0)) (while (< j n) (get-random-url n) (set! j (+ 1 j))))) (define (fork-random-url-test n-forks n-cycles) (let ((children nil) (j 0) (pid nil) (info nil)) (while (< j n-forks) (if (set! pid (fork)) (begin (if (> (verbose) 1) (writes nil "PARENT: Started " pid "\n")) (set! children (cons pid children))) (begin (if (> (verbose) 1) (writes nil "CHILD: " (getpid) "\n")) (set! j n-forks))) (set! j (+ j 1))) (if pid (begin (writes nil "PARENT: waiting on " (length children) " children.\n") ;; this will run until it gets an error. (while children (set! info (wait-for-child)) (set! children (subset (lambda (c) (not (eqv? c (car info)))) children)) (writes nil "PARENT: wait returned [" (car info) "," (cadr info) "] " (length children) " left\n"))) (begin (if (> (verbose) 1) (writes nil "CHILD: " (getpid) " random fetch " n-cycles " times through " (length *http-url-list*) " possible urls.\n")) (get-random-url-test n-cycles) (if (> (verbose) 1) (writes nil "CHILD: " (getpid) " exiting.\n")) (exit 0))))) (define (wait-for-child) (let ((result nil)) (while (not (set! result (wait nil '(WNOHANG)))) (if (> (verbose) 2) (begin (writes nil ".") (fflush))) (sleep 1)) result)) (define (url-encode-form-data . l) (define (loop x) (if (not x) nil (if (not (cdr x)) (error "odd number of arguments") (let ((rest (loop (cddr x)))) (cons (car x) (cons "=" (cons (if (number? (cadr x)) (number->string (cadr x)) (url-encode (cadr x))) (if rest (cons "&" rest))))))))) (apply string-append (loop l))) (if (> (verbose) 4) (trace http-get-test)) (define (get-total-runtime) (apply + (mapcar (lambda (x) (let ((r (current-resource-usage x))) (apply + (mapcar (lambda (y) (cdr (assq y r))) '(utime stime))))) '(SELF CHILDREN)))) (define (main-fork-random-url-test n-forks n-hits-per-fork) (if *main-url-verify* (begin (mapcar (lambda (sym) (writes nil sym " = " (eval sym) "\n")) '(*http-server-host* *http-server-port* *http-user-agent* *http-user-authorization* *http-accept*)) (writes nil "Verification of url access:\n") (mapcar (lambda (x) (writes nil x "\n") (mapcar (lambda (line) (writes nil " " line "\n")) (http-get-test x))) *http-url-list*) (writes nil "********************************\n"))) (let ((before-real nil) (before-run nil) (after-real nil) (after-run nil) (test-real nil) (test-run nil)) (set! before-real (realtime)) (set! before-run (get-total-runtime)) (fork-random-url-test n-forks n-hits-per-fork) (set! after-real (realtime)) (set! after-run (get-total-runtime)) (set! test-real (- after-real before-real)) (set! test-run (- after-run before-run)) (writes nil "********************************\n" "Test start: " (unix-ctime before-real) "\n" "Test ended: " (unix-ctime after-real) "\n" "test took " test-real " seconds realtime, " test-run " cpu seconds. (" (* 100 (/ test-run test-real)) "%)\n" (/ test-real (* n-forks n-hits-per-fork)) " realtime seconds per hit.\n" (* n-forks n-hits-per-fork) " total hits. " (/ (* n-forks n-hits-per-fork) test-real) " hits per second.\n"))) (define (continuos-loop-test time-limit) (let ((grand-start-time (realtime)) (before-real nil) (before-run nil) (after-real nil) (after-run nil) (test-real nil) (test-run nil) (l nil)) (while (or (not time-limit) (< (realtime) (+ time-limit grand-start-time))) (set! before-real (realtime)) (set! before-run (get-total-runtime)) (mapcar http-get-test *http-url-list*) (set! after-real (realtime)) (set! after-run (get-total-runtime)) (set! test-real (- after-real before-real)) (set! test-run (- after-run before-run)) (writes nil "[" (substring (unix-ctime before-real) 11 19) "," (substring (unix-ctime after-real) 11 19) "] " (trunc (/ (length *http-url-list*) test-real)) " hits per second. " (trunc (/ (* 100 test-run) test-real)) "% cpu.\n"))))