double t_resp;
double t_end;
+ /* Timeouts */
+ double connect_timeout;
+ double first_byte_timeout;
+ double between_bytes_timeout;
+
/* Acceptable grace period */
double grace;
void SES_Delete(struct sess *sp);
void SES_RefSrcAddr(struct sess *sp);
void SES_Charge(struct sess *sp);
+void SES_ResetBackendTimeouts(struct sess *sp);
+void SES_InheritBackendTimeouts(struct sess *sp);
/* cache_shmlog.c */
void VSL_Init(void);
if (s < 0)
return (s);
- tmo = params->connect_timeout;
+ tmo = (int)(sp->connect_timeout * 1000);
if (bp->connect_timeout > 10e-3)
tmo = (int)(bp->connect_timeout * 1000);
char *ident;
char *vcl_name;
double connect_timeout;
+ double first_byte_timeout;
+ double between_bytes_timeout;
uint32_t hash;
REPLACE(b->hosthdr, vb->hosthdr);
b->connect_timeout = vb->connect_timeout;
+ b->first_byte_timeout = vb->first_byte_timeout;
+ b->between_bytes_timeout = vb->between_bytes_timeout;
b->max_conn = vb->max_connections;
/*
CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC);
AZ(sp->obj);
+ SES_ResetBackendTimeouts(sp);
+
/* By default we use the first backend */
AZ(sp->director);
sp->director = sp->vcl->director[0];
if (sp->vbe == NULL)
return (__LINE__);
vc = sp->vbe;
+ /* Inherit the backend timeouts from the selected backend */
+ SES_InheritBackendTimeouts(sp);
/*
* Now that we know our backend, we can set a default Host:
VSL_stats->backend_req++;
HTC_Init(htc, bereq->ws, vc->fd);
- do
+ TCP_set_read_timeout(vc->fd, sp->first_byte_timeout);
+ do {
i = HTC_Rx(htc);
+ TCP_set_read_timeout(vc->fd, sp->between_bytes_timeout);
+ }
while (i == 0);
if (i < 0) {
#include "shmlog.h"
#include "cache.h"
+#include "cache_backend.h"
/*--------------------------------------------------------------------*/
sp->http = &sm->http[0];
sp->http0 = &sm->http[1];
+ SES_ResetBackendTimeouts(sp);
+
return (sp);
}
Lck_New(&stat_mtx);
Lck_New(&ses_mem_mtx);
}
+
+void
+SES_ResetBackendTimeouts(struct sess *sp)
+{
+ sp->connect_timeout = params->connect_timeout;
+ sp->first_byte_timeout = params->first_byte_timeout;
+ sp->between_bytes_timeout = params->between_bytes_timeout;
+}
+
+void
+SES_InheritBackendTimeouts(struct sess *sp)
+{
+ struct backend *be = NULL;
+
+ AN(sp);
+ AN(sp->vbe);
+ AN(sp->vbe->backend);
+
+ be = sp->vbe->backend;
+ /*
+ * We only inherit the backend's timeout if the session timeout
+ * has not already been set in the VCL, as the order of precedence
+ * is parameter < backend definition < VCL.
+ */
+ if (be->connect_timeout > 1e-3 &&
+ sp->connect_timeout == params->connect_timeout)
+ sp->connect_timeout = be->connect_timeout;
+ if (be->first_byte_timeout > 1e-3 &&
+ sp->first_byte_timeout == params->first_byte_timeout)
+ sp->first_byte_timeout = be->first_byte_timeout;
+ if (be->between_bytes_timeout > 1e-3
+ && sp->between_bytes_timeout == params->between_bytes_timeout)
+ sp->between_bytes_timeout = be->between_bytes_timeout;
+}
+
return (atoi(sp->http->hd[HTTP_HDR_STATUS].b));
}
+void
+VRT_l_bereq_connect_timeout(struct sess *sp, double num)
+{
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ sp->connect_timeout = (num > 0 ? num : 0);
+}
+
+double
+VRT_r_bereq_connect_timeout(struct sess *sp)
+{
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ return sp->connect_timeout;
+}
+
+void
+VRT_l_bereq_first_byte_timeout(struct sess *sp, double num)
+{
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ sp->first_byte_timeout = (num > 0 ? num : 0);
+}
+
+double
+VRT_r_bereq_first_byte_timeout(struct sess *sp)
+{
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ return sp->first_byte_timeout;
+}
+
+void
+VRT_l_bereq_between_bytes_timeout(struct sess *sp, double num)
+{
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ sp->between_bytes_timeout = (num > 0 ? num : 0);
+}
+
+double
+VRT_r_bereq_between_bytes_timeout(struct sess *sp)
+{
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ return sp->between_bytes_timeout;
+}
+
/*--------------------------------------------------------------------*/
void
unsigned cache_vbe_conns;
/* Default connection_timeout */
- unsigned connect_timeout;
+ double connect_timeout;
+
+ /* Read timeouts for backend */
+ double first_byte_timeout;
+ double between_bytes_timeout;
/* How long to linger on sessions */
unsigned session_linger;
cli_out(cli, "%u", *dst);
}
+static void
+tweak_generic_timeout_double(struct cli *cli, volatile double *dst, const char *arg)
+{
+ double u;
+
+ if (arg != NULL) {
+ u = strtod(arg, NULL);
+ if (u < 0) {
+ cli_out(cli, "Timeout must be greater or equal to zero\n");
+ cli_result(cli, CLIS_PARAM);
+ return;
+ }
+ *dst = u;
+ } else
+ cli_out(cli, "%f", *dst);
+}
+
+
/*--------------------------------------------------------------------*/
static void
tweak_generic_timeout(cli, dest, arg);
}
+static void
+tweak_timeout_double(struct cli *cli, const struct parspec *par, const char *arg)
+{
+ volatile double *dest;
+ dest = par->priv;
+ tweak_generic_timeout_double(cli, dest, arg);
+}
/*--------------------------------------------------------------------*/
static void
"Cache vbe_conn's or rely on malloc, that's the question.",
EXPERIMENTAL,
"off", "bool" },
- { "connect_timeout", tweak_uint,
+ { "connect_timeout", tweak_timeout_double,
&master.connect_timeout,0, UINT_MAX,
"Default connection timeout for backend connections. "
"We only try to connect to the backend for this many "
- "milliseconds before giving up. "
- "VCL can override this default value for each backend.",
+ "seconds before giving up. "
+ "VCL can override this default value for each backend. "
+ "This does not apply to pipe. ",
+ 0,
+ "0.4", "s" },
+ { "first_byte_timeout", tweak_timeout_double,
+ &master.first_byte_timeout,0, UINT_MAX,
+ "Default timeout for receiving first byte from backend. "
+ "We only wait for this many seconds for the first "
+ "byte before giving up. A value of 0 means it will never time out. "
+ "VCL can override this default value for each backend request.",
+ 0,
+ "60", "s" },
+ { "between_bytes_timeout", tweak_timeout_double,
+ &master.between_bytes_timeout,0, UINT_MAX,
+ "Default timeout between bytes when receiving data from backend. "
+ "We only wait for this many seconds between bytes "
+ "before giving up. A value of 0 means it will never time out. "
+ "VCL can override this default value for each backend request.",
0,
- "400", "ms" },
+ "60", "s" },
{ "accept_fd_holdoff", tweak_timeout,
&master.accept_fd_holdoff, 0, 3600*1000,
"If we run out of file descriptors, the accept thread will "
--- /dev/null
+# $Id: b00019.vtc 3300 2008-10-15 09:52:15Z tfheen $
+
+test "Check the between_bytes_timeout behaves from parameters"
+
+server s1 {
+ rxreq
+ send "HTTP/1.1 200 Ok\r\nConnection: close\r\n\r\n"
+ delay 1.5
+ send "Baba\n"
+} -start
+
+varnish v1 -vcl+backend {} -start
+varnish v1 -cliok "param.set between_bytes_timeout 1"
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+server s1 {
+ rxreq
+ send "HTTP/1.1 200 Ok\r\nConnection: close\r\n\r\n"
+ delay 0.5
+ send "Baba\n"
+ delay 0.5
+ send "Baba\n"
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
--- /dev/null
+# $Id: b00019.vtc 3300 2008-10-15 09:52:15Z tfheen $
+
+test "Check the between_bytes_timeout behaves from vcl"
+
+server s1 {
+ rxreq
+ send "HTTP/1.1 200 Ok\r\nConnection: close\r\n\r\n"
+ delay 1.5
+ send "Baba\n"
+} -start
+
+varnish v1 -vcl+backend {
+ sub vcl_miss {
+ set bereq.between_bytes_timeout = 1s;
+ }
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+server s1 {
+ rxreq
+ send "HTTP/1.1 200 Ok\r\nConnection: close\r\n\r\n"
+ delay 0.5
+ send "Baba\n"
+ delay 0.5
+ send "Baba\n"
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
--- /dev/null
+# $Id: b00019.vtc 3300 2008-10-15 09:52:15Z tfheen $
+
+test "Check the between_bytes_timeout behaves from backend definition"
+
+server s1 {
+ rxreq
+ send "HTTP/1.1 200 Ok\r\nConnection: close\r\n\r\n"
+ delay 1.5
+ send "Baba\n"
+} -start
+
+varnish v1 -vcl {
+ backend b1 {
+ .host = "127.0.0.1";
+ .port = "9080";
+ .between_bytes_timeout = 1s;
+ }
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+server s1 {
+ rxreq
+ send "HTTP/1.1 200 Ok\r\nConnection: close\r\n\r\n"
+ delay 0.5
+ send "Baba\n"
+ delay 0.5
+ send "Baba\n"
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
--- /dev/null
+# $Id: b00019.vtc 3300 2008-10-15 09:52:15Z tfheen $
+
+test "Check that the first_byte_timeout works from parameters"
+
+server s1 {
+ rxreq
+ delay 1.5
+ txresp
+} -start
+
+varnish v1 -vcl+backend {} -start
+varnish v1 -cliok "param.set first_byte_timeout 1"
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+
+server s1 {
+ rxreq
+ delay 0.5
+ txresp
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
--- /dev/null
+# $Id: b00019.vtc 3300 2008-10-15 09:52:15Z tfheen $
+
+test "Check that the first_byte_timeout works from vcl"
+
+server s1 {
+ rxreq
+ delay 1.5
+ txresp
+} -start
+
+varnish v1 -vcl+backend {
+ sub vcl_miss {
+ set bereq.first_byte_timeout = 1s;
+ }
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+
+server s1 {
+ rxreq
+ delay 0.5
+ txresp
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
--- /dev/null
+# $Id: b00019.vtc 3300 2008-10-15 09:52:15Z tfheen $
+
+test "Check that the first_byte_timeout works from backend definition"
+
+server s1 {
+ rxreq
+ delay 1.5
+ txresp
+} -start
+
+varnish v1 -vcl {
+ backend b1 {
+ .host = "127.0.0.1";
+ .port = "9080";
+ .first_byte_timeout = 1s;
+ }
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 503
+} -run
+
+
+server s1 {
+ rxreq
+ delay 0.5
+ txresp
+} -start
+
+client c1 {
+ txreq
+ rxresp
+ expect resp.status == 200
+} -run
--- /dev/null
+# $Id: b00019.vtc 3300 2008-10-15 09:52:15Z tfheen $
+
+test "Check the precedence for timeouts"
+
+server s1 -listen 127.0.0.1:9080 {
+ rxreq
+ expect req.url == "from_backend"
+ delay 1;
+ txresp
+} -start
+server s2 -listen 127.0.0.1:9180 {
+ rxreq
+ expect req.url == "from_vcl"
+ delay 1.5;
+ txresp
+} -start
+
+varnish v1 -vcl {
+ backend b1 {
+ .host = "127.0.0.1";
+ .port = "9080";
+ .first_byte_timeout = 2s;
+ }
+ backend b2 {
+ .host = "127.0.0.1";
+ .port = "9180";
+ .first_byte_timeout = 1s;
+ }
+
+ sub vcl_recv {
+ if (req.url == "from_backend") {
+ set req.backend = b1;
+ pass;
+ }
+ set req.backend = b2;
+ }
+ sub vcl_miss {
+ set bereq.first_byte_timeout = 2s;
+ }
+} -start
+varnish v1 -cliok "param.set first_byte_timeout 0.5"
+
+client c1 {
+ txreq -url "from_backend"
+ rxresp
+ expect resp.status == 200
+ txreq -url "from_vcl"
+ rxresp
+ expect resp.status == 200
+} -run
int TCP_connect(int s, const struct sockaddr *name, socklen_t namelen,
int msec);
void TCP_close(int *s);
+void TCP_set_read_timeout(int socket, double seconds);
#endif
/* from libvarnish/time.c */
const unsigned char *ipv6_sockaddr;
double connect_timeout;
+ double first_byte_timeout;
+ double between_bytes_timeout;
unsigned max_connections;
struct vrt_backend_probe probe;
};
void VRT_l_bereq_url(const struct sess *, const char *, ...);
const char * VRT_r_bereq_proto(const struct sess *);
void VRT_l_bereq_proto(const struct sess *, const char *, ...);
+double VRT_r_bereq_connect_timeout(struct sess *);
+void VRT_l_bereq_connect_timeout(struct sess *, double);
+double VRT_r_bereq_first_byte_timeout(struct sess *);
+void VRT_l_bereq_first_byte_timeout(struct sess *, double);
+double VRT_r_bereq_between_bytes_timeout(struct sess *);
+void VRT_l_bereq_between_bytes_timeout(struct sess *, double);
const char * VRT_r_obj_proto(const struct sess *);
void VRT_l_obj_proto(const struct sess *, const char *, ...);
int VRT_r_obj_status(const struct sess *);
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <math.h>
#include "config.h"
#ifndef HAVE_STRLCPY
errno == ENOTCONN);
*s = -1;
}
+
+void
+TCP_set_read_timeout(int s, double seconds)
+{
+ struct timeval timeout;
+ timeout.tv_sec = floor(seconds);
+ timeout.tv_usec = 1e6 * (seconds - timeout.tv_sec);
+#ifdef SO_RCVTIMEO_WORKS
+ AZ(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout));
+#endif
+}
"?port",
"?host_header",
"?connect_timeout",
+ "?first_byte_timeout",
+ "?between_bytes_timeout",
"?probe",
"?max_connections",
NULL);
Fb(tl, 0, ",\n");
ExpectErr(tl, ';');
vcc_NextToken(tl);
+ } else if (vcc_IdIs(t_field, "first_byte_timeout")) {
+ Fb(tl, 0, "\t.first_byte_timeout = ");
+ vcc_TimeVal(tl);
+ ERRCHK(tl);
+ Fb(tl, 0, ",\n");
+ ExpectErr(tl, ';');
+ vcc_NextToken(tl);
+ } else if (vcc_IdIs(t_field, "between_bytes_timeout")) {
+ Fb(tl, 0, "\t.between_bytes_timeout = ");
+ vcc_TimeVal(tl);
+ ERRCHK(tl);
+ Fb(tl, 0, ",\n");
+ ExpectErr(tl, ';');
+ vcc_NextToken(tl);
} else if (vcc_IdIs(t_field, "max_connections")) {
u = vcc_UintVal(tl);
vcc_NextToken(tl);
vsb_cat(sb, "\tconst unsigned char\t\t*ipv4_sockaddr;\n");
vsb_cat(sb, "\tconst unsigned char\t\t*ipv6_sockaddr;\n");
vsb_cat(sb, "\n\tdouble\t\t\t\tconnect_timeout;\n");
+ vsb_cat(sb, "\tdouble\t\t\t\tfirst_byte_timeout;\n");
+ vsb_cat(sb, "\tdouble\t\t\t\tbetween_bytes_timeout;\n");
vsb_cat(sb, "\tunsigned\t\t\tmax_connections;\n");
vsb_cat(sb, "\tstruct vrt_backend_probe\tprobe;\n");
vsb_cat(sb, "};\n\n/*\n * A director with a predictable reply\n");
vsb_cat(sb, " const char *, ...);\nconst char * VRT_r_bereq_proto(c");
vsb_cat(sb, "onst struct sess *);\nvoid VRT_l_bereq_proto(const str");
vsb_cat(sb, "uct sess *, const char *, ...);\n");
- vsb_cat(sb, "const char * VRT_r_obj_proto(const struct sess *);\n");
- vsb_cat(sb, "void VRT_l_obj_proto(const struct sess *, const char *");
- vsb_cat(sb, ", ...);\nint VRT_r_obj_status(const struct sess *);\n");
+ vsb_cat(sb, "double VRT_r_bereq_connect_timeout(struct sess *);\n");
+ vsb_cat(sb, "void VRT_l_bereq_connect_timeout(struct sess *, double");
+ vsb_cat(sb, ");\ndouble VRT_r_bereq_first_byte_timeout(struct sess ");
+ vsb_cat(sb, "*);\nvoid VRT_l_bereq_first_byte_timeout(struct sess *");
+ vsb_cat(sb, ", double);\ndouble VRT_r_bereq_between_bytes_timeout(s");
+ vsb_cat(sb, "truct sess *);\nvoid VRT_l_bereq_between_bytes_timeout");
+ vsb_cat(sb, "(struct sess *, double);\nconst char * VRT_r_obj_proto");
+ vsb_cat(sb, "(const struct sess *);\nvoid VRT_l_obj_proto(const str");
+ vsb_cat(sb, "uct sess *, const char *, ...);\n");
+ vsb_cat(sb, "int VRT_r_obj_status(const struct sess *);\n");
vsb_cat(sb, "void VRT_l_obj_status(const struct sess *, int);\n");
vsb_cat(sb, "const char * VRT_r_obj_response(const struct sess *);\n");
vsb_cat(sb, "void VRT_l_obj_response(const struct sess *, const cha");
{ pipe pass miss fetch }
"const struct sess *"
}
+ { bereq.connect_timeout
+ RW TIME
+ { pass miss }
+ "struct sess *"
+ }
+ { bereq.first_byte_timeout
+ RW TIME
+ { pass miss }
+ "struct sess *"
+ }
+ { bereq.between_bytes_timeout
+ RW TIME
+ { pass miss }
+ "struct sess *"
+ }
# The (possibly) cached object
{ obj.proto
V_RW, "HDR_BEREQ",
VCL_MET_PIPE | VCL_MET_PASS | VCL_MET_MISS | VCL_MET_FETCH
},
+ { "bereq.connect_timeout", TIME, 21,
+ "VRT_r_bereq_connect_timeout(sp)", "VRT_l_bereq_connect_timeout(sp, ",
+ V_RW, 0,
+ VCL_MET_PASS | VCL_MET_MISS
+ },
+ { "bereq.first_byte_timeout", TIME, 24,
+ "VRT_r_bereq_first_byte_timeout(sp)", "VRT_l_bereq_first_byte_timeout(sp, ",
+ V_RW, 0,
+ VCL_MET_PASS | VCL_MET_MISS
+ },
+ { "bereq.between_bytes_timeout", TIME, 27,
+ "VRT_r_bereq_between_bytes_timeout(sp)", "VRT_l_bereq_between_bytes_timeout(sp, ",
+ V_RW, 0,
+ VCL_MET_PASS | VCL_MET_MISS
+ },
{ "obj.proto", STRING, 9,
"VRT_r_obj_proto(sp)", "VRT_l_obj_proto(sp, ",
V_RW, 0,