Post by Jean LouisThank you. Sure, I am sending headers when necessary. If not
necessary, in case of direct call to CGI, I like to send the redirect.
I am waiting on either QUERY_STRING for GET parameters or
*standard-input* for parameters from POST.
However, if there is no QUERY_STRING and no standard input sent, then
I get in trouble.
wget -O /dev/shm/test "http://localhost/test.cgi"; less /dev/shm/test
--2017-03-28 17:21:18-- http://localhost/test.cgi
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified
Saving to: ‘/dev/shm/test’
/dev/shm/test [ <=> ] 0 --.-KB/s in 0s
2017-03-28 17:21:18 (0.00 B/s) - ‘/dev/shm/test’ saved [0]
and that is "almost" wanted. Yet the execution does not arrive to the
redirect. So the page does not get redirected. Why?
#!/usr/local/bin/clisp -q -ansi -norc -E utf-8
(setf custom:*ansi* t)
(defun main nil
(let ((url "http://localhost")
(query (or (getenv "QUERY_STRING")
(alexandria.0.dev:read-stream-content-into-string *standard-input*))))
(format t "Status: 302 Found~%Location: ~a~%~%" url)
(finish-output)
(exit 0)))
(main)
If I then save it as memory image, exactly this one here below, and
then do following, I get redirect: wget -O /dev/shm/test
"http://localhost/test.cgi?a";
*** - Ctrl-C: User break
Break 1 [1]>
I would like to understand what is happening.
There is some confusion between the type of delivery you want, and in the case of CGI, some technical difficulty in writing a file able to do both.
When you write a file starting with “#!” it’s to be considered as a script by the unix system, that is, a file that will be processed (interpreted) by the command given after “#!”.
Read: http://www.clisp.org/impnotes/quickstart.html#quickstart-unix <http://www.clisp.org/impnotes/quickstart.html#quickstart-unix>
Post by Jean Louis#!/usr/local/bin/clisp -q -ansi -norc -E utf-8
works. Notice that -ansi is equivalent to (setf custom:*ansi* t) so as a script, you should not need this line. (However if you consider loading the file in a clisp image, then the shebang line will be ignored (there’s a clisp specific dispatching reader macro on #\# #\! to treat this line as a comment).
If you save the above file in test.cgi and chmod +x it, it won’t work, because alexandria won’t be loaded by the script.
You could load ~/quicklisp/setup.lisp and then use quicklisp to load alexandria:
#!/usr/local/bin/clisp -q -ansi -norc -E utf-8
;; -*- mode:lisp;coding:utf-8 -*-
(in-package "COMMON-LISP-USER")
(load #P"~/quicklisp/setup.lisp")
(ql:quickload :alexandria)
(defun main (&optional (arguments *args*))
(declare (ignore arguments))
(let ((url "http://localhost")
(query (or (getenv "QUERY_STRING")
(alexandria:read-stream-content-into-string *standard-input*))))
(format t "Status: 302 Found~%Location: ~a~%~%" url)
(finish-output)
(exit 0)))
(main *args*)
but this won’t work either, in the case of a CGI, because quickload is quite verbose, and will print a lot of messages before your CGI script starts executing. You can see it by running the script directly in the shell (this is the big advantage of CGI, that you can run and test them in the shell, without going thru HTTP).
You could use a macro such as without-output or redirecting-stdout-to-stderr to prevent quicklisp messages:
(defmacro redirecting-stdout-to-stderr (&body body)
(let ((verror (gensym))
(voutput (gensym)))
`(let* ((,verror nil)
(,voutput (with-output-to-string (stream)
(let ((*standard-output* stream)
(*error-output* stream)
(*trace-output* stream))
(handler-case (progn ,@body)
(error (err) (setf ,verror err)))))))
(when ,verror
(terpri *error-output*)
(princ ,voutput *error-output*)
(terpri *error-output*)
(princ ,verror *error-output*)
(terpri *error-output*)
(terpri *error-output*)
#-testing-script (exit ex-software)))))
(defun getpid ()
(or (ignore-errors (find-symbol "getpid" "LINUX"))
(ignore-errors (find-symbol "PROCESS-ID" "OS"))
(ignore-errors (find-symbol "PROCESS-ID" "SYSTEM"))))
(defun report-the-error (err string-stream)
(let ((log-path (format nil "/tmp/~A.~D.errors" *program-name*
(let ((getpid (getpid)))
(if getpid
(funcall getpid)
"nopid")))))
(with-open-file (log-stream log-path
:direction :output
:if-exists :supersede
:if-does-not-exist :create)
(format log-stream "~A GOT AN ERROR: ~A~%~80,,,'-<~>~%~A~%"
*program-name* err (get-output-stream-string string-stream)))
(format *error-output* "~A: ~A~% See ~A~%" *program-name* err log-path)
(finish-output *error-output*)
(exit ex-software)))
(defmacro without-output (&body body)
`(prog1 (values)
(with-output-to-string (net)
(handler-case
(let ((*standard-output* net)
(*error-output* net)
(*trace-output* net))
,@body)
(error (err) (report-the-error err net))))))
(without-output (ql:quickload :alexandria))
By the way, you should normally use the nickname ALEXANDRIA, instead of the specific versioned package name ALEXANDRIA.0.DEV, unless you are using an internal API that you can expect to be specific to the version 0 (development) of ALEXANDRIA.
Now, if you don’t plan to use scripts for your CGI, but an executable image, I would advise you to structure your project with:
- a normal lisp source file,
- a lisp source file defining a package,
- an ASDF system definition file, to load your program with asdf or quicklisp,
- a lisp “script” to load and generate the executable image, and
- a Makefile to build it.
See attached files.
CL-USER> (ls)
./Makefile
./generate-executable.lisp
./jean-cgi.asd
./jean-cgi.lisp
./packages.lisp
; No value
CL-USER> (ql:quickload "jean-cgi")
To load "jean-cgi":
Load 1 ASDF system:
jean-cgi
; Loading "jean-cgi"
("jean-cgi")
CL-USER>
The advantage of being able to load your program in an interactive lisp image, is that you will be able to run and test it at the REPL, with the help of the interactive debugger, etc.
For example, this should work:
CL-USER> (with-input-from-string (*standard-intput* "")
(jean-cgi:main))
Well, unsurprisingly, it blocks in alexandria. I often find that alexandria does a bad job.
Here, it seems that it is able to read an empty *standard-input* stream when it’s connected to the terminal or an empty file, but not when it’s a string-stream.
My own utility library cesarum, doesn’t have this problem:
CL-USER> (with-input-from-string (*standard-intput* "")
(contents-from-stream *standard-intput* :min-size 16384))
""
you might prefer using it rather than alexandria (but beware, I license my software under the AGPL3 license). cesarum currently can be found at: https://gitlab.com/com-informatimago/com-informatimago <https://gitlab.com/com-informatimago/com-informatimago> ; clone it in ~/quicklisp/local-projects and use (ql:quickload :com.informatimago.common-lisp.cesarum) to load it.
Anyways, once you’ve debugged your program in an interactive REPL, it’s time to generate the executable image. Here, one objective may be to be able to do it in a reproducible way: you need to control the environment so that each time you or somebody else build it, you all build the same thing. In particular, you don’t want to inherit the environment loaded from the rc file, or found in an interactive REPL. For this reason, I use a generate-executable.lisp file, which is loaded without rc files, and that will set up the environment saved in the lisp image as precisely and reproductibly (notably across various CL implementations) as possible.
And to help, I write a Makefile that will invoke the various CL implementations with the precise options to load generate-executable.lisp correctly (it changes from one implementation to another).
[***@despina :0.0]$ make
clisp -q -ansi -norc -x '(load "generate-executable.lisp")' -x '(quit)'
;; Loading file generate-executable.lisp ...
;; Loading file /Users/pjb/quicklisp/setup.lisp ...
;; Loading file /Users/pjb/quicklisp/ASDF.lisp ...
;; Loaded file /Users/pjb/quicklisp/ASDF.lisp
;; Loaded file /Users/pjb/quicklisp/setup.lisp
To load "jean-cgi":
Load 1 ASDF system:
jean-cgi
; Loading "jean-cgi"
Bytes permanently allocated: 172,224
Bytes currently in use: 6,034,936
Bytes available until next GC: 1,507,604
;; Loaded file generate-executable.lisp
T
~/Sites/cgi
[***@despina :0.0]$
So you can see how clisp is invoked to load generate-executable.lisp.
generate-executable.lisp loads quicklisp, and then use it to compile
and load jean-cgi. Finally it saves the executable image.
In more complex programs, generate-executable.lisp would first delete the compiled fas files found in ~/.cache/common-lisp/*/… to ensure that all the fas file loaded by quicklisp are freshly compiled (notably with the *features* set that can be configured in generate-executable.lisp).
Finally, we’ve obtained a CGI executable that we can test in the shell:
[***@despina :0.0]$ ./jean.cgi </dev/null
Status: 302 Found
Location: http://localhost
~/Sites/cgi
[***@despina :0.0]$
Then invoking it from HTTP is a mere formality.
[***@despina :0.0]$ curl --head 'http://localhost/~pjb/cgi/jean.cgi'
HTTP/1.1 302 Found
Date: Tue, 28 Mar 2017 19:41:14 GMT
Server: Apache/2.2.32 (Unix) mod_ssl/2.2.32 OpenSSL/1.0.2k DAV/2
Location: http://localhost
Content-Type: text/plain
~/Sites/cgi
[***@despina :0.0]$
Have fun!
--
__Pascal J. Bourguignon__