Commit ed1ab480 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Add the

	STRING vmod_std.fileread(STRING filename)
function, which will read the contents of a file, typically
for use in synthetic responses.

Submitted by:	Sanjoy Das


git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@5592 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 26d3a235
# $Id$
# XXX: filenames should really contain m00004 for disambiguation
test "Test fileread for std VMOD"
shell {
echo -n "File One" > "${tmpdir}/file_one"
echo -n "File Two" > "${tmpdir}/file_two"
echo -n "File Three" > "${tmpdir}/file_three"
}
server s1 {
loop 3 {
rxreq
txresp -hdr "foo: bar" -bodylen 4
}
} -start
varnish v1 -arg "-pvmod_dir=${topbuild}/lib/libvmod_std/.libs/" \
-vcl+backend {
import std;
sub vcl_deliver {
if (req.url == "/one") {
set resp.http.one = std.fileread("${tmpdir}/file_one");
} else if (req.url == "/two") {
set resp.http.two = std.fileread("${tmpdir}/file_two");
} else if (req.url == "/three") {
set resp.http.three = std.fileread("${tmpdir}/file_three");
}
}
} -start
client c1 {
loop 5 {
txreq -url "/one"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.one == "File One"
}
loop 5 {
txreq -url "/two"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.two == "File Two"
txreq -url "/three"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.three == "File Three"
}
}
client c2 {
loop 5 {
txreq -url "/two"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.two == "File Two"
}
loop 5 {
txreq -url "/one"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.one == "File One"
txreq -url "/three"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.three == "File Three"
}
}
client c3 {
loop 5 {
txreq -url "/three"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.three == "File Three"
}
loop 5 {
txreq -url "/two"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.two == "File Two"
txreq -url "/one"
rxresp
expect resp.status == 200
expect resp.http.content-length == "4"
expect resp.http.foo == "bar"
expect resp.http.one == "File One"
}
}
client c1 -run
client c2 -run
client c3 -run
......@@ -9,7 +9,8 @@ libvmod_std_la_LDFLAGS = -version-info 1:0:0
libvmod_std_la_SOURCES = \
vcc_if.c \
vcc_if.h \
vmod_std.c
vmod_std.c \
vmod_std_fileread.c
vcc_if.c vcc_if.h: $(top_srcdir)/lib/libvmod_std/vmod.py $(top_srcdir)/lib/libvmod_std/vmod.vcc
@PYTHON@ $(top_srcdir)/lib/libvmod_std/vmod.py $(top_srcdir)/lib/libvmod_std/vmod.vcc
......
......@@ -33,3 +33,4 @@ Function VOID set_ip_tos(INT)
Function REAL random(REAL, REAL)
Function VOID log(STRING_LIST)
Function VOID syslog(INT, STRING_LIST)
Function STRING fileread(PRIV_CALL, STRING)
/*-
* Copyright (c) 2010 Linpro AS
* All rights reserved.
*
* Author: Sanjoy Das <sanjoy@playingwithpointers.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* XXX: It might make sense to use just a single global list of all files
* XXX: and use the call-private pointer to point to the file instance on
* XXX: that list.
* XXX: Duplicates in the global list can be avoided by examining the
* XXX: dev+inode fields of the stat structure.
* XXX: Individual files would need to be refcounted, so they can be
* XXX: deleted when no VCL's reference them.
*
* XXX: We should periodically stat(2) the filename and check if the
* XXX: underlying file has been updated.
*/
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "vrt.h"
#include "../../bin/varnishd/cache.h"
#include "vcc_if.h"
VSLIST_HEAD(cached_file_list, cached_file);
struct cached_file {
unsigned magic;
#define CACHED_FILE_MAGIC 0xa8e9d87a
char *file_name;
char *contents;
time_t last_modification;
off_t file_sz;
VSLIST_ENTRY(cached_file) next;
};
static void
free_cached_files(void *file_list)
{
struct cached_file *iter, *tmp;
struct cached_file_list *list = file_list;
VSLIST_FOREACH_SAFE(iter, list, next, tmp) {
CHECK_OBJ(iter, CACHED_FILE_MAGIC);
free(iter->file_name);
free(iter->contents);
FREE_OBJ(iter);
}
free(file_list);
}
static pthread_rwlock_t filelist_lock = PTHREAD_RWLOCK_INITIALIZER;
static int filelist_update = 0;
const char *
vmod_fileread(struct sess *sp, struct vmod_priv *priv, const char *file_name)
{
struct cached_file *iter = NULL;
struct stat buf;
struct cached_file_list *list;
int fd, my_filelist_update;
(void)sp;
AZ(pthread_rwlock_rdlock(&filelist_lock));
if (priv->free == NULL) {
AZ(pthread_rwlock_unlock(&filelist_lock));
/*
* Another thread may already have initialized priv
* here, making the repeat check necessary.
*/
AZ(pthread_rwlock_wrlock(&filelist_lock));
if (priv->free == NULL) {
priv->free = free_cached_files;
priv->priv = malloc(sizeof(struct cached_file_list));
AN(priv->priv);
list = priv->priv;
VSLIST_INIT(list);
}
AZ(pthread_rwlock_unlock(&filelist_lock));
AZ(pthread_rwlock_rdlock(&filelist_lock));
} else {
list = priv->priv;
VSLIST_FOREACH(iter, list, next) {
CHECK_OBJ(iter, CACHED_FILE_MAGIC);
if (strcmp(iter->file_name, file_name) == 0) {
/* This thread was holding a read lock. */
AZ(pthread_rwlock_unlock(&filelist_lock));
return iter->contents;
}
}
}
my_filelist_update = filelist_update;
/* This thread was holding a read lock. */
AZ(pthread_rwlock_unlock(&filelist_lock));
if ((fd = open(file_name, O_RDONLY)) == -1)
return "";
fstat(fd, &buf);
AZ(pthread_rwlock_wrlock(&filelist_lock));
if (my_filelist_update != filelist_update) {
/*
* Small optimization: search through the linked list again
* only if something has been changed.
*/
VSLIST_FOREACH(iter, list, next) {
CHECK_OBJ(iter, CACHED_FILE_MAGIC);
if (strcmp(iter->file_name, file_name) == 0) {
/* This thread was holding a write lock. */
AZ(pthread_rwlock_unlock(&filelist_lock));
return iter->contents;
}
}
}
ALLOC_OBJ(iter, CACHED_FILE_MAGIC);
AN(iter);
iter->file_name = strdup(file_name);
iter->last_modification = buf.st_mtime;
iter->contents = malloc(buf.st_size + 1);
AN(iter->contents);
iter->file_sz = read(fd, iter->contents, buf.st_size);
assert(iter->file_sz == buf.st_size);
AZ(close(fd));
iter->contents[iter->file_sz] = '\0';
VSLIST_INSERT_HEAD(list, iter, next);
filelist_update++;
/* This thread was holding a write lock. */
AZ(pthread_rwlock_unlock(&filelist_lock));
return iter->contents;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment