Prerequisites:
Ingredients: a fortune file (for instance this one or another one found on the Internet).
Summary
Open your browser at http://localhost/cgi-bin/quotes.cgi
. Observe the 404 error.
Find the cgi directory on your computer. On Linux, it’s usually /var/www/cgi-bin
. On Mac OS X, it’s usually /Library/WebServer/CGI-Executables
. Create there a file named quotes.cgi
, with contents:
#!/usr/bin/env ruby print "Content-Type: text/plain\r\n" print "\r\n" print "Hello, world!"
Open again reload http://localhost/cgi-bin/quotes.cgi
in the browser. Observe the 500 error. What is the reason? Find the error log. It’s usually at /var/log/apache2/error_log
. It’s not clear which lines were produced by our script, so we do
$ tail -f /var/log/apache2/error_log
type a few returns, and reload the browser. Now it’s clear
[Wed Nov 26 21:42:50 2008] [error] [client ::1] (13)Permission denied: exec of '/Library/WebServer/CGI-Executables/quotes.cgi' failed [Wed Nov 26 21:42:50 2008] [error] [client ::1] Premature end of script headers: quotes.cgi
Add the executability bit and reload the browser
$ chmod +x /Library/WebServer/CGI-Executables/quotes.cgi
Now we observe the “Hello, world!” message.
Open a second terminal window, and tail the access_log file. Reload the browser and observe the log message. Observe the success status 200.
Copy the fortune file to /tmp/quotes.txt
. Change quotes.cgi to
#!/usr/bin/env ruby print "Content-Type: text/plain\r\n" print "\r\n" print File.open("/tmp/quotes.txt").read
Reload the browser.
Now change quote.cgi to
#!/usr/bin/env ruby print "Content-Type: text/plain\r\n" print "\r\n" quotes = File.open("/tmp/quotes.txt").read.split("%") print quotes[33]
Reload the browser. Observe we see only one quote now.
Change the last lines to
quotes = File.open("/tmp/quotes.txt").read.split("%") print quotes[rand(quotes.size)]
Reload the browser repeatedly. Observe we see a different quote every time.
Rewrite quotes.cgi to
#!/usr/bin/env ruby print "Content-Type: text/html\r\n" print "\r\n" quotes = File.open("/tmp/quotes.txt").read.split("%") quote = quotes[rand(quotes.size)] print " <html> <head><title>Quote of the day</title></head> <body> <h3>Quote of the day</h3> <pre> #{quote} </p e> </body> </html> "
Reload the browser. Observe that the browser window title is changed as well.
We want to select all quotes that contain a given string. Point the browser to http://localhost/cgi-bin/quotes.cgi?q=Linus
. Observe no difference from before. Now change quotes.cgi to
#!/usr/bin/env ruby require 'cgi' print "Content-Type: text/html\r\n" print "\r\n" quotes = File.open("/tmp/quotes.txt").read.split("%") cgi = CGI.new keyword = cgi['q'] if keyword quote = keyword else quote = quotes[rand(quotes.size)] end ...
Reload the browser. Observe that the “q” parameter appears in place of the quote. Try to remove the parameter and see that the old behaviour is no longer working. Try replacing text/html
to text/plain
and observe that we get an empty quote. This means that the cgi object returns empty strings for missing parameters, and empty strings are not false. Correct by changing if keyword
to if keyword.length > 0
. Observe that the behaviour when the q
parameter is missing is now back to what it was before.
Now change the code to
quote = quotes.find {|quote| quote.include?(keyword) }
Reload the browser. Change the query string, and observe different quotes are retrieved. For any given value of q
, always the same quote is retrieved. Randomize by changing the code to
quote = quotes.select {|quote| quote.include?(keyword) }
Reload with q=Linus
and see an array of matching quotes.
Now change the code to
if keyword.length > 0 quotes = quotes.select {|quote| quote.include?(keyword) } end quote = quotes[rand(quotes.size)]
And see the selected quote change randomly.
Execute
$ ab -i -n 1000 -c 1 http://localhost/cgi-bin/quotes.cgi $ ab -i -n 1000 -c 10 http://localhost/cgi-bin/quotes.cgi $ ab -i -n 1000 -c 100 http://localhost/cgi-bin/quotes.cgi
And produce a table of the mean response time, and maximum response time in 90% of the cases, versus concurrency level.
Concurrency | Mean response time (ms) | Response time for 90% (ms) | Requests per second |
---|---|---|---|
1 | 6 | 6 | 160 |
10 | 38 | 58 | 263 |
100 | 372 | 398 | 268 |