Jump to content

XQuery/Setting HTTP Headers

From Wikibooks, open books for an open world

Motivation

[edit | edit source]

You want to put information in your outgoing HTTP headers to control aspects such as web caching and ETAGS.

Sample Module

[edit | edit source]

The following module was provided by Thomas White.

File tw_stream-binary-cached.xql

xquery version "1.0" encoding "UTF-8";

module namespace cached-binary = "http://www.thomas-white.net/xqm/stream-binary-cached.1.0" ;

declare default function namespace "http://www.w3.org/2005/xpath-functions";

import module namespace xdb="http://exist-db.org/xquery/xmldb";
import module namespace cache = "http://exist-db.org/xquery/cache";
import module namespace datetime = "http://exist-db.org/xquery/datetime";
import module namespace util = "http://exist-db.org/xquery/util";

declare option exist:serialize "method=xml media-type=text/xml"; 

declare function  cached-binary:eTag( $pathToBinaryResource as xs:string, $last-modified as xs:dateTime, $domain-tag as xs:string ) as xs:string{
    concat( $domain-tag, '-', 
            util:document-id( $pathToBinaryResource ) ,'-', 
            fn:translate( fn:substring($last-modified,1,19),':-T' , '')   )
};

declare function  cached-binary:eTag-from-uri( $pathToBinaryResource as xs:string, $domain-tag as xs:string ) as xs:string{
    cached-binary:eTag( $pathToBinaryResource,  
                        xdb:last-modified( util:collection-name( $pathToBinaryResource ), util:document-name( $pathToBinaryResource )), 
                       $domain-tag)
};

declare function  cached-binary:stream-binary-with-cache-headers( 
      $original-path as xs:string?,
      $pathToBinaryResource as xs:string, 
      $expiresAfter as xs:dayTimeDuration?, 
      $must-revalidate as xs:boolean, 
      $doNotCache as xs:string,
      $domain as xs:string?
) {   
    
    if( fn:string-length($pathToBinaryResource) = 0 or not( util:binary-doc-available( $pathToBinaryResource )) ) then (
          response:set-status-code( 404 ),
          concat( $original-path, ' ( ', $pathToBinaryResource, ' ) not found!')   (: ($original-path, $pathToBinaryResource)[1] :)          
    ) else ( 
             
             let $coll := util:collection-name( $pathToBinaryResource )
             let $file := util:document-name(   $pathToBinaryResource )
             let $last-modified :=  xdb:last-modified( $coll, $file) 
             let $ETag := cached-binary:eTag( $pathToBinaryResource, $last-modified, $domain ) 
             let $if-modified-since := request:get-header('If-Modified-Since')
             let $expire-after  := if( empty($expiresAfter) ) then  xs:dayTimeDuration( "P365D" ) else $expiresAfter (: 365 Day expiry period :) 
             
             let $content-type:= (
                util:declare-option('exist:serialize', concat("media-type=", xdb:get-mime-type( xs:anyURI( $pathToBinaryResource) )) ),
                response:set-header( "Pragma", 'o' )
             )             
            
             return if( not($doNotCache = 'true') and (
                        ( request:get-header('If-None-Match') = $ETag ) or (:  ETag :)
                        (fn:string-length($if-modified-since) > 0 and datetime:parse-dateTime( $if-modified-since, 'EEE, d MMM yyyy HH:mm:ss Z' ) <= $last-modified ) 
                     )) then (                              
                        response:set-status-code( 304 ),
                        response:set-header( "Cache-Control", concat('public, max-age=', $expire-after div xs:dayTimeDuration('PT1S')  )) (:   24h=86,400  , must-revalidate :)
                    ) else (                      
                      let $maxAge        := $expire-after  div xs:dayTimeDuration('PT1S')
                      let $headers := (                          
                           response:set-header( "ETag", $ETag ),
                           response:set-header( "Last-Modified",  datetime:format-dateTime( $last-modified, 'EEE, d MMM yyyy HH:mm:ss Z' )),
                           response:set-header( "Expires",        datetime:format-dateTime( dateTime(current-date(), util:system-time()) + $expire-after, 'EEE, d MMM yyyy HH:mm:ss Z' )), 
                           
                           if( $doNotCache = 'true' ) then (
                              response:set-header( "Cache-Control",  'no-cache, no-store, max-age=0, must-revalidate' ),
                              response:set-header( "X-Content-Type-Options", 'nosniff' )
                           )else 
                              response:set-header( "Cache-Control", concat( 'public, max-age=', $maxAge,  if( $must-revalidate ) then ', must-revalidate' else '' ))
                       )
                       return response:stream-binary(
                                util:binary-doc( xs:anyURI($pathToBinaryResource ) ), 
                                xdb:get-mime-type( xs:anyURI( $pathToBinaryResource) ),
                                xs:anyURI( ($original-path, $pathToBinaryResource)[1] ) 
                             )    
                    ) 

     )                                       
};

(:

HTTP/1.1 200 OK
Date: Fri, 30 Oct 1998 13:19:41 GMT
Server: Apache/1.3.3 (Unix)
Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: "3e86-410-3596fbbc"
Content-Length: 1040
Content-Type: text/html

Cache-Control: max-age=3600, must-revalidate
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Last-Modified: Mon, 29 Jun 1998 02:28:12 GMT
ETag: "3e86-410-3596fbbc"

Cache-Control: public, max-age=1728000
Expires: Thu, 06 Aug 2009 10:04:13 GMT
Date: Fri, 17 Jul 2009 10:04:13 GMT
Content-Type: text/javascript; charset=UTF-8
ETag: "ih2h6n8r44hc"
Last-Modified: Fri, 05 Sep 2003 02:11:15 GMT
X-Content-Type-Options: nosniff
:)
xquery version "1.0"  encoding "UTF-8";

declare default function namespace "http://www.w3.org/2005/xpath-functions";

import module namespace request  = "http://exist-db.org/xquery/request";
import module namespace cached-binary = "http://www.thomas-white.net/xqm/stream-binary-cached.1.0"   at "tw_stream-binary-cached.xql"; 
 
cached-binary:stream-binary-with-cache-headers( 
        request:get-parameter("url", ()), 
        request:get-parameter("uri", 'no-uri'), 
        xs:dayTimeDuration(request:get-parameter("expire", 'P30D')), 
        xs:boolean(request:get-parameter("must-revalidate", 'false') = 'true'), 
        request:get-parameter("doNotCache", ''), 
        request:get-parameter("domain", '')
       )