== call-with-query A reasonable abstraction around e.g. fastcgi server-invocations: ports, environment, query [[toc:]] === Motivation Using FastCGI is relatively pain-in-the-ass; take this contrived example, for instance, where we'd like create a server that exports a database (given as a post-parameter) as JSON: (fcgi-dynamic-server-accept-loop (lambda (in out err env) (out "Content-type: application/json\r\n\r\n") (let* ((post-data (form-urldecode (fcgi-get-post-data in env))) (database (alist-ref/default post-data 'database (default-database)))) (out (with-output-to-string (lambda () (json-write (database->json database)))))))) With {{call-with-query}}, however, we can do something like this: (call-with-dynamic-fastcgi-query (lambda (query) (display-content-type-&c. 'json) (json-write (database->json (query-any query 'database (default-database)))))) Anything written to stdout appears in the request; anything to stderr goes in the server logs; while {{display-content-type-&c.}} takes care of the HTTP headers. === Documentation ==== {{display-content-type-&c.}} (display-content-type-&c.) → unspecified (display-content-type-&c. content-type-&c.) → unspecified Write the content-type headers and e.g. XML prolog (if necessary); do not, however, write the status (see {{display-status}} and {{display-status-&c.}}). Valid content-types are {{xhtml}}, {{html}}, {{text}}, {{json}}, {{png}}, {{xml}}. ; content-type-&c. : The content-type-and--prolog, e.g. {{xhtml}} (define display-content-type-&c. (case-lambda (() (display-content-type-&c. (default-content-type-&c.))) ((content-type-&c.) ((alist-ref content-type-&cs. content-type-&c.))))) ==== {{display-status-&c.}} (display-status-&c.) → unspecified (display-status-&c. status) → unspecified (display-status-&c. status content-type . rest) → unspecified Display the status, content-type and prolog. ; status : Status code, e.g. {{status-no-content}} or 204 ; content-type : Content-type, e.g. {{xhtml}} ; rest : Optional arguments to the status, e.g. {{location}} in the case of 300 (define display-status-&c. (case-lambda (() (display-status-&c. (default-status))) ((status) (display-status-&c. status (default-content-type))) ((status content-type . rest) (display-status status) (apply (alist-ref/default statuses status void) rest) (display-content-type-&c. content-type)))) ==== {{query-client-any}} (query-client-any query key) → string (query-client-any query key default) → string Return the first client parameter (e.g. {get,post,cookie}-parameter) corresponding to the key. ; key : The key whose value to extract ; default : A default value if {{key}} doesn't exist (define query-client-any (case-lambda ((query key) (alist-any (query-client query) key)) ((query key default) (alist-any (query-client query) key default)))) ==== {{query-client-all}} (query-client-all query key) → list (query-client-all query key default) → list Return a list of client parameters (e.g. {get,post,cookie}-parameters) corresponding to the key. ; key : The key whose value to extract ; default : A default value if {{key}} doesn't exist (define query-client-all (case-lambda ((query key) (alist-all (query-client query) key)) ((query key default) (alist-all (query-client query) key default)))) ==== {{query-server-any}} (query-server-any query key) → string (query-server-any query key default) → string Return the first client parameter (e.g. environment-variable) corresponding to the key. ; key : The key whose value to extract ; default : A default value if {{key}} doesn't exist (define query-server-any (case-lambda ((query key) (alist-any (query-server query) key)) ((query key default) (alist-any (query-server query) key default)))) ==== {{query-server-all}} (query-server-all query key) → list (query-server-all query key default) → list Return a list of client parameters (e.g. environment-variables) corresponding to the key. ; key : The key whose value to extract ; default : A default value if {{key}} doesn't exist (define query-server-all (case-lambda ((query key) (alist-all (query-server query) key)) ((query key default) (alist-all (query-server query) key default)))) ==== {{query-any}} (query-any query key) → string (query-any query key default) → string Return the first client or server parameter (see above) corresponding to the key. ; key : The key whose value to extract ; default : A default value if {{key}} doesn't exist (define query-any (case-lambda ((query key) (alist-any (query-promiscuous query) key)) ((query key default) (alist-any (query-promiscuous query) key default)))) ==== {{query-all}} (query-all query key) → list (query-all query key default) → list Return a list of client or server parameters (see above) corresponding to the key. ; key : The key whose value to extract ; default : A default value if {{key}} doesn't exist (define query-all (case-lambda ((query key) (alist-all (query-promiscuous query) key)) ((query key default) (alist-all (query-promiscuous query) key default)))) ==== {{call-with-dynamic-fastcgi-query}} (call-with-dynamic-fastcgi-query quaerendum) → unspecified Start a dynamic FastCGI server where output is bound to stdout; and where a monadic function taking a query-record is called for every request. ; quaerendum : A monadic function receiving a query parameter (define (call-with-dynamic-fastcgi-query quaerendum) (fcgi-dynamic-server-accept-loop (lambda (in out err env) (let ((environment (map (match-lambda ((key . value) (cons (env-string->symbol key) value))) (env))) (cookies (form-urldecode (let ((cookies (string-delete char-set:whitespace (env "HTTP_COOKIE" "")))) (and (not (string-null? cookies)) cookies)))) (cookies2 (form-urldecode (let ((cookies (string-delete char-set:whitespace (env "HTTP_COOKIE2" "")))) (and (not (string-null? cookies)) cookies)))) (post-data (form-urldecode (fcgi-get-post-data in env))) (query (form-urldecode (let ((query (env "QUERY_STRING"))) (and (not (string-null? query)) query))))) (parameterize ((current-output-port (make-output-port (lambda (scribendum) (out scribendum)) void)) (current-error-port (make-output-port (lambda (errandum) (err errandum)) void))) (quaerendum (make-query environment (append cookies cookies2 post-data query)))))))) ===== Examples An authorization server (call-with-auth-db (lambda (connection) (call-with-dynamic-fastcgi-query (lambda (query) (let ((user (query-server-any query 'remote-user)) (password (query-server-any query 'remote-passwd))) (let ((status (if (valid? connection user password "physician") status-ok status-unauthorized))) (display-status-&c. status))))))) === About this egg ==== Author [[/users/klutometis|Peter Danenberg]] ==== Repository [[https://github.com/klutometis/call-with-query]] ==== License GPL-3 ==== Dependencies * [[alist-lib]] * [[args]] * [[call-with-environment-variables]] * [[cock]] * [[define-record-and-printer]] * [[fastcgi]] * [[format]] * [[matchable]] * [[regex]] * [[setup-helper]] * [[uri-common]] ==== Versions ; 0.1 : First release ; 0.2 : Record-printer for queries ; 0.2.1 : JSON and PNG ; 0.2.2 : HTML5 ; 0.2.3 : Remove dependency on regex. ; 0.2.4 : Fix irregex. ; 0.2.5 : Add pdf. ; 0.2.6 : Add XML. ==== Colophon Documented by [[/egg/cock|cock]].