== 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))))))) ==== {{call-with-cgi-query}} (call-with-cgi-query quaerendum) → unspecified Gather parameters (including post-variables, query-variables, cookies, server-variables) into an association-list when called as a CGI program. ; {{quaerendum}} : A monadic function receiving a query parameter (define (call-with-cgi-query quaerendum) (let ((environment (alist-map (lambda (key value) (cons (env-string->symbol key) value)) (get-environment-variables)))) (quaerendum (make-query environment (remove-null-artifacts (append (form-urldecode-environment environment 'http-cookie) (form-urldecode-environment environment 'http-cookie2) (form-urldecode-environment environment 'query-string) (form-urldecode-with-separator (read-all)))))))) ===== Examples Prints out the environment. (call-with-cgi-query (lambda (query) (display-content-type-&c. 'text) (display query))) === 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]] * [[debug]] * [[define-record-and-printer]] * [[fastcgi]] * [[format]] * [[matchable]] * [[regex]] * [[setup-helper]] * [[uri-common]] ==== Versions ; [[https://github.com/klutometis/call-with-query/releases/tag/0.1|0.1]] : First release ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2|0.2]] : Record-printer for queries ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.1|0.2.1]] : JSON and PNG ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.2|0.2.2]] : HTML5 ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.3|0.2.3]] : Remove dependency on regex. ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.4|0.2.4]] : Fix irregex. ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.5|0.2.5]] : Add pdf. ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.6|0.2.6]] : Add XML. ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.7|0.2.7]] : Remove the dependency on setup-helper-cock. ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.8|0.2.8]] : Remove debug. ; [[https://github.com/klutometis/call-with-query/releases/tag/0.2.9|0.2.9]] : Add call-with-cgi-query. ==== Colophon Documented by [[/egg/cock|cock]].