segunda-feira, 29 de março de 2010

Publicação dinâmica de arquivos estáticos com awful

Hoje um usuário no canal #chicken (freenode.net) levantou a questão de como servir conteúdo dinâmico a partir de arquivos estáticos, mas sem publicar os arquivos estáticos diretamente. É, parece confuso. Um exemplo seria ter o diretório de documentos do servidor vazio e gerar conteúdo dinâmico a partir de arquivos estáticos e código executado no servidor. Isto é exatamente o que awful faz (via define-page). Porém, o usuário queria disponibilizar conteúdo dinâmico em vários formatos, não apenas em [X]HTML. Por exemplo, servir uma imagem através da URL http://servidor/imagem, onde imagem não é um arquivo no diretório de documentos do servidor. Além disso, ele queria que http://servidor/imagem retornasse a própria imagem, não uma página em HTML com <img src="imagem">.

Confesso que isso não foi algo que cogitei quando implementei define-page. Pensei no uso de define-page para a definição de páginas em [X]HTML ou de texto, não para imagens, vídeos ou formatos arbitrários.

Para a minha surpresa, a solução é bastante simples com o uso do procedimento send-static-file, de Spiffy:
(define-page "img"

(lambda ()
(begin
(send-static-file "/home/mario/img.png")
"")))

O código acima faz com que send-static-file ajuste os cabeçalhos HTTP para indicar que a resposta é um arquivo de imagem (com base na extensão) e envie o conteúdo do arquivo dado como argumento.

Porém, send-static-file considera que o caminho do arquivo passado como argumento está a partir do diretório de documentos do servidor web (i.e., o caminho passado como argumento será concatenado com o diretório de documentos do servidor web). Tentativas de passar um caminho absoluto, obviamente, não funcionam, pois o caminho absoluto será concatenado ao diretório de documentos do servidor, possivelmente apontando para um arquivo que não existe ou, com certeza, apontando para o arquivo errado.

A solução para este problema, no entanto, também é fácil: basta temporariamente mudar o valor do parâmetro que indica o diretório de documentos do servidor (root-path):
(define-page "img"

(lambda ()
(begin
(parameterize ((root-path "/"))
(send-static-file "/home/mario/img.png"))
"")))

No exemplo acima, configurei o diretório de documentos do servidor web para ser /, de forma que eu possa referenciar arquivos através do caminho absoluto. O uso de parameterize garante que a definição temporária será válida apenas durante a execução do seu corpo (send-static-file, no exemplo).

Obviamente, a definição de URLs para envio de objetos estáticos de forma dinâmica pode ser simplificada se definirmos um procedimento para isso:
(define (define-static-file url-path abs-path)
(define-page url-path
(lambda ()
(begin

(parameterize ((root-path "/"))
(send-static-file abs-path))
""))))

Estando define-static-file definido, publicar um arquivo estático de forma dinâmica, basta:
(define-static-file "img" "/home/mario/img.png")

Nenhum comentário: