[BIGMOVE] exploded the monolithic haproxy.c file into multiple files.

The files are now stored under :
  - include/haproxy for the generic includes
  - include/types.h for the structures needed within prototypes
  - include/proto.h for function prototypes and inline functions
  - src/*.c for the C files

Most include files are now covered by LGPL. A last move still needs
to be done to put inline functions under GPL and not LGPL.

Version has been set to 1.3.0 in the code but some control still
needs to be done before releasing.
This commit is contained in:
Willy Tarreau 2006-06-26 02:48:02 +02:00
parent 0028339317
commit baaee00406
83 changed files with 15105 additions and 10860 deletions

View File

@ -2,7 +2,7 @@
# You should use it this way :
# make TARGET=os CPU=cpu
VERSION := 1.2.14
VERSION := 1.3.0
# Select target OS. TARGET must match a system for which COPTS and LIBS are
# correctly defined below.
@ -91,17 +91,15 @@ ADDLIB =
# set some defines when needed.
# Known ones are -DENABLE_POLL, -DENABLE_EPOLL, and -DUSE_MY_EPOLL
# - use -DSTATTIME=0 to disable statistics, else specify an interval in
# milliseconds.
# - use -DTPROXY to compile with transparent proxy support.
DEFINE = -DSTATTIME=0 -DTPROXY
DEFINE = -DTPROXY
# global options
TARGET_OPTS=$(COPTS.$(TARGET))
REGEX_OPTS=$(COPTS.$(REGEX))
CPU_OPTS=$(COPTS.$(CPU))
COPTS=-I. $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(DEFINE)
COPTS=-Iinclude $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(DEFINE)
LIBS=$(LIBS.$(TARGET)) $(LIBS.$(REGEX)) $(ADDLIB)
CFLAGS = -Wall $(COPTS) $(DEBUG)
@ -109,18 +107,31 @@ LDFLAGS = -g
all: haproxy
haproxy: src/list.o src/chtbl.o src/hashpjw.o haproxy.o src/base64.o src/uri_auth.o
OBJS = src/haproxy.o src/list.o src/chtbl.o src/hashpjw.o src/base64.o \
src/uri_auth.o src/standard.o src/buffers.o src/log.o src/task.o \
src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \
src/checks.o src/queue.o src/capture.o src/client.o src/proxy.o \
src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \
src/session.o
haproxy: $(OBJS)
$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
objsize: haproxy
@objdump -t $^|grep ' g '|grep -F '.text'|awk '{print $$5 FS $$6}'|sort
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
clean:
rm -f {.,src}/*.[oas] {.,src,include,doc}/*{~,.rej} core haproxy test nohup.out gmon.out
rm -f haproxy-$(VERSION).tar.gz haproxy-$(VERSION)
rm -f {.,src}/*.[oas] {.,src,include/*,doc}/*{~,.rej} core haproxy test
rm -f haproxy-$(VERSION).tar.gz haproxy-$(VERSION) nohup.out gmon.out
tar: clean
ln -s . haproxy-$(VERSION)
tar --exclude=haproxy-$(VERSION)/.git --exclude=haproxy-$(VERSION)/haproxy-$(VERSION) -cf - haproxy-$(VERSION)/* | gzip -c9 >haproxy-$(VERSION).tar.gz
tar --exclude=haproxy-$(VERSION)/.git \
--exclude=haproxy-$(VERSION)/haproxy-$(VERSION) \
--exclude=haproxy-$(VERSION)/haproxy-$(VERSION).tar.gz \
-cf - haproxy-$(VERSION)/* | gzip -c9 >haproxy-$(VERSION).tar.gz
rm -f haproxy-$(VERSION)

View File

@ -62,10 +62,8 @@ ADDLIB =
# set some defines when needed.
# Known ones are -DENABLE_POLL, -DENABLE_EPOLL, and -DUSE_MY_EPOLL
# - use -DSTATTIME=0 to disable statistics, else specify an interval in
# milliseconds.
# - use -DTPROXY to compile with transparent proxy support.
DEFINE = -DSTATTIME=0 -DTPROXY
DEFINE = -DTPROXY
# global options
TARGET_OPTS=$(COPTS.$(TARGET))

40
ROADMAP
View File

@ -36,6 +36,46 @@
max(srv->maxconn * px->nbsess / px->maxconn, srv->minconn)
1.3 :
- remove unused STATTIME
- reference all the include files that must be created, possibly under subdirs :
- acl.h => more general ACL work
- appcook.h => appsession-related cookies
- backend.h => back-end part of the PR_O_* + backend definitions
- buffers.h => buffer management relying on memory.h
- capture.h => header and cookie capture
- cfgparse.h => configuration parser
- checks.h => health checks
- clireq.h => the client side "request" part of the current sessions.
- compat.h => compatibility with other OSes (TCP_NODELAY, ...)
- config.h => config parameters, renamed CONFIG_HAP_*, includes defaults.h
- controls.h => SN_CACHEABLE, ...
- cookies.h => definitions related to cookie management + SN_SCK_*
- defaults.h => many default values, might disappear soon after cleanup
- frontend.h => front-end part of the PR_O_* + client definitions + listeners
- global.h => shared global variables
- http.h => HTTP state definitions and transitions
- httperr.{hc} => HTTP return codes
- libtask.h => task scheduler
- libtime.h => time-related definitions
- loadbal.h => load balancing algorithms
- log.h => log definitions
- memory.h => pools
- polling.h => definitions of select(), poll(), INTBITS, ...
- queue.h => queue management
- regex.h => filtering
- servers.h => servers definitions (SRV_*, states, ...)
- fd.h => FD_ST* (add FD_DGRAM), RES_*, socket states, etc...
- srvreq.h => the server side "request" part of the current sessions.
- standard.h => general purpose macros and defines (eg: MIN/MAX, ...)
- startup.h => MODE_*
- tuning.h => platform-specific tuning parameters
- clarify licence by adding a 'MODULE_LICENCE("GPL")' or something equivalent.
- handle half-closed connections better (cli/srv would not distinguish
DATA/SHUTR/SHUTW, it would be a session flag which would tell shutr/shutw).
Check how it got changed in httpterm.

8
TODO
View File

@ -160,3 +160,11 @@ Todo for 1.2
- embedded error pages loaded in memory at startup time (eg: for expired time
in connection queue)
TODO for 1.3
============
- check all copyrights
- put haproxy/config.h in every include file. This one will automatically
include version.h, defaults.h and compat.h.
- fix Makefile.bsd
- separate inline functions to put them in files covered by GPL

1052
doc/buffers.fig Normal file

File diff suppressed because it is too large Load Diff

60
doc/how-it-works.txt Normal file
View File

@ -0,0 +1,60 @@
How it works ? (unfinished and inexact)
For TCP and HTTP :
- listeners create listening sockets with a READ callback pointing to the
protocol-specific accept() function.
- the protocol-specific accept() function then accept()'s the connection and
instantiates a "server TCP socket" (which is dedicated to the client side),
and configures it (non_block, get_original_dst, ...).
For TCP :
- in case of pure TCP, a request buffer is created, as well as a "client TCP
socket", which tries to connect to the server.
- once the connection is established, the response buffer is allocated and
connected to both ends.
- both sockets are set to "autonomous mode" so that they only wake up their
supervising session when they encounter a special condition (error or close).
For HTTP :
- in case of HTTP, a request buffer is created with the "HOLD" flag set and
a read limit to support header rewriting (may be this one will be removed
eventually because it's better to limit only to the buffer size and report
an error when rewritten data overflows)
- a "flow analyzer" is attached to the buffer (or possibly multiple flow
analyzers). For the request, the flow analyzer is "http_lb_req". The flow
analyzer is a function which gets called when new data is present and
blocked. It has a timeout (request timeout). It can also be bypassed on
demand.
- when the "http_lb_req" has received the whole request, it creates a client
socket with all the parameters needed to try to connect to the server. When
the connection establishes, the response buffer is allocated on the fly,
put to HOLD mode, and a an "http_lb_resp" flow analyzer is attached to the
buffer.
For client-side HTTPS :
- the accept() function must completely instantiate a TCP socket + an SSL
reader. It is when the SSL session is complete that we call the
protocol-specific accept(), and create its buffer.
Conclusions
-----------
- we need a generic TCP accept() function with a lot of flags set by the
listener, to tell it what info we need to get at the accept() time, and
what flags will have to be set on the socket.
- once the TCP accept() function ends, it wakes up the protocol supervisor
which is in charge of creating the buffers, etc, switch states, etc...

10434
haproxy.c

File diff suppressed because it is too large Load Diff

View File

@ -1,44 +0,0 @@
#ifndef _APPSESS_H
#define _APPSESS_H
#define TBLSIZ 10
#define TBLCHKINT 5000 /* The time between two calls of appsession_refresh in ms */
/*
These Parts are copied from
http://www.oreilly.com/catalog/masteralgoc/index.html
Mastering Algorithms with C
By Kyle Loudon
ISBN: 1-56592-453-3
Publishd by O'Reilly
We have added our own struct to these function.
*/
#include <include/list.h>
#include <include/chtbl.h>
#include <include/hashpjw.h>
/* end of copied parts */
struct app_pool {
void **sessid;
void **serverid;
int ses_waste, ses_use, ses_msize;
int ser_waste, ser_use, ser_msize;
};
struct app_pool apools;
int have_appsession;
/* Callback for hash_lookup */
int match_str(const void *key1, const void *key2);
/* Callback for destroy */
void destroy(void *data);
#if defined(DEBUG_HASH)
static void print_table(const CHTbl *htbl);
#endif
#endif

View File

@ -0,0 +1,58 @@
#ifndef _HAPROXY_APPSESS_H
#define _HAPROXY_APPSESS_H
#define TBLSIZ 10
#define TBLCHKINT 5000 /* The time between two calls of appsession_refresh in ms */
#include <sys/time.h>
#include <haproxy/chtbl.h>
#include <haproxy/hashpjw.h>
#include <haproxy/list.h>
#include <types/task.h>
typedef struct appsessions {
char *sessid;
char *serverid;
struct timeval expire; /* next expiration time for this application session */
unsigned long int request_count;
} appsess;
#define sizeof_appsess sizeof(struct appsessions)
extern void **pool_appsess;
struct app_pool {
void **sessid;
void **serverid;
int ses_waste, ses_use, ses_msize;
int ser_waste, ser_use, ser_msize;
};
extern struct app_pool apools;
extern int have_appsession;
/* Callback for hash_lookup */
int match_str(const void *key1, const void *key2);
/* Callback for destroy */
void destroy(void *data);
#if defined(DEBUG_HASH)
static void print_table(const CHTbl *htbl);
#endif
int appsession_refresh(struct task *t);
int appsession_task_init(void);
int appsession_init(void);
void appsession_cleanup(void);
#endif /* _HAPROXY_APPSESS_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -1,6 +1,8 @@
/*
* include/haproxy/base64.h
* Ascii to Base64 conversion as described in RFC1421.
* Copyright 2006 Willy Tarreau <willy@w.ods.org>
*
* Copyright 2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -9,9 +11,10 @@
*
*/
#ifndef BASE64_H
#define BASE64_H
#ifndef _HAPROXY_BASE64_H
#define _HAPROXY_BASE64_H
int a2base64(char *in, int ilen, char *out, int olen);
extern const char base64tab[];
#endif /* BASE64_H */
#endif /* _HAPROXY_BASE64_H */

View File

@ -0,0 +1,45 @@
/*
include/haproxy/cfgparse.h
Configuration parsing functions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_CFGPARSE_H
#define _HAPROXY_CFGPARSE_H
/* configuration sections */
#define CFG_NONE 0
#define CFG_GLOBAL 1
#define CFG_LISTEN 2
extern int cfg_maxpconn;
extern int cfg_maxconn;
int cfg_parse_global(char *file, int linenum, char **args);
int cfg_parse_listen(char *file, int linenum, char **args);
int readcfgfile(char *file);
#endif /* _HAPROXY_CFGPARSE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -15,8 +15,8 @@
* *
*****************************************************************************/
#ifndef CHTBL_H
#define CHTBL_H
#ifndef _HAPROXY_CHTBL_H
#define _HAPROXY_CHTBL_H
#include <stdlib.h>
@ -59,4 +59,5 @@ int chtbl_lookup(const CHTbl *htbl, void **data);
#define chtbl_size(htbl) ((htbl)->size)
#endif
#endif /* _HAPROXY_CHTBL_H */

62
include/haproxy/compat.h Normal file
View File

@ -0,0 +1,62 @@
/*
include/haproxy/compat.h
Operating system compatibility interface.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_COMPAT_H
#define _HAPROXY_COMPAT_H
/* This is needed on Linux for Netfilter includes */
#include <sys/socket.h>
/* INTBITS
* how many bits are needed to code the size of an int on the target platform.
* (eg: 32bits -> 5)
*/
#define INTBITS 5
/* this is for libc5 for example */
#ifndef TCP_NODELAY
#define TCP_NODELAY 1
#endif
#ifndef SHUT_RD
#define SHUT_RD 0
#endif
#ifndef SHUT_WR
#define SHUT_WR 1
#endif
#if defined(TPROXY) && defined(NETFILTER)
#include <linux/netfilter_ipv4.h>
#endif
#if defined(__dietlibc__)
#include <strings.h>
#endif
#endif /* _HAPROXY_COMPAT_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

34
include/haproxy/config.h Normal file
View File

@ -0,0 +1,34 @@
/*
include/haproxy/config.h
This files contains most of the user-configurable settings.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_CONFIG_H
#define _HAPROXY_CONFIG_H
#include <haproxy/defaults.h>
/* this reduces the number of calls to select() by choosing appropriate
* sheduler precision in milliseconds. It should be near the minimum
* time that is needed by select() to collect all events. All timeouts
* are rounded up by adding this value prior to pass it to select().
*/
#define SCHEDULER_RESOLUTION 9
#endif /* _HAPROXY_CONFIG_H */

View File

@ -0,0 +1,94 @@
/*
include/haproxy/defaults.h
Miscellaneous default values.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_DEFAULTS_H
#define _HAPROXY_DEFAULTS_H
/* CONFIG_HAP_MEM_OPTIM
* This enables use of memory pools instead of malloc()/free(). There
* is no reason to disable it, except perhaps for rare debugging.
*/
#ifndef CONFIG_HAP_NO_MEM_OPTIM
# define CONFIG_HAP_MEM_OPTIM
#endif /* CONFIG_HAP_NO_MEM_OPTIM */
/*
* BUFSIZE defines the size of a read and write buffer. It is the maximum
* amount of bytes which can be stored by the proxy for each session. However,
* when reading HTTP headers, the proxy needs some spare space to add or rewrite
* headers if needed. The size of this spare is defined with MAXREWRITE. So it
* is not possible to process headers longer than BUFSIZE-MAXREWRITE bytes. By
* default, BUFSIZE=16384 bytes and MAXREWRITE=BUFSIZE/2, so the maximum length
* of headers accepted is 8192 bytes, which is in line with Apache's limits.
*/
#ifndef BUFSIZE
#define BUFSIZE 16384
#endif
// reserved buffer space for header rewriting
#ifndef MAXREWRITE
#define MAXREWRITE (BUFSIZE / 2)
#endif
#define REQURI_LEN 1024
#define CAPTURE_LEN 64
// max # args on a configuration line
#define MAX_LINE_ARGS 40
// max # of added headers per request
#define MAX_NEWHDR 10
// max # of matches per regexp
#define MAX_MATCH 10
// cookie delimitor in "prefix" mode. This character is inserted between the
// persistence cookie and the original value. The '~' is allowed by RFC2965,
// and should not be too common in server names.
#ifndef COOKIE_DELIM
#define COOKIE_DELIM '~'
#endif
#define CONN_RETRIES 3
#define CHK_CONNTIME 2000
#define DEF_CHKINTR 2000
#define DEF_FALLTIME 3
#define DEF_RISETIME 2
#define DEF_CHECK_REQ "OPTIONS / HTTP/1.0\r\n\r\n"
/* Default connections limit.
*
* A system limit can be enforced at build time in order to avoid using haproxy
* beyond reasonable system limits. For this, just define SYSTEM_MAXCONN to the
* absolute limit accepted by the system. If the configuration specifies a
* higher value, it will be capped to SYSTEM_MAXCONN and a warning will be
* emitted. The only way to override this limit will be to set it via the
* command-line '-n' argument.
*/
#ifndef SYSTEM_MAXCONN
#define DEFAULT_MAXCONN 2000
#else
#define DEFAULT_MAXCONN SYSTEM_MAXCONN
#endif
#endif /* _HAPROXY_DEFAULTS_H */

View File

@ -1,3 +1,24 @@
/*
include/haproxy/epoll.h
epoll definitions for older libc.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Those constants were found both in glibc and in the Linux kernel.
* They are provided here because the epoll() syscall is featured in
@ -5,6 +26,9 @@
* just a basic definition.
*/
#ifndef _HAPROXY_EPOLL_H
#define _HAPROXY_EPOLL_H
#include <linux/unistd.h>
#include <stdint.h>
@ -27,13 +51,13 @@
#endif
struct epoll_event {
uint32_t events;
union {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} data;
uint32_t events;
union {
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} data;
};
@ -64,6 +88,16 @@ struct epoll_event {
#define __NR_epoll_wait 256
#endif
_syscall1 (int, epoll_create, int, size);
_syscall4 (int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, event);
_syscall4 (int, epoll_wait, int, epfd, struct epoll_event *, events, int, maxevents, int, timeout);
extern int epoll_create(int size);
extern int epoll_ctl(int epfd, int op, int fd, struct epoll_event * event);
extern int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
#endif /* _HAPROXY_EPOLL_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -16,17 +16,8 @@
* *
*****************************************************************************/
#ifndef HASHPJW_H
#define HASHPJW_H
#include <sys/time.h>
typedef struct appsessions {
char *sessid;
char *serverid;
struct timeval expire; /* next expiration time for this application session */
unsigned long int request_count;
} appsess; /* end struct appsessions */
#ifndef _HAPROXY_HASHPJW_H
#define _HAPROXY_HASHPJW_H
/*****************************************************************************
* *
@ -44,4 +35,4 @@ typedef struct appsessions {
int hashpjw(const void *key);
#endif
#endif /* _HAPROXY_HASHPJW_H */

View File

@ -15,8 +15,8 @@
* *
*****************************************************************************/
#ifndef LIST_H
#define LIST_H
#ifndef _HAPROXY_LIST_H
#define _HAPROXY_LIST_H
#include <stdlib.h>
@ -27,9 +27,8 @@
*****************************************************************************/
typedef struct ListElmt_ {
void *data;
struct ListElmt_ *next;
void *data;
struct ListElmt_ *next;
} ListElmt;
/*****************************************************************************
@ -39,12 +38,12 @@ typedef struct ListElmt_ {
*****************************************************************************/
typedef struct List_ {
int size;
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
int size;
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
ListElmt *head;
ListElmt *tail;
ListElmt *head;
ListElmt *tail;
} List;
/*****************************************************************************
@ -75,4 +74,11 @@ int list_rem_next(List *list, ListElmt *element, void **data);
#define list_next(element) ((element)->next)
#endif
#endif /* _HAPROXY_LIST_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

122
include/haproxy/memory.h Normal file
View File

@ -0,0 +1,122 @@
/*
include/haproxy/memory.h
Memory management definitions..
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_MEMORY_H
#define _HAPROXY_MEMORY_H
#include <stdlib.h>
#include <haproxy/config.h>
#define sizeof_requri REQURI_LEN
#define sizeof_capture CAPTURE_LEN
/*
* Returns a pointer to an area of <__len> bytes taken from the pool <pool> or
* dynamically allocated. In the first case, <__pool> is updated to point to
* the next element in the list.
*/
#define pool_alloc_from(__pool, __len) \
({ \
void *__p; \
if ((__p = (__pool)) == NULL) \
__p = malloc(((__len) >= sizeof (void *)) ? \
(__len) : sizeof(void *)); \
else { \
__pool = *(void **)(__pool); \
} \
__p; \
})
/*
* Puts a memory area back to the corresponding pool.
* Items are chained directly through a pointer that
* is written in the beginning of the memory area, so
* there's no need for any carrier cell. This implies
* that each memory area is at least as big as one
* pointer.
*/
#define pool_free_to(__pool, __ptr) \
({ \
*(void **)(__ptr) = (void *)(__pool); \
__pool = (void *)(__ptr); \
})
#ifdef CONFIG_HAP_MEM_OPTIM
/*
* Returns a pointer to type <type> taken from the
* pool <pool_type> or dynamically allocated. In the
* first case, <pool_type> is updated to point to the
* next element in the list.
*/
#define pool_alloc(type) \
({ \
void *__p; \
if ((__p = pool_##type) == NULL) \
__p = malloc(sizeof_##type); \
else { \
pool_##type = *(void **)pool_##type; \
} \
__p; \
})
/*
* Puts a memory area back to the corresponding pool.
* Items are chained directly through a pointer that
* is written in the beginning of the memory area, so
* there's no need for any carrier cell. This implies
* that each memory area is at least as big as one
* pointer.
*/
#define pool_free(type, ptr) \
({ \
*(void **)ptr = (void *)pool_##type; \
pool_##type = (void *)ptr; \
})
#else
#define pool_alloc(type) (calloc(1,sizeof_##type))
#define pool_free(type, ptr) (free(ptr))
#endif /* CONFIG_HAP_MEM_OPTIM */
/*
* This function destroys a pull by freeing it completely.
* This should be called only under extreme circumstances.
*/
static inline void pool_destroy(void **pool)
{
void *temp, *next;
next = pool;
while (next) {
temp = next;
next = *(void **)temp;
free(temp);
}
}
#endif /* _HAPROXY_MEMORY_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -1,11 +1,11 @@
/*
* list.h : list manipulation macros and structures.
* (C) 2002-2006 - Willy Tarreau - willy@ant-computing.com
* Copyright 2002-2006 Willy Tarreau <w@1wt.eu>
*
*/
#ifndef __MINI_CLIST_H__
#define __MINI_CLIST_H__
#ifndef _HAPROXY_MINI_CLIST_H
#define _HAPROXY_MINI_CLIST_H
/* these are circular or bidirectionnal lists only. Each list pointer points to
* another list pointer in a structure, and not the structure itself. The
@ -17,6 +17,8 @@ struct list {
struct list *p; /* prev */
};
#define LIST_HEAD(a) ((void *)(&(a)))
#define LIST_INIT(l) ((l)->n = (l)->p = (l))
/* dual linked lists :
@ -89,4 +91,4 @@ struct list {
for ( ; (iterator) != (end_item); (iterator) = (backup), \
backup = LIST_ELEM((iterator)->struct_member.n, struct_type, struct_member))
#endif /* __MINI_CLIST_H__ */
#endif /* _HAPROXY_MINI_CLIST_H */

61
include/haproxy/regex.h Normal file
View File

@ -0,0 +1,61 @@
/*
include/haproxy/regex.h
This file defines everything related to regular expressions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_REGEX_H
#define _HAPROXY_REGEX_H
#include <haproxy/defaults.h>
#ifdef USE_PCRE
#include <pcre.h>
#include <pcreposix.h>
#else
#include <regex.h>
#endif
/* what to do when a header matches a regex */
#define ACT_ALLOW 0 /* allow the request */
#define ACT_REPLACE 1 /* replace the matching header */
#define ACT_REMOVE 2 /* remove the matching header */
#define ACT_DENY 3 /* deny the request */
#define ACT_PASS 4 /* pass this header without allowing or denying the request */
struct hdr_exp {
struct hdr_exp *next;
regex_t *preg; /* expression to look for */
int action; /* ACT_ALLOW, ACT_REPLACE, ACT_REMOVE, ACT_DENY */
char *replace; /* expression to set instead */
};
extern regmatch_t pmatch[MAX_MATCH];
int exp_replace(char *dst, char *src, char *str, regmatch_t *matches);
char *check_replace_string(char *str);
char *chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace);
#endif /* _HAPROXY_REGEX_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -0,0 +1,92 @@
/*
include/haproxy/standard.h
This files contains some general purpose functions and macros.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_STANDARD_H
#define _HAPROXY_STANDARD_H
#include <netinet/in.h>
#include <haproxy/defaults.h>
#include <haproxy/compat.h>
/****** string-specific macros and functions ******/
/* if a > max, then bound <a> to <max>. The macro returns the new <a> */
#define UBOUND(a, max) ({ typeof(a) b = (max); if ((a) > b) (a) = b; (a); })
/* if a < min, then bound <a> to <min>. The macro returns the new <a> */
#define LBOUND(a, min) ({ typeof(a) b = (min); if ((a) < b) (a) = b; (a); })
/* returns 1 only if only zero or one bit is set in X, which means that X is a
* power of 2, and 0 otherwise */
#define POWEROF2(x) (((x) & ((x)-1)) == 0)
/*
* copies at most <size-1> chars from <src> to <dst>. Last char is always
* set to 0, unless <size> is 0. The number of chars copied is returned
* (excluding the terminating zero).
* This code has been optimized for size and speed : on x86, it's 45 bytes
* long, uses only registers, and consumes only 4 cycles per char.
*/
extern int strlcpy2(char *dst, const char *src, int size);
/*
* This function simply returns a statically allocated string containing
* the ascii representation for number 'n' in decimal.
*/
extern char *ultoa(unsigned long n);
/*
* Returns non-zero if character <s> is a hex digit (0-9, a-f, A-F), else zero.
*/
extern int ishex(char s);
/*
* converts <str> to a struct sockaddr_in* which is locally allocated.
* The format is "addr:port", where "addr" can be a dotted IPv4 address,
* a host name, or empty or "*" to indicate INADDR_ANY.
*/
struct sockaddr_in *str2sa(char *str);
/*
* converts <str> to a two struct in_addr* which are locally allocated.
* The format is "addr[/mask]", where "addr" cannot be empty, and mask
* is optionnal and either in the dotted or CIDR notation.
* Note: "addr" can also be a hostname. Returns 1 if OK, 0 if error.
*/
int str2net(char *str, struct in_addr *addr, struct in_addr *mask);
/* will try to encode the string <string> replacing all characters tagged in
* <map> with the hexadecimal representation of their ASCII-code (2 digits)
* prefixed by <escape>, and will store the result between <start> (included)
* and <stop> (excluded), and will always terminate the string with a '\0'
* before <stop>. The position of the '\0' is returned if the conversion
* completes. If bytes are missing between <start> and <stop>, then the
* conversion will be incomplete and truncated. If <stop> <= <start>, the '\0'
* cannot even be stored so we return <start> without writing the 0.
* The input string must also be zero-terminated.
*/
extern const char hextab[];
char *encode_string(char *start, char *stop,
const char escape, const fd_set *map,
const char *string);
#endif /* _HAPROXY_STANDARD_H */

View File

@ -0,0 +1,33 @@
/*
include/haproxy/template.h
This file serves as a template for future include files.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_TEMPLATE_H
#define _HAPROXY_TEMPLATE_H
#endif /* _HAPROXY_TEMPLATE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

176
include/haproxy/time.h Normal file
View File

@ -0,0 +1,176 @@
/*
include/haproxy/time.h
Time calculation functions and macros.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_TIME_H
#define _HAPROXY_TIME_H
#include <stdlib.h>
#include <sys/time.h>
#define TIME_ETERNITY -1
/* returns the lowest delay amongst <old> and <new>, and respects TIME_ETERNITY */
#define MINTIME(old, new) (((new)<0)?(old):(((old)<0||(new)<(old))?(new):(old)))
#define SETNOW(a) (*a=now)
extern struct timeval now; /* the current date at any moment */
extern struct timeval start_date; /* the process's start date */
/*
* adds <ms> ms to <from>, set the result to <tv> and returns a pointer <tv>
*/
struct timeval *tv_delayfrom(struct timeval *tv, struct timeval *from, int ms);
/*
* compares <tv1> and <tv2> modulo 1ms: returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2
* Must not be used when either argument is eternity. Use tv_cmp2_ms() for that.
*/
int tv_cmp_ms(struct timeval *tv1, struct timeval *tv2);
/*
* compares <tv1> and <tv2> : returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2,
* considering that 0 is the eternity.
*/
int tv_cmp2(struct timeval *tv1, struct timeval *tv2);
/*
* compares <tv1> and <tv2> modulo 1 ms: returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2,
* considering that 0 is the eternity.
*/
int tv_cmp2_ms(struct timeval *tv1, struct timeval *tv2);
/*
* returns the remaining time between tv1=now and event=tv2
* if tv2 is passed, 0 is returned.
* Returns TIME_ETERNITY if tv2 is eternity.
*/
unsigned long tv_remain2(struct timeval *tv1, struct timeval *tv2);
/* sets <tv> to the current time */
static inline struct timeval *tv_now(struct timeval *tv)
{
if (tv)
gettimeofday(tv, NULL);
return tv;
}
/*
* compares <tv1> and <tv2> : returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2
* Must not be used when either argument is eternity. Use tv_cmp2() for that.
*/
static inline int tv_cmp(struct timeval *tv1, struct timeval *tv2)
{
if (tv1->tv_sec < tv2->tv_sec)
return -1;
else if (tv1->tv_sec > tv2->tv_sec)
return 1;
else if (tv1->tv_usec < tv2->tv_usec)
return -1;
else if (tv1->tv_usec > tv2->tv_usec)
return 1;
else
return 0;
}
/*
* returns the difference, in ms, between tv1 and tv2
* Must not be used when either argument is eternity.
*/
static inline unsigned long tv_diff(struct timeval *tv1, struct timeval *tv2)
{
unsigned long ret;
ret = (tv2->tv_sec - tv1->tv_sec) * 1000;
if (tv2->tv_usec > tv1->tv_usec)
ret += (tv2->tv_usec - tv1->tv_usec) / 1000;
else
ret -= (tv1->tv_usec - tv2->tv_usec) / 1000;
return (unsigned long) ret;
}
/*
* returns the remaining time between tv1=now and event=tv2
* if tv2 is passed, 0 is returned.
* Must not be used when either argument is eternity.
*/
static inline unsigned long tv_remain(struct timeval *tv1, struct timeval *tv2)
{
unsigned long ret;
if (tv_cmp_ms(tv1, tv2) >= 0)
return 0; /* event elapsed */
ret = (tv2->tv_sec - tv1->tv_sec) * 1000;
if (tv2->tv_usec > tv1->tv_usec)
ret += (tv2->tv_usec - tv1->tv_usec) / 1000;
else
ret -= (tv1->tv_usec - tv2->tv_usec) / 1000;
return (unsigned long) ret;
}
/*
* zeroes a struct timeval
*/
static inline struct timeval *tv_eternity(struct timeval *tv)
{
tv->tv_sec = tv->tv_usec = 0;
return tv;
}
/*
* returns 1 if tv is null, else 0
*/
static inline int tv_iseternity(struct timeval *tv)
{
if (tv->tv_sec == 0 && tv->tv_usec == 0)
return 1;
else
return 0;
}
/*
* returns the first event between tv1 and tv2 into tvmin.
* a zero tv is ignored. tvmin is returned.
*/
static inline struct timeval *tv_min(struct timeval *tvmin,
struct timeval *tv1, struct timeval *tv2)
{
if (tv_cmp2(tv1, tv2) <= 0)
*tvmin = *tv1;
else
*tvmin = *tv2;
return tvmin;
}
#endif /* _HAPROXY_TIME_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -1,7 +1,7 @@
/*
* URI-based user authentication using the HTTP basic method.
*
* Copyright 2006 Willy Tarreau <willy@w.ods.org>
* Copyright 2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -10,8 +10,8 @@
*
*/
#ifndef URI_AUTH_H
#define URI_AUTH_H
#ifndef _HAPROXY_URI_AUTH_H
#define _HAPROXY_URI_AUTH_H
/* here we find a very basic list of base64-encoded 'user:passwd' strings */
struct user_auth {
struct user_auth *next; /* next entry, NULL if none */
@ -65,4 +65,4 @@ struct uri_auth *stats_set_realm(struct uri_auth **root, char *realm);
struct uri_auth *stats_add_auth(struct uri_auth **root, char *user);
struct uri_auth *stats_add_scope(struct uri_auth **root, char *scope);
#endif /* URI_AUTH_H */
#endif /* _HAPROXY_URI_AUTH_H */

39
include/haproxy/version.h Normal file
View File

@ -0,0 +1,39 @@
/*
include/haproxy/version.h
This file serves as a template for future include files.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _HAPROXY_VERSION_H
#define _HAPROXY_VERSION_H
#ifdef CONFIG_PRODUCT_NAME
#define PRODUCT_NAME CONFIG_PRODUCT_NAME
#else
#define PRODUCT_NAME "HAProxy"
#endif
#ifndef HAPROXY_VERSION
#define HAPROXY_VERSION "1.3.0"
#endif
#ifndef HAPROXY_DATE
#define HAPROXY_DATE "2006/06/26"
#endif
#endif /* _HAPROXY_VERSION_H */

125
include/proto/backend.h Normal file
View File

@ -0,0 +1,125 @@
/*
include/proto/backend.h
Functions prototypes for the backend.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_BACKEND_H
#define _PROTO_BACKEND_H
#include <types/backend.h>
#include <types/session.h>
#include <proto/queue.h>
int assign_server(struct session *s);
int assign_server_address(struct session *s);
int assign_server_and_queue(struct session *s);
int connect_server(struct session *s);
int srv_count_retry_down(struct session *t, int conn_err);
int srv_retryable_connect(struct session *t);
int srv_redispatch_connect(struct session *t);
void recount_servers(struct proxy *px);
void recalc_server_map(struct proxy *px);
/*
* This function tries to find a running server with free connection slots for
* the proxy <px> following the round-robin method.
* If any server is found, it will be returned and px->srv_rr_idx will be updated
* to point to the next server. If no valid server is found, NULL is returned.
*/
static inline struct server *get_server_rr_with_conns(struct proxy *px)
{
int newidx;
struct server *srv;
if (px->srv_map_sz == 0)
return NULL;
if (px->srv_rr_idx < 0 || px->srv_rr_idx >= px->srv_map_sz)
px->srv_rr_idx = 0;
newidx = px->srv_rr_idx;
do {
srv = px->srv_map[newidx++];
if (!srv->maxconn || srv->cur_sess < srv_dynamic_maxconn(srv)) {
px->srv_rr_idx = newidx;
return srv;
}
if (newidx == px->srv_map_sz)
newidx = 0;
} while (newidx != px->srv_rr_idx);
return NULL;
}
/*
* This function tries to find a running server for the proxy <px> following
* the round-robin method.
* If any server is found, it will be returned and px->srv_rr_idx will be updated
* to point to the next server. If no valid server is found, NULL is returned.
*/
static inline struct server *get_server_rr(struct proxy *px)
{
if (px->srv_map_sz == 0)
return NULL;
if (px->srv_rr_idx < 0 || px->srv_rr_idx >= px->srv_map_sz)
px->srv_rr_idx = 0;
return px->srv_map[px->srv_rr_idx++];
}
/*
* This function tries to find a running server for the proxy <px> following
* the source hash method. Depending on the number of active/backup servers,
* it will either look for active servers, or for backup servers.
* If any server is found, it will be returned. If no valid server is found,
* NULL is returned.
*/
static inline struct server *get_server_sh(struct proxy *px, char *addr, int len)
{
unsigned int h, l;
if (px->srv_map_sz == 0)
return NULL;
l = h = 0;
if (px->srv_act > 1 || (px->srv_act == 0 && px->srv_bck > 1)) {
while ((l + sizeof (int)) <= len) {
h ^= ntohl(*(unsigned int *)(&addr[l]));
l += sizeof (int);
}
h %= px->srv_map_sz;
}
return px->srv_map[h];
}
#endif /* _PROTO_BACKEND_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

85
include/proto/buffers.h Normal file
View File

@ -0,0 +1,85 @@
/*
include/proto/buffers.h
Buffer management definitions, macros and inline functions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_BUFFERS_H
#define _PROTO_BUFFERS_H
#include <haproxy/defaults.h>
#include <types/buffers.h>
/* returns 1 if the buffer is empty, 0 otherwise */
static inline int buffer_isempty(struct buffer *buf)
{
return buf->l == 0;
}
/* returns 1 if the buffer is full, 0 otherwise */
static inline int buffer_isfull(struct buffer *buf) {
return buf->l == BUFSIZE;
}
/* flushes any content from buffer <buf> */
static inline void buffer_flush(struct buffer *buf)
{
buf->r = buf->h = buf->lr = buf->w = buf->data;
buf->l = 0;
}
/* returns the maximum number of bytes writable at once in this buffer */
static inline int buffer_max(struct buffer *buf)
{
if (buf->l == BUFSIZE)
return 0;
else if (buf->r >= buf->w)
return buf->data + BUFSIZE - buf->r;
else
return buf->w - buf->r;
}
/*
* Tries to realign the given buffer, and returns how many bytes can be written
* there at once without overwriting anything.
*/
static inline int buffer_realign(struct buffer *buf)
{
if (buf->l == 0) {
/* let's realign the buffer to optimize I/O */
buf->r = buf->w = buf->h = buf->lr = buf->data;
}
return buffer_max(buf);
}
int buffer_write(struct buffer *buf, const char *msg, int len);
int buffer_replace(struct buffer *b, char *pos, char *end, char *str);
int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len);
#endif /* _PROTO_BUFFERS_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

36
include/proto/checks.h Normal file
View File

@ -0,0 +1,36 @@
/*
include/proto/checks.h
Functions prototypes for the checks.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_CHECKS_H
#define _PROTO_CHECKS_H
#include <types/task.h>
int process_chk(struct task *t);
#endif /* _PROTO_CHECKS_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

38
include/proto/client.h Normal file
View File

@ -0,0 +1,38 @@
/*
include/proto/client.h
This file contains client-side definitions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_CLIENT_H
#define _PROTO_CLIENT_H
#include <types/client.h>
int event_accept(int fd);
#endif /* _PROTO_CLIENT_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

52
include/proto/fd.h Normal file
View File

@ -0,0 +1,52 @@
/*
include/proto/fd.h
File descriptors states.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_FD_H
#define _PROTO_FD_H
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <types/fd.h>
/* Deletes an FD from the fdsets, and recomputes the maxfd limit.
* The file descriptor is also closed.
*/
void fd_delete(int fd);
/* recomputes the maxfd limit from the fd */
static inline void fd_insert(int fd)
{
if (fd + 1 > maxfd)
maxfd = fd + 1;
}
#endif /* _PROTO_FD_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

84
include/proto/log.h Normal file
View File

@ -0,0 +1,84 @@
/*
include/proto/log.h
This file contains definitions of log-related functions, structures,
and macros.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_LOG_H
#define _PROTO_LOG_H
#include <stdio.h>
#include <syslog.h>
#include <types/log.h>
#include <types/proxy.h>
#include <types/session.h>
/*
* Displays the message on stderr with the date and pid. Overrides the quiet
* mode during startup.
*/
void Alert(char *fmt, ...);
/*
* Displays the message on stderr with the date and pid.
*/
void Warning(char *fmt, ...);
/*
* Displays the message on <out> only if quiet mode is not set.
*/
void qfprintf(FILE *out, char *fmt, ...);
/*
* This function sends a syslog message to both log servers of a proxy,
* or to global log servers if the proxy is NULL.
* It also tries not to waste too much time computing the message header.
* It doesn't care about errors nor does it report them.
*/
void send_log(struct proxy *p, int level, char *message, ...);
/*
* send a log for the session when we have enough info about it
*/
void sess_log(struct session *s);
/*
* returns log level for <lev> or -1 if not found.
*/
int get_log_level(const char *lev);
/*
* returns log facility for <fac> or -1 if not found.
*/
int get_log_facility(const char *fac);
/*
* Initializes some data needed later.
*/
void init_log();
#endif /* _PROTO_LOG_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

72
include/proto/polling.h Normal file
View File

@ -0,0 +1,72 @@
/*
include/proto/polling.h
Polling functions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_POLLING_H
#define _PROTO_POLLING_H
#include <types/polling.h>
/*
* Main select() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int select_loop(int action);
#if defined(ENABLE_POLL)
/*
* Main poll() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int poll_loop(int action);
#endif
#if defined(ENABLE_EPOLL)
/*
* Main epoll() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int epoll_loop(int action);
#endif
#endif /* _PROTO_POLLING_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -0,0 +1,49 @@
/*
include/proto/proto_http.h
This file contains HTTP protocol definitions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_PROTO_HTTP_H
#define _PROTO_PROTO_HTTP_H
#include <types/proto_http.h>
#include <types/session.h>
#include <types/task.h>
int event_accept(int fd);
int process_session(struct task *t);
int process_cli(struct session *t);
int process_srv(struct session *t);
void client_retnclose(struct session *s, int len, const char *msg);
void client_return(struct session *s, int len, const char *msg);
void srv_close_with_err(struct session *t, int err, int finst,
int status, int msglen, char *msg);
int produce_content(struct session *s);
#endif /* _PROTO_PROTO_HTTP_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

43
include/proto/proxy.h Normal file
View File

@ -0,0 +1,43 @@
/*
include/proto/proxy.h
This file defines function prototypes for proxy management.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_PROXY_H
#define _PROTO_PROXY_H
#include <types/proxy.h>
int start_proxies(int verbose);
int maintain_proxies(void);
void soft_stop(void);
void pause_proxy(struct proxy *p);
void pause_proxies(void);
void listen_proxies(void);
#endif /* _PROTO_PROXY_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

78
include/proto/queue.h Normal file
View File

@ -0,0 +1,78 @@
/*
include/proto/queue.h
This file defines everything related to queues.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_QUEUE_H
#define _PROTO_QUEUE_H
#include <haproxy/memory.h>
#include <haproxy/mini-clist.h>
#include <types/proxy.h>
#include <types/queue.h>
#include <types/session.h>
#include <types/server.h>
#include <types/task.h>
struct session *pendconn_get_next_sess(struct server *srv, struct proxy *px);
struct pendconn *pendconn_add(struct session *sess);
void pendconn_free(struct pendconn *p);
int process_srv_queue(struct task *t);
unsigned int srv_dynamic_maxconn(struct server *s);
/* Returns the first pending connection for server <s>, which may be NULL if
* nothing is pending.
*/
static inline struct pendconn *pendconn_from_srv(struct server *s) {
if (!s->nbpend)
return NULL;
return LIST_ELEM(s->pendconns.n, struct pendconn *, list);
}
/* Returns the first pending connection for proxy <px>, which may be NULL if
* nothing is pending.
*/
static inline struct pendconn *pendconn_from_px(struct proxy *px) {
if (!px->nbpend)
return NULL;
return LIST_ELEM(px->pendconns.n, struct pendconn *, list);
}
/* returns 0 if nothing has to be done for server <s> regarding queued connections,
* and non-zero otherwise. Suited for and if/else usage.
*/
static inline int may_dequeue_tasks(struct server *s, struct proxy *p) {
return (s && (s->nbpend || p->nbpend) &&
(!s->maxconn || s->cur_sess < srv_dynamic_maxconn(s)) &&
s->queue_mgt);
}
#endif /* _PROTO_QUEUE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

42
include/proto/server.h Normal file
View File

@ -0,0 +1,42 @@
/*
include/proto/server.h
This file defines everything related to servers.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_SERVER_H
#define _PROTO_SERVER_H
#include <unistd.h>
#include <types/proxy.h>
#include <types/queue.h>
#include <types/server.h>
#include <proto/queue.h>
#endif /* _PROTO_SERVER_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

38
include/proto/session.h Normal file
View File

@ -0,0 +1,38 @@
/*
include/proto/session.h
This file defines everything related to sessions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_SESSION_H
#define _PROTO_SESSION_H
#include <types/session.h>
void session_free(struct session *s);
#endif /* _PROTO_SESSION_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -0,0 +1,63 @@
/*
include/proto/stream_sock.h
This file contains client-side definitions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_STREAM_SOCK_H
#define _PROTO_STREAM_SOCK_H
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <haproxy/defaults.h>
#include <haproxy/config.h>
/* FIXME: merge those ones together */
int event_cli_read(int fd);
int event_cli_write(int fd);
int event_srv_read(int fd);
int event_srv_write(int fd);
/* This either returns the sockname or the original destination address. Code
* inspired from Patrick Schaaf's example of nf_getsockname() implementation.
*/
static inline int get_original_dst(int fd, struct sockaddr_in *sa, socklen_t *salen) {
#if defined(TPROXY) && defined(SO_ORIGINAL_DST)
return getsockopt(fd, SOL_IP, SO_ORIGINAL_DST, (void *)sa, salen);
#else
#if defined(TPROXY) && defined(USE_GETSOCKNAME)
return getsockname(fd, (struct sockaddr *)sa, salen);
#else
return -1;
#endif
#endif
}
#endif /* _PROTO_STREAM_SOCK_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

101
include/proto/task.h Normal file
View File

@ -0,0 +1,101 @@
/*
include/proto/task.h
Functions for task management.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_TASK_H
#define _PROTO_TASK_H
#include <sys/time.h>
#include <types/task.h>
#include <haproxy/memory.h>
/* puts the task <t> in run queue <q>, and returns <t> */
static inline struct task *task_wakeup(struct task **q, struct task *t)
{
if (t->state == TASK_RUNNING)
return t;
else {
t->rqnext = *q;
t->state = TASK_RUNNING;
return *q = t;
}
}
/* removes the task <t> from the queue <q>
* <s> MUST be <q>'s first task.
* set the run queue to point to the next one, and return it
*/
static inline struct task *task_sleep(struct task **q, struct task *t)
{
if (t->state == TASK_RUNNING) {
*q = t->rqnext;
t->state = TASK_IDLE; /* tell that s has left the run queue */
}
return *q; /* return next running task */
}
/*
* removes the task <t> from its wait queue. It must have already been removed
* from the run queue. A pointer to the task itself is returned.
*/
static inline struct task *task_delete(struct task *t)
{
t->prev->next = t->next;
t->next->prev = t->prev;
return t;
}
/*
* frees a task. Its context must have been freed since it will be lost.
*/
static inline void task_free(struct task *t)
{
pool_free(task, t);
}
/* inserts <task> into its assigned wait queue, where it may already be. In this case, it
* may be only moved or left where it was, depending on its timing requirements.
* <task> is returned.
*/
struct task *task_queue(struct task *task);
/*
* This does 4 things :
* - wake up all expired tasks
* - call all runnable tasks
* - call maintain_proxies() to enable/disable the listeners
* - return the delay till next event in ms, -1 = wait indefinitely
* Note: this part should be rewritten with the O(ln(n)) scheduler.
*
*/
int process_runnable_tasks();
#endif /* _PROTO_TASK_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

36
include/proto/template.h Normal file
View File

@ -0,0 +1,36 @@
/*
include/proto/template.h
This file serves as a template for future include files.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _PROTO_TEMPLATE_H
#define _PROTO_TEMPLATE_H
#include <types/template.h>
#endif /* _PROTO_TEMPLATE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

60
include/types/backend.h Normal file
View File

@ -0,0 +1,60 @@
/*
include/types/backend.h
This file rassembles definitions for backends
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_BACKEND_H
#define _TYPES_BACKEND_H
/* bits for proxy->options */
#define PR_O_REDISP 0x00000001 /* allow reconnection to dispatch in case of errors */
#define PR_O_TRANSP 0x00000002 /* transparent mode : use original DEST as dispatch */
#define PR_O_COOK_RW 0x00000004 /* rewrite all direct cookies with the right serverid */
#define PR_O_COOK_IND 0x00000008 /* keep only indirect cookies */
#define PR_O_COOK_INS 0x00000010 /* insert cookies when not accessing a server directly */
#define PR_O_COOK_PFX 0x00000020 /* rewrite all cookies by prefixing the right serverid */
#define PR_O_COOK_ANY (PR_O_COOK_RW | PR_O_COOK_IND | PR_O_COOK_INS | PR_O_COOK_PFX)
#define PR_O_BALANCE_RR 0x00000040 /* balance in round-robin mode */
#define PR_O_KEEPALIVE 0x00000080 /* follow keep-alive sessions */
#define PR_O_FWDFOR 0x00000100 /* insert x-forwarded-for with client address */
#define PR_O_BIND_SRC 0x00000200 /* bind to a specific source address when connect()ing */
#define PR_O_NULLNOLOG 0x00000400 /* a connect without request will not be logged */
#define PR_O_COOK_NOC 0x00000800 /* add a 'Cache-control' header with the cookie */
#define PR_O_COOK_POST 0x00001000 /* don't insert cookies for requests other than a POST */
#define PR_O_HTTP_CHK 0x00002000 /* use HTTP 'OPTIONS' method to check server health */
#define PR_O_PERSIST 0x00004000 /* server persistence stays effective even when server is down */
#define PR_O_LOGASAP 0x00008000 /* log as soon as possible, without waiting for the session to complete */
#define PR_O_HTTP_CLOSE 0x00010000 /* force 'connection: close' in both directions */
#define PR_O_CHK_CACHE 0x00020000 /* require examination of cacheability of the 'set-cookie' field */
#define PR_O_TCP_CLI_KA 0x00040000 /* enable TCP keep-alive on client-side sessions */
#define PR_O_TCP_SRV_KA 0x00080000 /* enable TCP keep-alive on server-side sessions */
#define PR_O_USE_ALL_BK 0x00100000 /* load-balance between backup servers */
#define PR_O_FORCE_CLO 0x00200000 /* enforce the connection close immediately after server response */
#define PR_O_BALANCE_SH 0x00400000 /* balance on source IP hash */
#define PR_O_BALANCE (PR_O_BALANCE_RR | PR_O_BALANCE_SH)
#define PR_O_ABRT_CLOSE 0x00800000 /* immediately abort request when client closes */
#endif /* _TYPES_BACKEND_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

53
include/types/buffers.h Normal file
View File

@ -0,0 +1,53 @@
/*
include/types/buffers.h
Buffer management definitions, macros and inline functions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_BUFFERS_H
#define _TYPES_BUFFERS_H
#include <haproxy/defaults.h>
#include <haproxy/memory.h>
/* describes a chunk of string */
struct chunk {
char *str; /* beginning of the string itself. Might not be 0-terminated */
int len; /* size of the string from first to last char. <0 = uninit. */
};
struct buffer {
unsigned int l; /* data length */
char *r, *w, *h, *lr; /* read ptr, write ptr, last header ptr, last read */
char *rlim; /* read limit, used for header rewriting */
unsigned long long total; /* total data read */
char data[BUFSIZE];
};
#define sizeof_buffer sizeof(struct buffer)
extern void **pool_buffer;
#endif /* _TYPES_BUFFERS_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

43
include/types/capture.h Normal file
View File

@ -0,0 +1,43 @@
/*
include/types/capture.h
This file defines everything related to captures.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_CAPTURE_H
#define _TYPES_CAPTURE_H
struct cap_hdr {
struct cap_hdr *next;
char *name; /* header name, case insensitive */
int namelen; /* length of the header name, to speed-up lookups */
int len; /* capture length, not including terminal zero */
int index; /* index in the output array */
void *pool; /* pool of pre-allocated memory area of (len+1) bytes */
};
extern void **pool_capture;
#endif /* _TYPES_CAPTURE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

45
include/types/client.h Normal file
View File

@ -0,0 +1,45 @@
/*
include/types/client.h
This file contains client-side definitions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_CLIENT_H
#define _TYPES_CLIENT_H
/*
* FIXME: break this into HTTP state and TCP socket state.
* See server.h for the other end.
*/
/* different possible states for the client side */
#define CL_STHEADERS 0
#define CL_STDATA 1
#define CL_STSHUTR 2
#define CL_STSHUTW 3
#define CL_STCLOSE 4
#endif /* _TYPES_CLIENT_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

60
include/types/fd.h Normal file
View File

@ -0,0 +1,60 @@
/*
include/fd.h
File descriptors states.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_FD_H
#define _TYPES_FD_H
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <haproxy/config.h>
#include <types/task.h>
/* different possible states for the fd */
#define FD_STCLOSE 0
#define FD_STLISTEN 1
#define FD_STCONN 2
#define FD_STREADY 3
#define FD_STERROR 4
/* info about one given fd */
struct fdtab {
int (*read)(int fd); /* read function */
int (*write)(int fd); /* write function */
struct task *owner; /* the session (or proxy) associated with this fd */
int state; /* the state of this fd */
};
extern struct fdtab *fdtab; /* array of all the file descriptors */
extern int maxfd; /* # of the highest fd + 1 */
extern int totalconn; /* total # of terminated sessions */
extern int actconn; /* # of active sessions */
#endif /* _TYPES_FD_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

75
include/types/global.h Normal file
View File

@ -0,0 +1,75 @@
/*
include/types/global.h
Global variables.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_GLOBAL_H
#define _TYPES_GLOBAL_H
#include <netinet/in.h>
#include <types/task.h>
/* modes of operation (global.mode) */
#define MODE_DEBUG 1
#define MODE_STATS 2
#define MODE_LOG 4
#define MODE_DAEMON 8
#define MODE_QUIET 16
#define MODE_CHECK 32
#define MODE_VERBOSE 64
#define MODE_STARTING 128
#define MODE_FOREGROUND 256
/* FIXME : this will have to be redefined correctly */
struct global {
int uid;
int gid;
int nbproc;
int maxconn;
int maxsock; /* max # of sockets */
int rlimit_nofile; /* default ulimit-n value : 0=unset */
int rlimit_memmax; /* default ulimit-d in megs value : 0=unset */
int mode;
char *chroot;
char *pidfile;
int logfac1, logfac2;
int loglev1, loglev2;
struct sockaddr_in logsrv1, logsrv2;
};
extern struct global global;
extern char *progname; /* program name */
extern int pid; /* current process id */
extern int actconn; /* # of active sessions */
extern int listeners;
extern char trash[BUFSIZE];
extern const int zero;
extern const int one;
extern int stopping; /* non zero means stopping in progress */
#endif /* _TYPES_GLOBAL_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

42
include/types/httperr.h Normal file
View File

@ -0,0 +1,42 @@
/*
include/types/httperr.h
This file defines everything related to HTTP responses and errors.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_HTTPERR_H
#define _TYPES_HTTPERR_H
/* various data sources for the responses */
#define DATA_SRC_NONE 0
#define DATA_SRC_STATS 1
/* data transmission states for the responses */
#define DATA_ST_INIT 0
#define DATA_ST_DATA 1
#endif /* _TYPES_HTTPERR_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

56
include/types/log.h Normal file
View File

@ -0,0 +1,56 @@
/*
include/types/log.h
This file contains definitions of log-related structures and macros.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_LOG_H
#define _TYPES_LOG_H
#define MAX_SYSLOG_LEN 1024
#define NB_LOG_FACILITIES 24
#define NB_LOG_LEVELS 8
#define SYSLOG_PORT 514
/* fields that need to be logged. They appear as flags in session->logs.logwait */
#define LW_DATE 1 /* date */
#define LW_CLIP 2 /* CLient IP */
#define LW_SVIP 4 /* SerVer IP */
#define LW_SVID 8 /* server ID */
#define LW_REQ 16 /* http REQuest */
#define LW_RESP 32 /* http RESPonse */
#define LW_PXIP 64 /* proxy IP */
#define LW_PXID 128 /* proxy ID */
#define LW_BYTES 256 /* bytes read from server */
#define LW_COOKIE 512 /* captured cookie */
#define LW_REQHDR 1024 /* request header(s) */
#define LW_RSPHDR 2048 /* response header(s) */
extern void **pool_requri;
#endif /* _TYPES_LOG_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

74
include/types/polling.h Normal file
View File

@ -0,0 +1,74 @@
/*
include/types/polling.h
File descriptors and polling definitions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_POLLING_H
#define _TYPES_POLLING_H
/* for fd_set */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <haproxy/config.h>
/* for POLL_* */
#if defined(ENABLE_POLL)
#include <sys/poll.h>
#endif
/* for EPOLL_* */
#if defined(ENABLE_EPOLL)
#if !defined(USE_MY_EPOLL)
#include <sys/epoll.h>
#else
#include <haproxy/epoll.h>
#endif
#endif
/* possible actions for the *poll() loops */
#define POLL_LOOP_ACTION_INIT 0
#define POLL_LOOP_ACTION_RUN 1
#define POLL_LOOP_ACTION_CLEAN 2
/* poll mechanisms available */
#define POLL_USE_SELECT (1<<0)
#define POLL_USE_POLL (1<<1)
#define POLL_USE_EPOLL (1<<2)
/* result of an I/O event */
#define RES_SILENT 0 /* didn't happen */
#define RES_DATA 1 /* data were sent or received */
#define RES_NULL 2 /* result is 0 (read == 0), or connect without need for writing */
#define RES_ERROR 3 /* result -1 or error on the socket (eg: connect()) */
/* fd states */
extern fd_set *StaticReadEvent, *StaticWriteEvent;
extern int cfg_polling_mechanism; /* POLL_USE_{SELECT|POLL|EPOLL} */
#endif /* _TYPES_POLLING_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -0,0 +1,60 @@
/*
include/types/proto_http.h
This file contains HTTP protocol definitions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_PROTO_HTTP_H
#define _TYPES_PROTO_HTTP_H
/*
* FIXME: break this into HTTP state and TCP socket state.
* See server.h for the other end.
*/
/* different possible states for the client side */
#define CL_STHEADERS 0
#define CL_STDATA 1
#define CL_STSHUTR 2
#define CL_STSHUTW 3
#define CL_STCLOSE 4
/*
* FIXME: break this into HTTP state and TCP socket state.
* See client.h for the other end.
*/
/* different possible states for the server side */
#define SV_STIDLE 0
#define SV_STCONN 1
#define SV_STHEADERS 2
#define SV_STDATA 3
#define SV_STSHUTR 4
#define SV_STSHUTW 5
#define SV_STCLOSE 6
#endif /* _TYPES_PROTO_HTTP_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

145
include/types/proxy.h Normal file
View File

@ -0,0 +1,145 @@
/*
include/types/proxy.h
This file defines everything related to proxies.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_PROXY_H
#define _TYPES_PROXY_H
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <haproxy/appsession.h>
#include <haproxy/chtbl.h>
#include <haproxy/mini-clist.h>
#include <haproxy/regex.h>
#include <types/buffers.h>
#include <types/session.h>
#include <types/server.h>
/* values for proxy->state */
#define PR_STNEW 0
#define PR_STIDLE 1
#define PR_STRUN 2
#define PR_STSTOPPED 3
#define PR_STPAUSED 4
#define PR_STERROR 5
/* values for proxy->mode */
#define PR_MODE_TCP 0
#define PR_MODE_HTTP 1
#define PR_MODE_HEALTH 2
/* return codes for start_proxies */
#define ERR_NONE 0 /* no error */
#define ERR_RETRYABLE 1 /* retryable error, may be cumulated */
#define ERR_FATAL 2 /* fatal error, may be cumulated */
struct listener {
int fd; /* the listen socket */
struct sockaddr_storage addr; /* the address we listen to */
struct listener *next; /* next address or NULL */
};
struct proxy {
struct listener *listen; /* the listen addresses and sockets */
struct in_addr mon_net, mon_mask; /* don't forward connections from this net (network order) FIXME: should support IPv6 */
int state; /* proxy state */
struct sockaddr_in dispatch_addr; /* the default address to connect to */
struct server *srv; /* known servers */
int srv_act, srv_bck; /* # of running servers */
int tot_wact, tot_wbck; /* total weights of active and backup servers */
struct server **srv_map; /* the server map used to apply weights */
int srv_map_sz; /* the size of the effective server map */
int srv_rr_idx; /* next server to be elected in round robin mode */
char *cookie_name; /* name of the cookie to look for */
int cookie_len; /* strlen(cookie_name), computed only once */
char *appsession_name; /* name of the cookie to look for */
int appsession_name_len; /* strlen(appsession_name), computed only once */
int appsession_len; /* length of the appsession cookie value to be used */
int appsession_timeout;
CHTbl htbl_proxy; /* Per Proxy hashtable */
char *capture_name; /* beginning of the name of the cookie to capture */
int capture_namelen; /* length of the cookie name to match */
int capture_len; /* length of the string to be captured */
struct uri_auth *uri_auth; /* if non-NULL, the (list of) per-URI authentications */
int clitimeout; /* client I/O timeout (in milliseconds) */
int srvtimeout; /* server I/O timeout (in milliseconds) */
int contimeout; /* connect timeout (in milliseconds) */
char *id; /* proxy id */
struct list pendconns; /* pending connections with no server assigned yet */
int nbpend, nbpend_max; /* number of pending connections with no server assigned yet */
int totpend; /* total number of pending connections on this instance (for stats) */
unsigned int nbconn, nbconn_max; /* # of active sessions */
unsigned int cum_conn; /* cumulated number of processed sessions */
unsigned int maxconn; /* max # of active sessions */
unsigned failed_conns, failed_resp; /* failed connect() and responses */
unsigned failed_secu; /* blocked responses because of security concerns */
int conn_retries; /* maximum number of connect retries */
int options; /* PR_O_REDISP, PR_O_TRANSP, ... */
int mode; /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
struct proxy *next;
struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */
signed char logfac1, logfac2; /* log facility for both servers. -1 = disabled */
int loglev1, loglev2; /* log level for each server, 7 by default */
int to_log; /* things to be logged (LW_*) */
struct timeval stop_time; /* date to stop listening, when stopping != 0 */
int nb_reqadd, nb_rspadd;
struct hdr_exp *req_exp; /* regular expressions for request headers */
struct hdr_exp *rsp_exp; /* regular expressions for response headers */
int nb_req_cap, nb_rsp_cap; /* # of headers to be captured */
struct cap_hdr *req_cap; /* chained list of request headers to be captured */
struct cap_hdr *rsp_cap; /* chained list of response headers to be captured */
void *req_cap_pool, *rsp_cap_pool; /* pools of pre-allocated char ** used to build the sessions */
char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */
int grace; /* grace time after stop request */
char *check_req; /* HTTP request to use if PR_O_HTTP_CHK is set, else NULL */
int check_len; /* Length of the HTTP request */
struct {
char *msg400; /* message for error 400 */
int len400; /* message length for error 400 */
char *msg403; /* message for error 403 */
int len403; /* message length for error 403 */
char *msg408; /* message for error 408 */
int len408; /* message length for error 408 */
char *msg500; /* message for error 500 */
int len500; /* message length for error 500 */
char *msg502; /* message for error 502 */
int len502; /* message length for error 502 */
char *msg503; /* message for error 503 */
int len503; /* message length for error 503 */
char *msg504; /* message for error 504 */
int len504; /* message length for error 504 */
} errmsg;
};
extern struct proxy *proxy;
#endif /* _TYPES_PROXY_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

47
include/types/queue.h Normal file
View File

@ -0,0 +1,47 @@
/*
include/types/queue.h
This file defines variables and structures needed for queues.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_QUEUE_H
#define _TYPES_QUEUE_H
#include <haproxy/mini-clist.h>
#include <types/server.h>
#include <types/session.h>
struct pendconn {
struct list list; /* chaining ... */
struct session *sess; /* the session waiting for a connection */
struct server *srv; /* the server we are waiting for */
};
#define sizeof_pendconn sizeof(struct pendconn)
extern void **pool_pendconn;
#endif /* _TYPES_QUEUE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

87
include/types/server.h Normal file
View File

@ -0,0 +1,87 @@
/*
include/types/server.h
This file defines everything related to servers.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_SERVER_H
#define _TYPES_SERVER_H
#include <netinet/in.h>
#include <arpa/inet.h>
#include <haproxy/mini-clist.h>
#include <types/buffers.h>
#include <types/proxy.h>
#include <types/queue.h>
#include <types/task.h>
/* server flags */
#define SRV_RUNNING 1 /* the server is UP */
#define SRV_BACKUP 2 /* this server is a backup server */
#define SRV_MAPPORTS 4 /* this server uses mapped ports */
#define SRV_BIND_SRC 8 /* this server uses a specific source address */
#define SRV_CHECKED 16 /* this server needs to be checked */
/* function which act on servers need to return various errors */
#define SRV_STATUS_OK 0 /* everything is OK. */
#define SRV_STATUS_INTERNAL 1 /* other unrecoverable errors. */
#define SRV_STATUS_NOSRV 2 /* no server is available */
#define SRV_STATUS_FULL 3 /* the/all server(s) are saturated */
#define SRV_STATUS_QUEUED 4 /* the/all server(s) are saturated but the connection was queued */
struct server {
struct server *next;
int state; /* server state (SRV_*) */
int cklen; /* the len of the cookie, to speed up checks */
char *cookie; /* the id set in the cookie */
char *id; /* just for identification */
struct list pendconns; /* pending connections */
int nbpend, nbpend_max; /* number of pending connections */
struct task *queue_mgt; /* the task associated to the queue processing */
struct sockaddr_in addr; /* the address to connect to */
struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
short check_port; /* the port to use for the health checks */
int health; /* 0->rise-1 = bad; rise->rise+fall-1 = good */
int rise, fall; /* time in iterations */
int inter; /* time in milliseconds */
int result; /* 0 = connect OK, -1 = connect KO */
int curfd; /* file desc used for current test, or -1 if not in test */
unsigned char uweight, eweight; /* user-specified weight-1, and effective weight-1 */
unsigned int wscore; /* weight score, used during srv map computation */
int cur_sess, cur_sess_max; /* number of currently active sessions (including syn_sent) */
unsigned int cum_sess; /* cumulated number of sessions really sent to this server */
unsigned int maxconn, minconn; /* max # of active sessions (0 = unlimited), min# for dynamic limit. */
unsigned failed_checks, down_trans; /* failed checks and up-down transitions */
unsigned failed_conns, failed_resp; /* failed connect() and responses */
unsigned failed_secu; /* blocked responses because of security concerns */
struct proxy *proxy; /* the proxy this server belongs to */
};
#endif /* _TYPES_SERVER_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

166
include/types/session.h Normal file
View File

@ -0,0 +1,166 @@
/*
include/types/session.h
This file defines everything related to sessions.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_SESSION_H
#define _TYPES_SESSION_H
#include <sys/time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <haproxy/mini-clist.h>
#include <types/buffers.h>
#include <types/proxy.h>
#include <types/queue.h>
#include <types/server.h>
#include <types/task.h>
/* various session flags, bits values 0x01 to 0x20 (shift 0) */
#define SN_DIRECT 0x00000001 /* connection made on the server matching the client cookie */
#define SN_CLDENY 0x00000002 /* a client header matches a deny regex */
#define SN_CLALLOW 0x00000004 /* a client header matches an allow regex */
#define SN_SVDENY 0x00000008 /* a server header matches a deny regex */
#define SN_SVALLOW 0x00000010 /* a server header matches an allow regex */
#define SN_POST 0x00000020 /* the request was an HTTP POST */
/* session flags dedicated to cookies : bits values 0x40, 0x80 (0-3 shift 6) */
#define SN_CK_NONE 0x00000000 /* this session had no cookie */
#define SN_CK_INVALID 0x00000040 /* this session had a cookie which matches no server */
#define SN_CK_DOWN 0x00000080 /* this session had cookie matching a down server */
#define SN_CK_VALID 0x000000C0 /* this session had cookie matching a valid server */
#define SN_CK_MASK 0x000000C0 /* mask to get this session's cookie flags */
#define SN_CK_SHIFT 6 /* bit shift */
/* session termination conditions, bits values 0x100 to 0x700 (0-7 shift 8) */
#define SN_ERR_NONE 0x00000000
#define SN_ERR_CLITO 0x00000100 /* client time-out */
#define SN_ERR_CLICL 0x00000200 /* client closed (read/write error) */
#define SN_ERR_SRVTO 0x00000300 /* server time-out, connect time-out */
#define SN_ERR_SRVCL 0x00000400 /* server closed (connect/read/write error) */
#define SN_ERR_PRXCOND 0x00000500 /* the proxy decided to close (deny...) */
#define SN_ERR_RESOURCE 0x00000600 /* the proxy encountered a lack of a local resources (fd, mem, ...) */
#define SN_ERR_INTERNAL 0x00000700 /* the proxy encountered an internal error */
#define SN_ERR_MASK 0x00000700 /* mask to get only session error flags */
#define SN_ERR_SHIFT 8 /* bit shift */
/* session state at termination, bits values 0x1000 to 0x7000 (0-7 shift 12) */
#define SN_FINST_R 0x00001000 /* session ended during client request */
#define SN_FINST_C 0x00002000 /* session ended during server connect */
#define SN_FINST_H 0x00003000 /* session ended during server headers */
#define SN_FINST_D 0x00004000 /* session ended during data phase */
#define SN_FINST_L 0x00005000 /* session ended while pushing last data to client */
#define SN_FINST_Q 0x00006000 /* session ended while waiting in queue for a server slot */
#define SN_FINST_MASK 0x00007000 /* mask to get only final session state flags */
#define SN_FINST_SHIFT 12 /* bit shift */
/* cookie information, bits values 0x10000 to 0x80000 (0-8 shift 16) */
#define SN_SCK_NONE 0x00000000 /* no set-cookie seen for the server cookie */
#define SN_SCK_DELETED 0x00010000 /* existing set-cookie deleted or changed */
#define SN_SCK_INSERTED 0x00020000 /* new set-cookie inserted or changed existing one */
#define SN_SCK_SEEN 0x00040000 /* set-cookie seen for the server cookie */
#define SN_SCK_MASK 0x00070000 /* mask to get the set-cookie field */
#define SN_SCK_ANY 0x00080000 /* at least one set-cookie seen (not to be counted) */
#define SN_SCK_SHIFT 16 /* bit shift */
/* cacheability management, bits values 0x100000 to 0x300000 (0-3 shift 20) */
#define SN_CACHEABLE 0x00100000 /* at least part of the response is cacheable */
#define SN_CACHE_COOK 0x00200000 /* a cookie in the response is cacheable */
#define SN_CACHE_SHIFT 20 /* bit shift */
/* various other session flags, bits values 0x400000 and above */
#define SN_MONITOR 0x00400000 /* this session comes from a monitoring system */
#define SN_ASSIGNED 0x00800000 /* no need to assign a server to this session */
#define SN_ADDR_SET 0x01000000 /* this session's server address has been set */
#define SN_SELF_GEN 0x02000000 /* the proxy generates data for the client (eg: stats) */
/* WARNING: if new fields are added, they must be initialized in event_accept() */
struct session {
struct task *task; /* the task associated with this session */
/* application specific below */
struct timeval crexpire; /* expiration date for a client read */
struct timeval cwexpire; /* expiration date for a client write */
struct timeval srexpire; /* expiration date for a server read */
struct timeval swexpire; /* expiration date for a server write */
struct timeval cnexpire; /* expiration date for a connect */
char res_cr, res_cw, res_sr, res_sw; /* results of some events */
struct proxy *proxy; /* the proxy this socket belongs to */
int cli_fd; /* the client side fd */
int srv_fd; /* the server side fd */
int cli_state; /* state of the client side */
int srv_state; /* state of the server side */
int conn_retries; /* number of connect retries left */
int flags; /* some flags describing the session */
struct buffer *req; /* request buffer */
struct buffer *rep; /* response buffer */
struct sockaddr_storage cli_addr; /* the client address */
struct sockaddr_in srv_addr; /* the address to connect to */
struct server *srv; /* the server being used */
struct pendconn *pend_pos; /* if not NULL, points to the position in the pending queue */
char **req_cap; /* array of captured request headers (may be NULL) */
char **rsp_cap; /* array of captured response headers (may be NULL) */
struct chunk req_line; /* points to first line */
struct chunk auth_hdr; /* points to 'Authorization:' header */
struct {
int logwait; /* log fields waiting to be collected : LW_* */
struct timeval tv_accept; /* date of the accept() (beginning of the session) */
long t_request; /* delay before the end of the request arrives, -1 if never occurs */
long t_queue; /* delay before the session gets out of the connect queue, -1 if never occurs */
long t_connect; /* delay before the connect() to the server succeeds, -1 if never occurs */
long t_data; /* delay before the first data byte from the server ... */
unsigned long t_close; /* total session duration */
unsigned long srv_queue_size; /* number of sessions waiting for a connect slot on this server at accept() time (in direct assignment) */
unsigned long prx_queue_size; /* overall number of sessions waiting for a connect slot on this instance at accept() time */
char *uri; /* first line if log needed, NULL otherwise */
char *cli_cookie; /* cookie presented by the client, in capture mode */
char *srv_cookie; /* cookie presented by the server, in capture mode */
int status; /* HTTP status from the server, negative if from proxy */
long long bytes; /* number of bytes transferred from the server */
} logs;
short int data_source; /* where to get the data we generate ourselves */
short int data_state; /* where to get the data we generate ourselves */
union {
struct {
struct proxy *px;
struct server *sv;
short px_st, sv_st; /* DATA_ST_INIT or DATA_ST_DATA */
} stats;
} data_ctx;
unsigned int uniq_id; /* unique ID used for the traces */
};
#define sizeof_session sizeof(struct session)
extern void **pool_session;
#endif /* _TYPES_SESSION_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

58
include/types/task.h Normal file
View File

@ -0,0 +1,58 @@
/*
include/types/task.h
Macros, variables and structures for task management.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, version 2.1
exclusively.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TYPES_TASK_H
#define _TYPES_TASK_H
#include <sys/time.h>
/* values for task->state */
#define TASK_IDLE 0
#define TASK_RUNNING 1
/* The base for all tasks */
struct task {
struct task *next, *prev; /* chaining ... */
struct task *rqnext; /* chaining in run queue ... */
struct task *wq; /* the wait queue this task is in */
int state; /* task state : IDLE or RUNNING */
struct timeval expire; /* next expiration time for this task, use only for fast sorting */
int (*process)(struct task *t); /* the function which processes the task */
void *context; /* the task's context */
};
#define sizeof_task sizeof(struct task)
extern void **pool_task;
extern struct task wait_queue[2];
extern struct task *rq;
#endif /* _TYPES_TASK_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -1,6 +1,7 @@
/*
include/template.h
include/types/template.h
This file serves as a template for future include files.
Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
This library is free software; you can redistribute it and/or
@ -18,8 +19,15 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _TEMPLATE_H
#define _TEMPLATE_H
#ifndef _TYPES_TEMPLATE_H
#define _TYPES_TEMPLATE_H
#endif
#endif /* _TYPES_TEMPLATE_H */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

222
src/appsession.c Normal file
View File

@ -0,0 +1,222 @@
/*
* AppSession functions.
*
* Copyright 2004-2006 Alexander Lazic, Klaus Wagner
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <stdio.h>
#include <haproxy/appsession.h>
#include <haproxy/chtbl.h>
#include <haproxy/list.h>
#include <haproxy/time.h>
#include <types/buffers.h>
#include <types/global.h>
#include <types/proxy.h>
#include <types/server.h>
#include <proto/task.h>
void **pool_appsess = NULL;
struct app_pool apools;
int have_appsession;
#if defined(DEBUG_HASH)
void print_table(const CHTbl *htbl)
{
ListElmt *element;
int i;
appsess *asession;
/*********************************************************************
* *
* Display the chained hash table. *
* *
*********************************************************************/
fprintf(stdout, "Table size is %d\n", chtbl_size(htbl));
for (i = 0; i < TBLSIZ; i++) {
fprintf(stdout, "Bucket[%03d]\n", i);
for (element = list_head(&htbl->table[i]);
element != NULL; element = list_next(element)) {
//fprintf(stdout, "%c", *(char *)list_data(element));
asession = (appsess *)list_data(element);
fprintf(stdout, "ELEM :%s:", asession->sessid);
fprintf(stdout, " Server :%s: \n", asession->serverid);
//fprintf(stdout, " Server request_count :%li:\n",asession->request_count);
}
fprintf(stdout, "\n");
}
return;
} /* end print_table */
#endif
int appsession_init(void)
{
static int initialized = 0;
int idlen;
struct server *s;
struct proxy *p = proxy;
if (!initialized) {
if (!appsession_task_init()) {
apools.sessid = NULL;
apools.serverid = NULL;
apools.ser_waste = 0;
apools.ser_use = 0;
apools.ser_msize = sizeof(void *);
apools.ses_waste = 0;
apools.ses_use = 0;
apools.ses_msize = sizeof(void *);
while (p) {
s = p->srv;
if (apools.ses_msize < p->appsession_len)
apools.ses_msize = p->appsession_len;
while (s) {
idlen = strlen(s->id);
if (apools.ser_msize < idlen)
apools.ser_msize = idlen;
s = s->next;
}
p = p->next;
}
/* we use strings, so reserve space for '\0' */
apools.ser_msize ++;
apools.ses_msize ++;
}
else {
fprintf(stderr, "appsession_task_init failed\n");
return -1;
}
initialized ++;
}
return 0;
}
int appsession_task_init(void)
{
static int initialized = 0;
struct task *t;
if (!initialized) {
if ((t = pool_alloc(task)) == NULL)
return -1;
t->next = t->prev = t->rqnext = NULL;
t->wq = LIST_HEAD(wait_queue[0]);
t->state = TASK_IDLE;
t->context = NULL;
tv_delayfrom(&t->expire, &now, TBLCHKINT);
task_queue(t);
t->process = appsession_refresh;
initialized ++;
}
return 0;
}
int appsession_refresh(struct task *t)
{
struct proxy *p = proxy;
CHTbl *htbl;
ListElmt *element, *last;
int i;
appsess *asession;
void *data;
while (p) {
if (p->appsession_name != NULL) {
htbl = &p->htbl_proxy;
/* if we ever give up the use of TBLSIZ, we need to change this */
for (i = 0; i < TBLSIZ; i++) {
last = NULL;
for (element = list_head(&htbl->table[i]);
element != NULL; element = list_next(element)) {
asession = (appsess *)list_data(element);
if (tv_cmp2_ms(&asession->expire, &now) <= 0) {
if ((global.mode & MODE_DEBUG) &&
(!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
int len;
/*
on Linux NULL pointers are catched by sprintf, on solaris -> segfault
*/
len = sprintf(trash, "appsession_refresh: cleaning up expired Session '%s' on Server %s\n",
asession->sessid, asession->serverid?asession->serverid:"(null)");
write(1, trash, len);
}
/* delete the expired element from within the hash table */
if ((list_rem_next(&htbl->table[i], last, (void **)&data) == 0)
&& (htbl->table[i].destroy != NULL)) {
htbl->table[i].destroy(data);
}
if (last == NULL) {/* patient lost his head, get a new one */
element = list_head(&htbl->table[i]);
if (element == NULL) break; /* no heads left, go to next patient */
}
else
element = last;
}/* end if (tv_cmp2_ms(&asession->expire, &now) <= 0) */
else
last = element;
}/* end for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) */
}
}
p = p->next;
}
tv_delayfrom(&t->expire, &now, TBLCHKINT); /* check expiration every 5 seconds */
return TBLCHKINT;
} /* end appsession_refresh */
int match_str(const void *key1, const void *key2)
{
appsess *temp1,*temp2;
temp1 = (appsess *)key1;
temp2 = (appsess *)key2;
//fprintf(stdout,">>>>>>>>>>>>>>temp1->sessid :%s:\n",temp1->sessid);
//fprintf(stdout,">>>>>>>>>>>>>>temp2->sessid :%s:\n",temp2->sessid);
return (strcmp(temp1->sessid,temp2->sessid) == 0);
}/* end match_str */
void destroy(void *data) {
appsess *temp1;
//printf("destroy called\n");
temp1 = (appsess *)data;
if (temp1->sessid)
pool_free_to(apools.sessid, temp1->sessid);
if (temp1->serverid)
pool_free_to(apools.serverid, temp1->serverid);
pool_free(appsess, temp1);
} /* end destroy */
void appsession_cleanup( void )
{
struct proxy *p = proxy;
while(p) {
chtbl_destroy(&(p->htbl_proxy));
p = p->next;
}
}/* end appsession_cleanup() */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

618
src/backend.c Normal file
View File

@ -0,0 +1,618 @@
/*
* Backend variables and functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <haproxy/compat.h>
#include <haproxy/time.h>
#include <types/buffers.h>
#include <types/global.h>
#include <types/polling.h>
#include <types/proxy.h>
#include <types/server.h>
#include <types/session.h>
#include <proto/backend.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/proto_http.h>
#include <proto/queue.h>
#include <proto/stream_sock.h>
#include <proto/task.h>
/*
* This function recounts the number of usable active and backup servers for
* proxy <p>. These numbers are returned into the p->srv_act and p->srv_bck.
* This function also recomputes the total active and backup weights.
*/
void recount_servers(struct proxy *px)
{
struct server *srv;
px->srv_act = 0; px->srv_bck = px->tot_wact = px->tot_wbck = 0;
for (srv = px->srv; srv != NULL; srv = srv->next) {
if (srv->state & SRV_RUNNING) {
if (srv->state & SRV_BACKUP) {
px->srv_bck++;
px->tot_wbck += srv->eweight + 1;
} else {
px->srv_act++;
px->tot_wact += srv->eweight + 1;
}
}
}
}
/* This function recomputes the server map for proxy px. It
* relies on px->tot_wact and px->tot_wbck, so it must be
* called after recount_servers(). It also expects px->srv_map
* to be initialized to the largest value needed.
*/
void recalc_server_map(struct proxy *px)
{
int o, tot, flag;
struct server *cur, *best;
if (px->srv_act) {
flag = SRV_RUNNING;
tot = px->tot_wact;
} else if (px->srv_bck) {
flag = SRV_RUNNING | SRV_BACKUP;
if (px->options & PR_O_USE_ALL_BK)
tot = px->tot_wbck;
else
tot = 1; /* the first server is enough */
} else {
px->srv_map_sz = 0;
return;
}
/* this algorithm gives priority to the first server, which means that
* it will respect the declaration order for equivalent weights, and
* that whatever the weights, the first server called will always be
* the first declard. This is an important asumption for the backup
* case, where we want the first server only.
*/
for (cur = px->srv; cur; cur = cur->next)
cur->wscore = 0;
for (o = 0; o < tot; o++) {
int max = 0;
best = NULL;
for (cur = px->srv; cur; cur = cur->next) {
if ((cur->state & (SRV_RUNNING | SRV_BACKUP)) == flag) {
int v;
/* If we are forced to return only one server, we don't want to
* go further, because we would return the wrong one due to
* divide overflow.
*/
if (tot == 1) {
best = cur;
break;
}
cur->wscore += cur->eweight + 1;
v = (cur->wscore + tot) / tot; /* result between 0 and 3 */
if (best == NULL || v > max) {
max = v;
best = cur;
}
}
}
px->srv_map[o] = best;
best->wscore -= tot;
}
px->srv_map_sz = tot;
}
/*
* This function marks the session as 'assigned' in direct or dispatch modes,
* or tries to assign one in balance mode, according to the algorithm. It does
* nothing if the session had already been assigned a server.
*
* It may return :
* SRV_STATUS_OK if everything is OK. s->srv will be valid.
* SRV_STATUS_NOSRV if no server is available. s->srv = NULL.
* SRV_STATUS_FULL if all servers are saturated. s->srv = NULL.
* SRV_STATUS_INTERNAL for other unrecoverable errors.
*
* Upon successful return, the session flag SN_ASSIGNED to indicate that it does
* not need to be called anymore. This usually means that s->srv can be trusted
* in balance and direct modes. This flag is not cleared, so it's to the caller
* to clear it if required (eg: redispatch).
*
*/
int assign_server(struct session *s)
{
#ifdef DEBUG_FULL
fprintf(stderr,"assign_server : s=%p\n",s);
#endif
if (s->pend_pos)
return SRV_STATUS_INTERNAL;
if (!(s->flags & SN_ASSIGNED)) {
if ((s->proxy->options & PR_O_BALANCE) && !(s->flags & SN_DIRECT)) {
if (!s->proxy->srv_act && !s->proxy->srv_bck)
return SRV_STATUS_NOSRV;
if (s->proxy->options & PR_O_BALANCE_RR) {
s->srv = get_server_rr_with_conns(s->proxy);
if (!s->srv)
return SRV_STATUS_FULL;
}
else if (s->proxy->options & PR_O_BALANCE_SH) {
int len;
if (s->cli_addr.ss_family == AF_INET)
len = 4;
else if (s->cli_addr.ss_family == AF_INET6)
len = 16;
else /* unknown IP family */
return SRV_STATUS_INTERNAL;
s->srv = get_server_sh(s->proxy,
(void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
len);
}
else /* unknown balancing algorithm */
return SRV_STATUS_INTERNAL;
}
s->flags |= SN_ASSIGNED;
}
return SRV_STATUS_OK;
}
/*
* This function assigns a server address to a session, and sets SN_ADDR_SET.
* The address is taken from the currently assigned server, or from the
* dispatch or transparent address.
*
* It may return :
* SRV_STATUS_OK if everything is OK.
* SRV_STATUS_INTERNAL for other unrecoverable errors.
*
* Upon successful return, the session flag SN_ADDR_SET is set. This flag is
* not cleared, so it's to the caller to clear it if required.
*
*/
int assign_server_address(struct session *s)
{
#ifdef DEBUG_FULL
fprintf(stderr,"assign_server_address : s=%p\n",s);
#endif
if (s->flags & SN_DIRECT || s->proxy->options & PR_O_BALANCE) {
/* A server is necessarily known for this session */
if (!(s->flags & SN_ASSIGNED))
return SRV_STATUS_INTERNAL;
s->srv_addr = s->srv->addr;
/* if this server remaps proxied ports, we'll use
* the port the client connected to with an offset. */
if (s->srv->state & SRV_MAPPORTS) {
struct sockaddr_in sockname;
socklen_t namelen = sizeof(sockname);
if (!(s->proxy->options & PR_O_TRANSP) ||
get_original_dst(s->cli_fd, (struct sockaddr_in *)&sockname, &namelen) == -1)
getsockname(s->cli_fd, (struct sockaddr *)&sockname, &namelen);
s->srv_addr.sin_port = htons(ntohs(s->srv_addr.sin_port) + ntohs(sockname.sin_port));
}
}
else if (*(int *)&s->proxy->dispatch_addr.sin_addr) {
/* connect to the defined dispatch addr */
s->srv_addr = s->proxy->dispatch_addr;
}
else if (s->proxy->options & PR_O_TRANSP) {
/* in transparent mode, use the original dest addr if no dispatch specified */
socklen_t salen = sizeof(s->srv_addr);
if (get_original_dst(s->cli_fd, &s->srv_addr, &salen) == -1) {
qfprintf(stderr, "Cannot get original server address.\n");
return SRV_STATUS_INTERNAL;
}
}
s->flags |= SN_ADDR_SET;
return SRV_STATUS_OK;
}
/* This function assigns a server to session <s> if required, and can add the
* connection to either the assigned server's queue or to the proxy's queue.
*
* Returns :
*
* SRV_STATUS_OK if everything is OK.
* SRV_STATUS_NOSRV if no server is available. s->srv = NULL.
* SRV_STATUS_QUEUED if the connection has been queued.
* SRV_STATUS_FULL if the server(s) is/are saturated and the
* connection could not be queued.
* SRV_STATUS_INTERNAL for other unrecoverable errors.
*
*/
int assign_server_and_queue(struct session *s)
{
struct pendconn *p;
int err;
if (s->pend_pos)
return SRV_STATUS_INTERNAL;
if (s->flags & SN_ASSIGNED) {
/* a server does not need to be assigned, perhaps because we're in
* direct mode, or in dispatch or transparent modes where the server
* is not needed.
*/
if (s->srv &&
s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) {
p = pendconn_add(s);
if (p)
return SRV_STATUS_QUEUED;
else
return SRV_STATUS_FULL;
}
return SRV_STATUS_OK;
}
/* a server needs to be assigned */
err = assign_server(s);
switch (err) {
case SRV_STATUS_OK:
/* in balance mode, we might have servers with connection limits */
if (s->srv &&
s->srv->maxconn && s->srv->cur_sess >= srv_dynamic_maxconn(s->srv)) {
p = pendconn_add(s);
if (p)
return SRV_STATUS_QUEUED;
else
return SRV_STATUS_FULL;
}
return SRV_STATUS_OK;
case SRV_STATUS_FULL:
/* queue this session into the proxy's queue */
p = pendconn_add(s);
if (p)
return SRV_STATUS_QUEUED;
else
return SRV_STATUS_FULL;
case SRV_STATUS_NOSRV:
case SRV_STATUS_INTERNAL:
return err;
default:
return SRV_STATUS_INTERNAL;
}
}
/*
* This function initiates a connection to the server assigned to this session
* (s->srv, s->srv_addr). It will assign a server if none is assigned yet.
* It can return one of :
* - SN_ERR_NONE if everything's OK
* - SN_ERR_SRVTO if there are no more servers
* - SN_ERR_SRVCL if the connection was refused by the server
* - SN_ERR_PRXCOND if the connection has been limited by the proxy (maxconn)
* - SN_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
* - SN_ERR_INTERNAL for any other purely internal errors
* Additionnally, in the case of SN_ERR_RESOURCE, an emergency log will be emitted.
*/
int connect_server(struct session *s)
{
int fd, err;
if (!(s->flags & SN_ADDR_SET)) {
err = assign_server_address(s);
if (err != SRV_STATUS_OK)
return SN_ERR_INTERNAL;
}
if ((fd = s->srv_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
qfprintf(stderr, "Cannot get a server socket.\n");
if (errno == ENFILE)
send_log(s->proxy, LOG_EMERG,
"Proxy %s reached system FD limit at %d. Please check system tunables.\n",
s->proxy->id, maxfd);
else if (errno == EMFILE)
send_log(s->proxy, LOG_EMERG,
"Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n",
s->proxy->id, maxfd);
else if (errno == ENOBUFS || errno == ENOMEM)
send_log(s->proxy, LOG_EMERG,
"Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
s->proxy->id, maxfd);
/* this is a resource error */
return SN_ERR_RESOURCE;
}
if (fd >= global.maxsock) {
/* do not log anything there, it's a normal condition when this option
* is used to serialize connections to a server !
*/
Alert("socket(): not enough free sockets. Raise -n argument. Giving up.\n");
close(fd);
return SN_ERR_PRXCOND; /* it is a configuration limit */
}
if ((fcntl(fd, F_SETFL, O_NONBLOCK)==-1) ||
(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) == -1)) {
qfprintf(stderr,"Cannot set client socket to non blocking mode.\n");
close(fd);
return SN_ERR_INTERNAL;
}
if (s->proxy->options & PR_O_TCP_SRV_KA)
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
/* allow specific binding :
* - server-specific at first
* - proxy-specific next
*/
if (s->srv != NULL && s->srv->state & SRV_BIND_SRC) {
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
if (bind(fd, (struct sockaddr *)&s->srv->source_addr, sizeof(s->srv->source_addr)) == -1) {
Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
s->proxy->id, s->srv->id);
close(fd);
send_log(s->proxy, LOG_EMERG,
"Cannot bind to source address before connect() for server %s/%s.\n",
s->proxy->id, s->srv->id);
return SN_ERR_RESOURCE;
}
}
else if (s->proxy->options & PR_O_BIND_SRC) {
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
if (bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n", s->proxy->id);
close(fd);
send_log(s->proxy, LOG_EMERG,
"Cannot bind to source address before connect() for server %s/%s.\n",
s->proxy->id, s->srv->id);
return SN_ERR_RESOURCE;
}
}
if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) &&
(errno != EINPROGRESS) && (errno != EALREADY) && (errno != EISCONN)) {
if (errno == EAGAIN || errno == EADDRINUSE) {
char *msg;
if (errno == EAGAIN) /* no free ports left, try again later */
msg = "no free ports";
else
msg = "local address already in use";
qfprintf(stderr,"Cannot connect: %s.\n",msg);
close(fd);
send_log(s->proxy, LOG_EMERG,
"Connect() failed for server %s/%s: %s.\n",
s->proxy->id, s->srv->id, msg);
return SN_ERR_RESOURCE;
} else if (errno == ETIMEDOUT) {
//qfprintf(stderr,"Connect(): ETIMEDOUT");
close(fd);
return SN_ERR_SRVTO;
} else {
// (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EACCES || errno == EPERM)
//qfprintf(stderr,"Connect(): %d", errno);
close(fd);
return SN_ERR_SRVCL;
}
}
fdtab[fd].owner = s->task;
fdtab[fd].read = &event_srv_read;
fdtab[fd].write = &event_srv_write;
fdtab[fd].state = FD_STCONN; /* connection in progress */
FD_SET(fd, StaticWriteEvent); /* for connect status */
#if defined(DEBUG_FULL) && defined(ENABLE_EPOLL)
if (PrevReadEvent) {
assert(!(FD_ISSET(fd, PrevReadEvent)));
assert(!(FD_ISSET(fd, PrevWriteEvent)));
}
#endif
fd_insert(fd);
if (s->srv) {
s->srv->cur_sess++;
if (s->srv->cur_sess > s->srv->cur_sess_max)
s->srv->cur_sess_max = s->srv->cur_sess;
}
if (s->proxy->contimeout)
tv_delayfrom(&s->cnexpire, &now, s->proxy->contimeout);
else
tv_eternity(&s->cnexpire);
return SN_ERR_NONE; /* connection is OK */
}
/*
* This function checks the retry count during the connect() job.
* It updates the session's srv_state and retries, so that the caller knows
* what it has to do. It uses the last connection error to set the log when
* it expires. It returns 1 when it has expired, and 0 otherwise.
*/
int srv_count_retry_down(struct session *t, int conn_err)
{
/* we are in front of a retryable error */
t->conn_retries--;
if (t->conn_retries < 0) {
/* if not retryable anymore, let's abort */
tv_eternity(&t->cnexpire);
srv_close_with_err(t, conn_err, SN_FINST_C,
503, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
if (t->srv)
t->srv->failed_conns++;
t->proxy->failed_conns++;
/* We used to have a free connection slot. Since we'll never use it,
* we have to inform the server that it may be used by another session.
*/
if (may_dequeue_tasks(t->srv, t->proxy))
task_wakeup(&rq, t->srv->queue_mgt);
return 1;
}
return 0;
}
/*
* This function performs the retryable part of the connect() job.
* It updates the session's srv_state and retries, so that the caller knows
* what it has to do. It returns 1 when it breaks out of the loop, or 0 if
* it needs to redispatch.
*/
int srv_retryable_connect(struct session *t)
{
int conn_err;
/* This loop ensures that we stop before the last retry in case of a
* redispatchable server.
*/
do {
/* initiate a connection to the server */
conn_err = connect_server(t);
switch (conn_err) {
case SN_ERR_NONE:
//fprintf(stderr,"0: c=%d, s=%d\n", c, s);
t->srv_state = SV_STCONN;
return 1;
case SN_ERR_INTERNAL:
tv_eternity(&t->cnexpire);
srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
500, t->proxy->errmsg.len500, t->proxy->errmsg.msg500);
if (t->srv)
t->srv->failed_conns++;
t->proxy->failed_conns++;
/* release other sessions waiting for this server */
if (may_dequeue_tasks(t->srv, t->proxy))
task_wakeup(&rq, t->srv->queue_mgt);
return 1;
}
/* ensure that we have enough retries left */
if (srv_count_retry_down(t, conn_err)) {
/* let's try to offer this slot to anybody */
if (may_dequeue_tasks(t->srv, t->proxy))
task_wakeup(&rq, t->srv->queue_mgt);
return 1;
}
} while (t->srv == NULL || t->conn_retries > 0 || !(t->proxy->options & PR_O_REDISP));
/* We're on our last chance, and the REDISP option was specified.
* We will ignore cookie and force to balance or use the dispatcher.
*/
/* let's try to offer this slot to anybody */
if (may_dequeue_tasks(t->srv, t->proxy))
task_wakeup(&rq, t->srv->queue_mgt);
if (t->srv)
t->srv->failed_conns++;
t->proxy->failed_conns++;
t->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
t->srv = NULL; /* it's left to the dispatcher to choose a server */
if ((t->flags & SN_CK_MASK) == SN_CK_VALID) {
t->flags &= ~SN_CK_MASK;
t->flags |= SN_CK_DOWN;
}
return 0;
}
/* This function performs the "redispatch" part of a connection attempt. It
* will assign a server if required, queue the connection if required, and
* handle errors that might arise at this level. It can change the server
* state. It will return 1 if it encounters an error, switches the server
* state, or has to queue a connection. Otherwise, it will return 0 indicating
* that the connection is ready to use.
*/
int srv_redispatch_connect(struct session *t)
{
int conn_err;
/* We know that we don't have any connection pending, so we will
* try to get a new one, and wait in this state if it's queued
*/
conn_err = assign_server_and_queue(t);
switch (conn_err) {
case SRV_STATUS_OK:
break;
case SRV_STATUS_NOSRV:
/* note: it is guaranteed that t->srv == NULL here */
tv_eternity(&t->cnexpire);
srv_close_with_err(t, SN_ERR_SRVTO, SN_FINST_C,
503, t->proxy->errmsg.len503, t->proxy->errmsg.msg503);
if (t->srv)
t->srv->failed_conns++;
t->proxy->failed_conns++;
return 1;
case SRV_STATUS_QUEUED:
/* FIXME-20060503 : we should use the queue timeout instead */
if (t->proxy->contimeout)
tv_delayfrom(&t->cnexpire, &now, t->proxy->contimeout);
else
tv_eternity(&t->cnexpire);
t->srv_state = SV_STIDLE;
/* do nothing else and do not wake any other session up */
return 1;
case SRV_STATUS_FULL:
case SRV_STATUS_INTERNAL:
default:
tv_eternity(&t->cnexpire);
srv_close_with_err(t, SN_ERR_INTERNAL, SN_FINST_C,
500, t->proxy->errmsg.len500, t->proxy->errmsg.msg500);
if (t->srv)
t->srv->failed_conns++;
t->proxy->failed_conns++;
/* release other sessions waiting for this server */
if (may_dequeue_tasks(t->srv, t->proxy))
task_wakeup(&rq, t->srv->queue_mgt);
return 1;
}
/* if we get here, it's because we got SRV_STATUS_OK, which also
* means that the connection has not been queued.
*/
return 0;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -1,6 +1,7 @@
/*
* Ascii to Base64 conversion as described in RFC1421.
* Copyright 2006 Willy Tarreau <willy@w.ods.org>
*
* Copyright 2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -9,8 +10,9 @@
*
*/
#include <include/base64.h>
#include <haproxy/base64.h>
const char base64tab[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* Encodes <ilen> bytes from <in> to <out> for at most <olen> chars (including
* the trailing zero). Returns the number of bytes written. No check is made
@ -19,7 +21,6 @@
*/
int a2base64(char *in, int ilen, char *out, int olen)
{
char base64[64]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int convlen;
convlen = ((ilen + 2) / 3) * 4;
@ -29,10 +30,10 @@ int a2base64(char *in, int ilen, char *out, int olen)
/* we don't need to check olen anymore */
while (ilen >= 3) {
out[0] = base64[(((unsigned char)in[0]) >> 2)];
out[1] = base64[(((unsigned char)in[0] & 0x03) << 4) | (((unsigned char)in[1]) >> 4)];
out[2] = base64[(((unsigned char)in[1] & 0x0F) << 2) | (((unsigned char)in[2]) >> 6)];
out[3] = base64[(((unsigned char)in[2] & 0x3F))];
out[0] = base64tab[(((unsigned char)in[0]) >> 2)];
out[1] = base64tab[(((unsigned char)in[0] & 0x03) << 4) | (((unsigned char)in[1]) >> 4)];
out[2] = base64tab[(((unsigned char)in[1] & 0x0F) << 2) | (((unsigned char)in[2]) >> 6)];
out[3] = base64tab[(((unsigned char)in[2] & 0x3F))];
out += 4;
in += 3; ilen -= 3;
}
@ -40,14 +41,14 @@ int a2base64(char *in, int ilen, char *out, int olen)
if (!ilen) {
out[0] = '\0';
} else {
out[0] = base64[((unsigned char)in[0]) >> 2];
out[0] = base64tab[((unsigned char)in[0]) >> 2];
if (ilen == 1) {
out[1] = base64[((unsigned char)in[0] & 0x03) << 4];
out[1] = base64tab[((unsigned char)in[0] & 0x03) << 4];
out[2] = '=';
} else {
out[1] = base64[(((unsigned char)in[0] & 0x03) << 4) |
out[1] = base64tab[(((unsigned char)in[0] & 0x03) << 4) |
(((unsigned char)in[1]) >> 4)];
out[2] = base64[((unsigned char)in[1] & 0x0F) << 2];
out[2] = base64tab[((unsigned char)in[1] & 0x0F) << 2];
}
out[3] = '=';
out[4] = '\0';

116
src/buffers.c Normal file
View File

@ -0,0 +1,116 @@
/*
* Buffer management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <string.h>
#include <proto/buffers.h>
void **pool_buffer = NULL;
/* writes <len> bytes from message <msg> to buffer <buf>. Returns 0 in case of
* success, or the number of bytes available otherwise.
* FIXME-20060521: handle unaligned data.
*/
int buffer_write(struct buffer *buf, const char *msg, int len)
{
int max;
max = buffer_realign(buf);
if (len > max)
return max;
memcpy(buf->r, msg, len);
buf->l += len;
buf->r += len;
if (buf->r == buf->data + BUFSIZE)
buf->r = buf->data;
return 0;
}
/*
* this function writes the string <str> at position <pos> which must be in buffer <b>,
* and moves <end> just after the end of <str>.
* <b>'s parameters (l, r, w, h, lr) are recomputed to be valid after the shift.
* the shift value (positive or negative) is returned.
* If there's no space left, the move is not done.
*
*/
int buffer_replace(struct buffer *b, char *pos, char *end, char *str)
{
int delta;
int len;
len = strlen(str);
delta = len - (end - pos);
if (delta + b->r >= b->data + BUFSIZE)
return 0; /* no space left */
/* first, protect the end of the buffer */
memmove(end + delta, end, b->data + b->l - end);
/* now, copy str over pos */
memcpy(pos, str,len);
/* we only move data after the displaced zone */
if (b->r > pos) b->r += delta;
if (b->w > pos) b->w += delta;
if (b->h > pos) b->h += delta;
if (b->lr > pos) b->lr += delta;
b->l += delta;
return delta;
}
/*
* same except that the string length is given, which allows str to be NULL if
* len is 0.
*/
int buffer_replace2(struct buffer *b, char *pos, char *end, char *str, int len)
{
int delta;
delta = len - (end - pos);
if (delta + b->r >= b->data + BUFSIZE)
return 0; /* no space left */
if (b->data + b->l < end) {
/* The data has been stolen, we could have crashed.
* Maybe we should abort() ? */
return 0;
}
/* first, protect the end of the buffer */
memmove(end + delta, end, b->data + b->l - end);
/* now, copy str over pos */
if (len)
memcpy(pos, str, len);
/* we only move data after the displaced zone */
if (b->r > pos) b->r += delta;
if (b->w > pos) b->w += delta;
if (b->h > pos) b->h += delta;
if (b->lr > pos) b->lr += delta;
b->l += delta;
return delta;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

24
src/capture.c Normal file
View File

@ -0,0 +1,24 @@
/*
* Capture variables and functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <stdlib.h>
#include <types/capture.h>
void **pool_capture = NULL;
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

2058
src/cfgparse.c Normal file

File diff suppressed because it is too large Load Diff

393
src/checks.c Normal file
View File

@ -0,0 +1,393 @@
/*
* Health-checks functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <haproxy/compat.h>
#include <haproxy/config.h>
#include <haproxy/mini-clist.h>
#include <haproxy/time.h>
#include <types/global.h>
#include <types/polling.h>
#include <types/proxy.h>
#include <types/session.h>
#include <proto/backend.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/queue.h>
#include <proto/server.h>
#include <proto/task.h>
/* Sets server <s> down, notifies by all available means, recounts the
* remaining servers on the proxy and transfers queued sessions whenever
* possible to other servers.
*/
void set_server_down(struct server *s)
{
struct pendconn *pc, *pc_bck, *pc_end;
struct session *sess;
int xferred;
s->state &= ~SRV_RUNNING;
if (s->health == s->rise) {
recount_servers(s->proxy);
recalc_server_map(s->proxy);
/* we might have sessions queued on this server and waiting for
* a connection. Those which are redispatchable will be queued
* to another server or to the proxy itself.
*/
xferred = 0;
FOREACH_ITEM_SAFE(pc, pc_bck, &s->pendconns, pc_end, struct pendconn *, list) {
sess = pc->sess;
if ((sess->proxy->options & PR_O_REDISP)) {
/* The REDISP option was specified. We will ignore
* cookie and force to balance or use the dispatcher.
*/
sess->flags &= ~(SN_DIRECT | SN_ASSIGNED | SN_ADDR_SET);
sess->srv = NULL; /* it's left to the dispatcher to choose a server */
if ((sess->flags & SN_CK_MASK) == SN_CK_VALID) {
sess->flags &= ~SN_CK_MASK;
sess->flags |= SN_CK_DOWN;
}
pendconn_free(pc);
task_wakeup(&rq, sess->task);
xferred++;
}
}
sprintf(trash, "%sServer %s/%s is DOWN. %d active and %d backup servers left.%s"
" %d sessions active, %d requeued, %d remaining in queue.\n",
s->state & SRV_BACKUP ? "Backup " : "",
s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
s->cur_sess, xferred, s->nbpend);
Warning("%s", trash);
send_log(s->proxy, LOG_ALERT, "%s", trash);
if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) {
Alert("Proxy %s has no server available !\n", s->proxy->id);
send_log(s->proxy, LOG_EMERG, "Proxy %s has no server available !\n", s->proxy->id);
}
s->down_trans++;
}
s->health = 0; /* failure */
}
/*
* This function is used only for server health-checks. It handles
* the connection acknowledgement. If the proxy requires HTTP health-checks,
* it sends the request. In other cases, it returns 1 if the socket is OK,
* or -1 if an error occured.
*/
int event_srv_chk_w(int fd)
{
struct task *t = fdtab[fd].owner;
struct server *s = t->context;
int skerr;
socklen_t lskerr = sizeof(skerr);
skerr = 1;
if ((getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == -1)
|| (skerr != 0)) {
/* in case of TCP only, this tells us if the connection failed */
s->result = -1;
fdtab[fd].state = FD_STERROR;
FD_CLR(fd, StaticWriteEvent);
}
else if (s->result != -1) {
/* we don't want to mark 'UP' a server on which we detected an error earlier */
if (s->proxy->options & PR_O_HTTP_CHK) {
int ret;
/* we want to check if this host replies to "OPTIONS / HTTP/1.0"
* so we'll send the request, and won't wake the checker up now.
*/
#ifndef MSG_NOSIGNAL
ret = send(fd, s->proxy->check_req, s->proxy->check_len, MSG_DONTWAIT);
#else
ret = send(fd, s->proxy->check_req, s->proxy->check_len, MSG_DONTWAIT | MSG_NOSIGNAL);
#endif
if (ret == s->proxy->check_len) {
FD_SET(fd, StaticReadEvent); /* prepare for reading reply */
FD_CLR(fd, StaticWriteEvent); /* nothing more to write */
return 0;
}
else {
s->result = -1;
FD_CLR(fd, StaticWriteEvent);
}
}
else {
/* good TCP connection is enough */
s->result = 1;
}
}
task_wakeup(&rq, t);
return 0;
}
/*
* This function is used only for server health-checks. It handles
* the server's reply to an HTTP request. It returns 1 if the server replies
* 2xx or 3xx (valid responses), or -1 in other cases.
*/
int event_srv_chk_r(int fd)
{
char reply[64];
int len, result;
struct task *t = fdtab[fd].owner;
struct server *s = t->context;
int skerr;
socklen_t lskerr = sizeof(skerr);
result = len = -1;
getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
if (!skerr) {
#ifndef MSG_NOSIGNAL
len = recv(fd, reply, sizeof(reply), 0);
#else
/* Warning! Linux returns EAGAIN on SO_ERROR if data are still available
* but the connection was closed on the remote end. Fortunately, recv still
* works correctly and we don't need to do the getsockopt() on linux.
*/
len = recv(fd, reply, sizeof(reply), MSG_NOSIGNAL);
#endif
if ((len >= sizeof("HTTP/1.0 000")) &&
!memcmp(reply, "HTTP/1.", 7) &&
(reply[9] == '2' || reply[9] == '3')) /* 2xx or 3xx */
result = 1;
}
if (result == -1)
fdtab[fd].state = FD_STERROR;
if (s->result != -1)
s->result = result;
FD_CLR(fd, StaticReadEvent);
task_wakeup(&rq, t);
return 0;
}
/*
* manages a server health-check. Returns
* the time the task accepts to wait, or TIME_ETERNITY for infinity.
*/
int process_chk(struct task *t)
{
struct server *s = t->context;
struct sockaddr_in sa;
int fd;
//fprintf(stderr, "process_chk: task=%p\n", t);
new_chk:
fd = s->curfd;
if (fd < 0) { /* no check currently running */
//fprintf(stderr, "process_chk: 2\n");
if (tv_cmp2_ms(&t->expire, &now) > 0) { /* not good time yet */
task_queue(t); /* restore t to its place in the task list */
return tv_remain2(&now, &t->expire);
}
/* we don't send any health-checks when the proxy is stopped or when
* the server should not be checked.
*/
if (!(s->state & SRV_CHECKED) || s->proxy->state == PR_STSTOPPED) {
while (tv_cmp2_ms(&t->expire, &now) <= 0)
tv_delayfrom(&t->expire, &t->expire, s->inter);
task_queue(t); /* restore t to its place in the task list */
return tv_remain2(&now, &t->expire);
}
/* we'll initiate a new check */
s->result = 0; /* no result yet */
if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1) {
if ((fd < global.maxsock) &&
(fcntl(fd, F_SETFL, O_NONBLOCK) != -1) &&
(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &one, sizeof(one)) != -1)) {
//fprintf(stderr, "process_chk: 3\n");
/* we'll connect to the check port on the server */
sa = s->addr;
sa.sin_port = htons(s->check_port);
/* allow specific binding :
* - server-specific at first
* - proxy-specific next
*/
if (s->state & SRV_BIND_SRC) {
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
if (bind(fd, (struct sockaddr *)&s->source_addr, sizeof(s->source_addr)) == -1) {
Alert("Cannot bind to source address before connect() for server %s/%s. Aborting.\n",
s->proxy->id, s->id);
s->result = -1;
}
}
else if (s->proxy->options & PR_O_BIND_SRC) {
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
if (bind(fd, (struct sockaddr *)&s->proxy->source_addr, sizeof(s->proxy->source_addr)) == -1) {
Alert("Cannot bind to source address before connect() for proxy %s. Aborting.\n",
s->proxy->id);
s->result = -1;
}
}
if (!s->result) {
if ((connect(fd, (struct sockaddr *)&sa, sizeof(sa)) != -1) || (errno == EINPROGRESS)) {
/* OK, connection in progress or established */
//fprintf(stderr, "process_chk: 4\n");
s->curfd = fd; /* that's how we know a test is in progress ;-) */
fdtab[fd].owner = t;
fdtab[fd].read = &event_srv_chk_r;
fdtab[fd].write = &event_srv_chk_w;
fdtab[fd].state = FD_STCONN; /* connection in progress */
FD_SET(fd, StaticWriteEvent); /* for connect status */
#ifdef DEBUG_FULL
assert (!FD_ISSET(fd, StaticReadEvent));
#endif
fd_insert(fd);
/* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
tv_delayfrom(&t->expire, &now, s->inter);
task_queue(t); /* restore t to its place in the task list */
return tv_remain(&now, &t->expire);
}
else if (errno != EALREADY && errno != EISCONN && errno != EAGAIN) {
s->result = -1; /* a real error */
}
}
}
close(fd); /* socket creation error */
}
if (!s->result) { /* nothing done */
//fprintf(stderr, "process_chk: 6\n");
while (tv_cmp2_ms(&t->expire, &now) <= 0)
tv_delayfrom(&t->expire, &t->expire, s->inter);
goto new_chk; /* may be we should initialize a new check */
}
/* here, we have seen a failure */
if (s->health > s->rise) {
s->health--; /* still good */
s->failed_checks++;
}
else
set_server_down(s);
//fprintf(stderr, "process_chk: 7\n");
/* FIXME: we allow up to <inter> for a connection to establish, but we should use another parameter */
while (tv_cmp2_ms(&t->expire, &now) <= 0)
tv_delayfrom(&t->expire, &t->expire, s->inter);
goto new_chk;
}
else {
//fprintf(stderr, "process_chk: 8\n");
/* there was a test running */
if (s->result > 0) { /* good server detected */
//fprintf(stderr, "process_chk: 9\n");
s->health++; /* was bad, stays for a while */
if (s->health >= s->rise) {
s->state |= SRV_RUNNING;
if (s->health == s->rise) {
int xferred;
recount_servers(s->proxy);
recalc_server_map(s->proxy);
/* check if we can handle some connections queued at the proxy. We
* will take as many as we can handle.
*/
for (xferred = 0; !s->maxconn || xferred < srv_dynamic_maxconn(s); xferred++) {
struct session *sess;
struct pendconn *p;
p = pendconn_from_px(s->proxy);
if (!p)
break;
p->sess->srv = s;
sess = p->sess;
pendconn_free(p);
task_wakeup(&rq, sess->task);
}
sprintf(trash,
"%sServer %s/%s is UP. %d active and %d backup servers online.%s"
" %d sessions requeued, %d total in queue.\n",
s->state & SRV_BACKUP ? "Backup " : "",
s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
xferred, s->nbpend);
Warning("%s", trash);
send_log(s->proxy, LOG_NOTICE, "%s", trash);
}
s->health = s->rise + s->fall - 1; /* OK now */
}
s->curfd = -1; /* no check running anymore */
//FD_CLR(fd, StaticWriteEvent);
fd_delete(fd);
while (tv_cmp2_ms(&t->expire, &now) <= 0)
tv_delayfrom(&t->expire, &t->expire, s->inter);
goto new_chk;
}
else if (s->result < 0 || tv_cmp2_ms(&t->expire, &now) <= 0) {
//fprintf(stderr, "process_chk: 10\n");
/* failure or timeout detected */
if (s->health > s->rise) {
s->health--; /* still good */
s->failed_checks++;
}
else
set_server_down(s);
s->curfd = -1;
//FD_CLR(fd, StaticWriteEvent);
fd_delete(fd);
while (tv_cmp2_ms(&t->expire, &now) <= 0)
tv_delayfrom(&t->expire, &t->expire, s->inter);
goto new_chk;
}
/* if result is 0 and there's no timeout, we have to wait again */
}
//fprintf(stderr, "process_chk: 11\n");
s->result = 0;
task_queue(t); /* restore t to its place in the task list */
return tv_remain2(&now, &t->expire);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -18,8 +18,8 @@
#include <stdlib.h>
#include <string.h>
#include <include/list.h>
#include <include/chtbl.h>
#include <haproxy/list.h>
#include <haproxy/chtbl.h>
/*****************************************************************************
* *
@ -30,47 +30,47 @@
int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int
(*match)(const void *key1, const void *key2), void (*destroy)(void*data)) {
int i;
int i;
/*****************************************************************************
* *
* Allocate space for the hash table. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Allocate space for the hash table. *
* *
*****************************************************************************/
if ((htbl->table = (List *)malloc(buckets * sizeof(List))) == NULL)
return -1;
if ((htbl->table = (List *)malloc(buckets * sizeof(List))) == NULL)
return -1;
/*****************************************************************************
* *
* Initialize the buckets. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Initialize the buckets. *
* *
*****************************************************************************/
htbl->buckets = buckets;
htbl->buckets = buckets;
for (i = 0; i < htbl->buckets; i++)
list_init(&htbl->table[i], destroy);
for (i = 0; i < htbl->buckets; i++)
list_init(&htbl->table[i], destroy);
/*****************************************************************************
* *
* Encapsulate the functions. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Encapsulate the functions. *
* *
*****************************************************************************/
htbl->h = h;
htbl->match = match;
htbl->destroy = destroy;
htbl->h = h;
htbl->match = match;
htbl->destroy = destroy;
/*****************************************************************************
* *
* Initialize the number of elements in the table. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Initialize the number of elements in the table. *
* *
*****************************************************************************/
htbl->size = 0;
htbl->size = 0;
return 0;
return 0;
} /* end chtbl_init () */
/*****************************************************************************
@ -81,35 +81,35 @@ int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int
void chtbl_destroy(CHTbl *htbl) {
int i;
int i;
/*****************************************************************************
* *
* Destroy each bucket. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Destroy each bucket. *
* *
*****************************************************************************/
for (i = 0; i < htbl->buckets; i++) {
list_destroy(&htbl->table[i]);
} /* end for () */
for (i = 0; i < htbl->buckets; i++) {
list_destroy(&htbl->table[i]);
} /* end for () */
/*****************************************************************************
* *
* Free the storage allocated for the hash table. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Free the storage allocated for the hash table. *
* *
*****************************************************************************/
free(htbl->table);
free(htbl->table);
/*****************************************************************************
* *
* No operations are allowed now, but clear the structure as a precaution. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* No operations are allowed now, but clear the structure as a precaution. *
* *
*****************************************************************************/
memset(htbl, 0, sizeof(CHTbl));
memset(htbl, 0, sizeof(CHTbl));
return;
return;
} /* end chtbl_destroy() */
/*****************************************************************************
@ -120,38 +120,38 @@ void chtbl_destroy(CHTbl *htbl) {
int chtbl_insert(CHTbl *htbl, const void *data) {
void *temp;
int bucket,retval;
void *temp;
int bucket,retval;
/*****************************************************************************
* *
* Do nothing if the data is already in the table. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Do nothing if the data is already in the table. *
* *
*****************************************************************************/
temp = (void *)data;
temp = (void *)data;
if (chtbl_lookup(htbl, &temp) == 0)
return 1;
if (chtbl_lookup(htbl, &temp) == 0)
return 1;
/*****************************************************************************
* *
* Hash the key. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Hash the key. *
* *
*****************************************************************************/
bucket = htbl->h(data) % htbl->buckets;
bucket = htbl->h(data) % htbl->buckets;
/*****************************************************************************
* *
* Insert the data into the bucket. *
* *
*****************************************************************************/
/*****************************************************************************
* *
* Insert the data into the bucket. *
* *
*****************************************************************************/
if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
htbl->size++;
if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0)
htbl->size++;
return retval;
return retval;
} /* end chtbl_insert() */
/*****************************************************************************
@ -162,53 +162,54 @@ int chtbl_insert(CHTbl *htbl, const void *data) {
int chtbl_remove(CHTbl *htbl, void **data) {
ListElmt *element, *prev;
int bucket;
ListElmt *element, *prev;
int bucket;
/*****************************************************************************
* *
* Hash the key. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Hash the key. *
* *
*********************************************************************/
bucket = htbl->h(*data) % htbl->buckets;
bucket = htbl->h(*data) % htbl->buckets;
/*****************************************************************************
* *
* Search for the data in the bucket. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Search for the data in the bucket. *
* *
*********************************************************************/
prev = NULL;
prev = NULL;
for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
if (htbl->match(*data, list_data(element))) {
for (element = list_head(&htbl->table[bucket]);
element != NULL; element = list_next(element)) {
if (htbl->match(*data, list_data(element))) {
/***********************************************************************
* *
* Remove the data from the bucket. *
* *
***********************************************************************/
/*****************************************************
* *
* Remove the data from the bucket. *
* *
*****************************************************/
if (list_rem_next(&htbl->table[bucket], prev, data) == 0) {
htbl->size--;
return 0;
} /* end if() */
else {
return -1;
}/* end else */
}/* end if (htbl->match(*data, list_data(element))) */
prev = element;
}/* end for() */
/*********************************************************************
* *
* Return that the data was not found. *
* *
*********************************************************************/
if (list_rem_next(&htbl->table[bucket], prev, data) == 0) {
htbl->size--;
return 0;
} /* end if() */
else {
return -1;
}/* end else */
}/* end if (htbl->match(*data, list_data(element))) */
prev = element;
}/* end for() */
/*****************************************************************************
* *
* Return that the data was not found. *
* *
*****************************************************************************/
return -1;
} /* end int chtbl_remove(CHTbl *htbl, void **data) */
/*****************************************************************************
@ -219,42 +220,50 @@ int chtbl_remove(CHTbl *htbl, void **data) {
int chtbl_lookup(const CHTbl *htbl, void **data) {
ListElmt *element;
int bucket;
ListElmt *element;
int bucket;
/*****************************************************************************
* *
* Hash the key. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Hash the key. *
* *
*********************************************************************/
bucket = htbl->h(*data) % htbl->buckets;
bucket = htbl->h(*data) % htbl->buckets;
/*****************************************************************************
* *
* Search for the data in the bucket. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Search for the data in the bucket. *
* *
*********************************************************************/
for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) {
if (htbl->match(*data, list_data(element))) {
for (element = list_head(&htbl->table[bucket]);
element != NULL; element = list_next(element)) {
if (htbl->match(*data, list_data(element))) {
/***********************************************************************
* *
* Pass back the data from the table. *
* *
***********************************************************************/
/*****************************************************
* *
* Pass back the data from the table. *
* *
*****************************************************/
*data = list_data(element);
return 0;
}/* end if() */
}/* end for() */
*data = list_data(element);
return 0;
}/* end if() */
}/* end for() */
/*****************************************************************************
* *
* Return that the data was not found. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Return that the data was not found. *
* *
*********************************************************************/
return -1;
return -1;
} /* end chtbl_lookup() */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

397
src/client.c Normal file
View File

@ -0,0 +1,397 @@
/*
* Client-side variables and functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <haproxy/compat.h>
#include <haproxy/time.h>
#include <types/backend.h>
#include <types/buffers.h>
#include <types/global.h>
#include <types/httperr.h>
#include <types/polling.h>
#include <types/proxy.h>
#include <types/server.h>
#include <types/session.h>
#include <proto/client.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/proto_http.h>
#include <proto/stream_sock.h>
#include <proto/task.h>
/*
* FIXME: This should move to the STREAM_SOCK code then split into TCP and HTTP.
*/
/*
* this function is called on a read event from a listen socket, corresponding
* to an accept. It tries to accept as many connections as possible.
* It returns 0.
*/
int event_accept(int fd) {
struct proxy *p = (struct proxy *)fdtab[fd].owner;
struct session *s;
struct task *t;
int cfd;
int max_accept;
if (global.nbproc > 1)
max_accept = 8; /* let other processes catch some connections too */
else
max_accept = -1;
while (p->nbconn < p->maxconn && max_accept--) {
struct sockaddr_storage addr;
socklen_t laddr = sizeof(addr);
if ((cfd = accept(fd, (struct sockaddr *)&addr, &laddr)) == -1) {
switch (errno) {
case EAGAIN:
case EINTR:
case ECONNABORTED:
return 0; /* nothing more to accept */
case ENFILE:
send_log(p, LOG_EMERG,
"Proxy %s reached system FD limit at %d. Please check system tunables.\n",
p->id, maxfd);
return 0;
case EMFILE:
send_log(p, LOG_EMERG,
"Proxy %s reached process FD limit at %d. Please check 'ulimit-n' and restart.\n",
p->id, maxfd);
return 0;
case ENOBUFS:
case ENOMEM:
send_log(p, LOG_EMERG,
"Proxy %s reached system memory limit at %d sockets. Please check system tunables.\n",
p->id, maxfd);
return 0;
default:
return 0;
}
}
if ((s = pool_alloc(session)) == NULL) { /* disable this proxy for a while */
Alert("out of memory in event_accept().\n");
FD_CLR(fd, StaticReadEvent);
p->state = PR_STIDLE;
close(cfd);
return 0;
}
/* if this session comes from a known monitoring system, we want to ignore
* it as soon as possible, which means closing it immediately for TCP.
*/
s->flags = 0;
if (addr.ss_family == AF_INET &&
p->mon_mask.s_addr &&
(((struct sockaddr_in *)&addr)->sin_addr.s_addr & p->mon_mask.s_addr) == p->mon_net.s_addr) {
if (p->mode == PR_MODE_TCP) {
close(cfd);
pool_free(session, s);
continue;
}
s->flags |= SN_MONITOR;
}
if ((t = pool_alloc(task)) == NULL) { /* disable this proxy for a while */
Alert("out of memory in event_accept().\n");
FD_CLR(fd, StaticReadEvent);
p->state = PR_STIDLE;
close(cfd);
pool_free(session, s);
return 0;
}
s->cli_addr = addr;
if (cfd >= global.maxsock) {
Alert("accept(): not enough free sockets. Raise -n argument. Giving up.\n");
close(cfd);
pool_free(task, t);
pool_free(session, s);
return 0;
}
if ((fcntl(cfd, F_SETFL, O_NONBLOCK) == -1) ||
(setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY,
(char *) &one, sizeof(one)) == -1)) {
Alert("accept(): cannot set the socket in non blocking mode. Giving up\n");
close(cfd);
pool_free(task, t);
pool_free(session, s);
return 0;
}
if (p->options & PR_O_TCP_CLI_KA)
setsockopt(cfd, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
t->next = t->prev = t->rqnext = NULL; /* task not in run queue yet */
t->wq = LIST_HEAD(wait_queue[0]); /* but already has a wait queue assigned */
t->state = TASK_IDLE;
t->process = process_session;
t->context = s;
s->task = t;
s->proxy = p;
s->cli_state = (p->mode == PR_MODE_HTTP) ? CL_STHEADERS : CL_STDATA; /* no HTTP headers for non-HTTP proxies */
s->srv_state = SV_STIDLE;
s->req = s->rep = NULL; /* will be allocated later */
s->res_cr = s->res_cw = s->res_sr = s->res_sw = RES_SILENT;
s->cli_fd = cfd;
s->srv_fd = -1;
s->req_line.len = -1;
s->auth_hdr.len = -1;
s->srv = NULL;
s->pend_pos = NULL;
s->conn_retries = p->conn_retries;
if (s->flags & SN_MONITOR)
s->logs.logwait = 0;
else
s->logs.logwait = p->to_log;
s->logs.tv_accept = now;
s->logs.t_request = -1;
s->logs.t_queue = -1;
s->logs.t_connect = -1;
s->logs.t_data = -1;
s->logs.t_close = 0;
s->logs.uri = NULL;
s->logs.cli_cookie = NULL;
s->logs.srv_cookie = NULL;
s->logs.status = -1;
s->logs.bytes = 0;
s->logs.prx_queue_size = 0; /* we get the number of pending conns before us */
s->logs.srv_queue_size = 0; /* we will get this number soon */
s->data_source = DATA_SRC_NONE;
s->uniq_id = totalconn;
p->cum_conn++;
if (p->nb_req_cap > 0) {
if ((s->req_cap =
pool_alloc_from(p->req_cap_pool, p->nb_req_cap*sizeof(char *)))
== NULL) { /* no memory */
close(cfd); /* nothing can be done for this fd without memory */
pool_free(task, t);
pool_free(session, s);
return 0;
}
memset(s->req_cap, 0, p->nb_req_cap*sizeof(char *));
}
else
s->req_cap = NULL;
if (p->nb_rsp_cap > 0) {
if ((s->rsp_cap =
pool_alloc_from(p->rsp_cap_pool, p->nb_rsp_cap*sizeof(char *)))
== NULL) { /* no memory */
if (s->req_cap != NULL)
pool_free_to(p->req_cap_pool, s->req_cap);
close(cfd); /* nothing can be done for this fd without memory */
pool_free(task, t);
pool_free(session, s);
return 0;
}
memset(s->rsp_cap, 0, p->nb_rsp_cap*sizeof(char *));
}
else
s->rsp_cap = NULL;
if ((p->mode == PR_MODE_TCP || p->mode == PR_MODE_HTTP)
&& (p->logfac1 >= 0 || p->logfac2 >= 0)) {
struct sockaddr_storage sockname;
socklen_t namelen = sizeof(sockname);
if (addr.ss_family != AF_INET ||
!(s->proxy->options & PR_O_TRANSP) ||
get_original_dst(cfd, (struct sockaddr_in *)&sockname, &namelen) == -1)
getsockname(cfd, (struct sockaddr *)&sockname, &namelen);
if (p->to_log) {
/* we have the client ip */
if (s->logs.logwait & LW_CLIP)
if (!(s->logs.logwait &= ~LW_CLIP))
sess_log(s);
}
else if (s->cli_addr.ss_family == AF_INET) {
char pn[INET_ADDRSTRLEN], sn[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&sockname)->sin_addr,
sn, sizeof(sn)) &&
inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
pn, sizeof(pn))) {
send_log(p, LOG_INFO, "Connect from %s:%d to %s:%d (%s/%s)\n",
pn, ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port),
sn, ntohs(((struct sockaddr_in *)&sockname)->sin_port),
p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
}
}
else {
char pn[INET6_ADDRSTRLEN], sn[INET6_ADDRSTRLEN];
if (inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&sockname)->sin6_addr,
sn, sizeof(sn)) &&
inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)&s->cli_addr)->sin6_addr,
pn, sizeof(pn))) {
send_log(p, LOG_INFO, "Connect from %s:%d to %s:%d (%s/%s)\n",
pn, ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
sn, ntohs(((struct sockaddr_in6 *)&sockname)->sin6_port),
p->id, (p->mode == PR_MODE_HTTP) ? "HTTP" : "TCP");
}
}
}
if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) {
struct sockaddr_in sockname;
socklen_t namelen = sizeof(sockname);
int len;
if (addr.ss_family != AF_INET ||
!(s->proxy->options & PR_O_TRANSP) ||
get_original_dst(cfd, (struct sockaddr_in *)&sockname, &namelen) == -1)
getsockname(cfd, (struct sockaddr *)&sockname, &namelen);
if (s->cli_addr.ss_family == AF_INET) {
char pn[INET_ADDRSTRLEN];
inet_ntop(AF_INET,
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
pn, sizeof(pn));
len = sprintf(trash, "%08x:%s.accept(%04x)=%04x from [%s:%d]\n",
s->uniq_id, p->id, (unsigned short)fd, (unsigned short)cfd,
pn, ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port));
}
else {
char pn[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
pn, sizeof(pn));
len = sprintf(trash, "%08x:%s.accept(%04x)=%04x from [%s:%d]\n",
s->uniq_id, p->id, (unsigned short)fd, (unsigned short)cfd,
pn, ntohs(((struct sockaddr_in6 *)(&s->cli_addr))->sin6_port));
}
write(1, trash, len);
}
if ((s->req = pool_alloc(buffer)) == NULL) { /* no memory */
if (s->rsp_cap != NULL)
pool_free_to(p->rsp_cap_pool, s->rsp_cap);
if (s->req_cap != NULL)
pool_free_to(p->req_cap_pool, s->req_cap);
close(cfd); /* nothing can be done for this fd without memory */
pool_free(task, t);
pool_free(session, s);
return 0;
}
s->req->l = 0;
s->req->total = 0;
s->req->h = s->req->r = s->req->lr = s->req->w = s->req->data; /* r and w will be reset further */
s->req->rlim = s->req->data + BUFSIZE;
if (s->cli_state == CL_STHEADERS) /* reserve some space for header rewriting */
s->req->rlim -= MAXREWRITE;
if ((s->rep = pool_alloc(buffer)) == NULL) { /* no memory */
pool_free(buffer, s->req);
if (s->rsp_cap != NULL)
pool_free_to(p->rsp_cap_pool, s->rsp_cap);
if (s->req_cap != NULL)
pool_free_to(p->req_cap_pool, s->req_cap);
close(cfd); /* nothing can be done for this fd without memory */
pool_free(task, t);
pool_free(session, s);
return 0;
}
s->rep->l = 0;
s->rep->total = 0;
s->rep->h = s->rep->r = s->rep->lr = s->rep->w = s->rep->rlim = s->rep->data;
fdtab[cfd].read = &event_cli_read;
fdtab[cfd].write = &event_cli_write;
fdtab[cfd].owner = t;
fdtab[cfd].state = FD_STREADY;
if ((p->mode == PR_MODE_HTTP && (s->flags & SN_MONITOR)) ||
(p->mode == PR_MODE_HEALTH && (p->options & PR_O_HTTP_CHK)))
/* Either we got a request from a monitoring system on an HTTP instance,
* or we're in health check mode with the 'httpchk' option enabled. In
* both cases, we return a fake "HTTP/1.0 200 OK" response and we exit.
*/
client_retnclose(s, 19, "HTTP/1.0 200 OK\r\n\r\n"); /* forge a 200 response */
else if (p->mode == PR_MODE_HEALTH) { /* health check mode, no client reading */
client_retnclose(s, 3, "OK\n"); /* forge an "OK" response */
}
else {
FD_SET(cfd, StaticReadEvent);
}
#if defined(DEBUG_FULL) && defined(ENABLE_EPOLL)
if (PrevReadEvent) {
assert(!(FD_ISSET(cfd, PrevReadEvent)));
assert(!(FD_ISSET(cfd, PrevWriteEvent)));
}
#endif
fd_insert(cfd);
tv_eternity(&s->cnexpire);
tv_eternity(&s->srexpire);
tv_eternity(&s->swexpire);
tv_eternity(&s->crexpire);
tv_eternity(&s->cwexpire);
if (s->proxy->clitimeout) {
if (FD_ISSET(cfd, StaticReadEvent))
tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
if (FD_ISSET(cfd, StaticWriteEvent))
tv_delayfrom(&s->cwexpire, &now, s->proxy->clitimeout);
}
tv_min(&t->expire, &s->crexpire, &s->cwexpire);
task_queue(t);
if (p->mode != PR_MODE_HEALTH)
task_wakeup(&rq, t);
p->nbconn++;
if (p->nbconn > p->nbconn_max)
p->nbconn_max = p->nbconn;
actconn++;
totalconn++;
// fprintf(stderr, "accepting from %p => %d conn, %d total, task=%p\n", p, actconn, totalconn, t);
} /* end of while (p->nbconn < p->maxconn) */
return 0;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

503
src/fd.c Normal file
View File

@ -0,0 +1,503 @@
/*
* File descriptors management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
/*
* FIXME:
* - we still use 'listeners' to check whether we want to stop or not.
* - the various pollers should be moved to other external files, possibly
* dynamic libs.
* - merge event_cli_read() and event_srv_read(). The difference is res_*,
* buffer (at the beginning) and timeouts (at the end).
* => event_tcp_read(). It may be called from event_accept().
* - extract the connect code from event_srv_write()
* => event_tcp_connect(). It must then call event_write().
* - merge the remaining event_cli_write() and event_srv_write()
* => single event_tcp_write(). Check buffer, fd_state, res*, and timeouts.
*
*/
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <haproxy/compat.h>
#include <haproxy/config.h>
#include <haproxy/time.h>
#include <types/fd.h>
#include <types/global.h>
#include <proto/polling.h>
#include <proto/task.h>
struct fdtab *fdtab = NULL; /* array of all the file descriptors */
int maxfd; /* # of the highest fd + 1 */
int totalconn; /* total # of terminated sessions */
int actconn; /* # of active sessions */
fd_set *StaticReadEvent, *StaticWriteEvent;
int cfg_polling_mechanism = 0; /* POLL_USE_{SELECT|POLL|EPOLL} */
/******************************
* pollers
******************************/
/*
* FIXME: this is dirty, but at the moment, there's no other solution to remove
* the old FDs from outside the loop. Perhaps we should export a global 'poll'
* structure with pointers to functions such as init_fd() and close_fd(), plus
* a private structure with several pointers to places such as below.
*/
#if defined(ENABLE_EPOLL)
fd_set *PrevReadEvent = NULL, *PrevWriteEvent = NULL;
#if defined(USE_MY_EPOLL)
_syscall1 (int, epoll_create, int, size);
_syscall4 (int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, event);
_syscall4 (int, epoll_wait, int, epfd, struct epoll_event *, events, int, maxevents, int, timeout);
#endif
/*
* Main epoll() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int epoll_loop(int action)
{
int next_time;
int status;
int fd;
int fds, count;
int pr, pw, sr, sw;
unsigned rn, ro, wn, wo; /* read new, read old, write new, write old */
struct epoll_event ev;
/* private data */
static struct epoll_event *epoll_events = NULL;
static int epoll_fd;
if (action == POLL_LOOP_ACTION_INIT) {
epoll_fd = epoll_create(global.maxsock + 1);
if (epoll_fd < 0)
return 0;
else {
epoll_events = (struct epoll_event*)
calloc(1, sizeof(struct epoll_event) * global.maxsock);
PrevReadEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
PrevWriteEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
}
return 1;
}
else if (action == POLL_LOOP_ACTION_CLEAN) {
if (PrevWriteEvent) free(PrevWriteEvent);
if (PrevReadEvent) free(PrevReadEvent);
if (epoll_events) free(epoll_events);
close(epoll_fd);
epoll_fd = 0;
return 1;
}
/* OK, it's POLL_LOOP_ACTION_RUN */
tv_now(&now);
while (1) {
next_time = process_runnable_tasks();
/* stop when there's no connection left and we don't allow them anymore */
if (!actconn && listeners == 0)
break;
for (fds = 0; (fds << INTBITS) < maxfd; fds++) {
rn = ((int*)StaticReadEvent)[fds]; ro = ((int*)PrevReadEvent)[fds];
wn = ((int*)StaticWriteEvent)[fds]; wo = ((int*)PrevWriteEvent)[fds];
if ((ro^rn) | (wo^wn)) {
for (count = 0, fd = fds << INTBITS; count < (1<<INTBITS) && fd < maxfd; count++, fd++) {
#define FDSETS_ARE_INT_ALIGNED
#ifdef FDSETS_ARE_INT_ALIGNED
#define WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
#ifdef WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
pr = (ro >> count) & 1;
pw = (wo >> count) & 1;
sr = (rn >> count) & 1;
sw = (wn >> count) & 1;
#else
pr = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&ro);
pw = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&wo);
sr = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&rn);
sw = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&wn);
#endif
#else
pr = FD_ISSET(fd, PrevReadEvent);
pw = FD_ISSET(fd, PrevWriteEvent);
sr = FD_ISSET(fd, StaticReadEvent);
sw = FD_ISSET(fd, StaticWriteEvent);
#endif
if (!((sr^pr) | (sw^pw)))
continue;
ev.events = (sr ? EPOLLIN : 0) | (sw ? EPOLLOUT : 0);
ev.data.fd = fd;
#ifdef EPOLL_CTL_MOD_WORKAROUND
/* I encountered a rarely reproducible problem with
* EPOLL_CTL_MOD where a modified FD (systematically
* the one in epoll_events[0], fd#7) would sometimes
* be set EPOLL_OUT while asked for a read ! This is
* with the 2.4 epoll patch. The workaround is to
* delete then recreate in case of modification.
* This is in 2.4 up to epoll-lt-0.21 but not in 2.6
* nor RHEL kernels.
*/
if ((pr | pw) && fdtab[fd].state != FD_STCLOSE)
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
if ((sr | sw))
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
#else
if ((pr | pw)) {
/* the file-descriptor already exists... */
if ((sr | sw)) {
/* ...and it will still exist */
if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev) < 0) {
// perror("epoll_ctl(MOD)");
// exit(1);
}
} else {
/* ...and it will be removed */
if (fdtab[fd].state != FD_STCLOSE &&
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev) < 0) {
// perror("epoll_ctl(DEL)");
// exit(1);
}
}
} else {
/* the file-descriptor did not exist, let's add it */
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
// perror("epoll_ctl(ADD)");
// exit(1);
}
}
#endif // EPOLL_CTL_MOD_WORKAROUND
}
((int*)PrevReadEvent)[fds] = rn;
((int*)PrevWriteEvent)[fds] = wn;
}
}
/* now let's wait for events */
status = epoll_wait(epoll_fd, epoll_events, maxfd, next_time);
tv_now(&now);
for (count = 0; count < status; count++) {
fd = epoll_events[count].data.fd;
if (FD_ISSET(fd, StaticReadEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (epoll_events[count].events & ( EPOLLIN | EPOLLERR | EPOLLHUP ))
fdtab[fd].read(fd);
}
if (FD_ISSET(fd, StaticWriteEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (epoll_events[count].events & ( EPOLLOUT | EPOLLERR | EPOLLHUP ))
fdtab[fd].write(fd);
}
}
}
return 1;
}
#endif
#if defined(ENABLE_POLL)
/*
* Main poll() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int poll_loop(int action)
{
int next_time;
int status;
int fd, nbfd;
int fds, count;
int sr, sw;
unsigned rn, wn; /* read new, write new */
/* private data */
static struct pollfd *poll_events = NULL;
if (action == POLL_LOOP_ACTION_INIT) {
poll_events = (struct pollfd*)
calloc(1, sizeof(struct pollfd) * global.maxsock);
return 1;
}
else if (action == POLL_LOOP_ACTION_CLEAN) {
if (poll_events)
free(poll_events);
return 1;
}
/* OK, it's POLL_LOOP_ACTION_RUN */
tv_now(&now);
while (1) {
next_time = process_runnable_tasks();
/* stop when there's no connection left and we don't allow them anymore */
if (!actconn && listeners == 0)
break;
nbfd = 0;
for (fds = 0; (fds << INTBITS) < maxfd; fds++) {
rn = ((int*)StaticReadEvent)[fds];
wn = ((int*)StaticWriteEvent)[fds];
if ((rn|wn)) {
for (count = 0, fd = fds << INTBITS; count < (1<<INTBITS) && fd < maxfd; count++, fd++) {
#define FDSETS_ARE_INT_ALIGNED
#ifdef FDSETS_ARE_INT_ALIGNED
#define WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
#ifdef WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
sr = (rn >> count) & 1;
sw = (wn >> count) & 1;
#else
sr = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&rn);
sw = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&wn);
#endif
#else
sr = FD_ISSET(fd, StaticReadEvent);
sw = FD_ISSET(fd, StaticWriteEvent);
#endif
if ((sr|sw)) {
poll_events[nbfd].fd = fd;
poll_events[nbfd].events = (sr ? POLLIN : 0) | (sw ? POLLOUT : 0);
nbfd++;
}
}
}
}
/* now let's wait for events */
status = poll(poll_events, nbfd, next_time);
tv_now(&now);
for (count = 0; status > 0 && count < nbfd; count++) {
fd = poll_events[count].fd;
if (!(poll_events[count].revents & ( POLLOUT | POLLIN | POLLERR | POLLHUP )))
continue;
/* ok, we found one active fd */
status--;
if (FD_ISSET(fd, StaticReadEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (poll_events[count].revents & ( POLLIN | POLLERR | POLLHUP ))
fdtab[fd].read(fd);
}
if (FD_ISSET(fd, StaticWriteEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (poll_events[count].revents & ( POLLOUT | POLLERR | POLLHUP ))
fdtab[fd].write(fd);
}
}
}
return 1;
}
#endif
/*
* Main select() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int select_loop(int action)
{
int next_time;
int status;
int fd,i;
struct timeval delta;
int readnotnull, writenotnull;
static fd_set *ReadEvent = NULL, *WriteEvent = NULL;
if (action == POLL_LOOP_ACTION_INIT) {
ReadEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
WriteEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
return 1;
}
else if (action == POLL_LOOP_ACTION_CLEAN) {
if (WriteEvent) free(WriteEvent);
if (ReadEvent) free(ReadEvent);
return 1;
}
/* OK, it's POLL_LOOP_ACTION_RUN */
tv_now(&now);
while (1) {
next_time = process_runnable_tasks();
/* stop when there's no connection left and we don't allow them anymore */
if (!actconn && listeners == 0)
break;
if (next_time > 0) { /* FIXME */
/* Convert to timeval */
/* to avoid eventual select loops due to timer precision */
next_time += SCHEDULER_RESOLUTION;
delta.tv_sec = next_time / 1000;
delta.tv_usec = (next_time % 1000) * 1000;
}
else if (next_time == 0) { /* allow select to return immediately when needed */
delta.tv_sec = delta.tv_usec = 0;
}
/* let's restore fdset state */
readnotnull = 0; writenotnull = 0;
for (i = 0; i < (maxfd + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
readnotnull |= (*(((int*)ReadEvent)+i) = *(((int*)StaticReadEvent)+i)) != 0;
writenotnull |= (*(((int*)WriteEvent)+i) = *(((int*)StaticWriteEvent)+i)) != 0;
}
// /* just a verification code, needs to be removed for performance */
// for (i=0; i<maxfd; i++) {
// if (FD_ISSET(i, ReadEvent) != FD_ISSET(i, StaticReadEvent))
// abort();
// if (FD_ISSET(i, WriteEvent) != FD_ISSET(i, StaticWriteEvent))
// abort();
//
// }
status = select(maxfd,
readnotnull ? ReadEvent : NULL,
writenotnull ? WriteEvent : NULL,
NULL,
(next_time >= 0) ? &delta : NULL);
/* this is an experiment on the separation of the select work */
// status = (readnotnull ? select(maxfd, ReadEvent, NULL, NULL, (next_time >= 0) ? &delta : NULL) : 0);
// status |= (writenotnull ? select(maxfd, NULL, WriteEvent, NULL, (next_time >= 0) ? &delta : NULL) : 0);
tv_now(&now);
if (status > 0) { /* must proceed with events */
int fds;
char count;
for (fds = 0; (fds << INTBITS) < maxfd; fds++)
if ((((int *)(ReadEvent))[fds] | ((int *)(WriteEvent))[fds]) != 0)
for (count = 1<<INTBITS, fd = fds << INTBITS; count && fd < maxfd; count--, fd++) {
/* if we specify read first, the accepts and zero reads will be
* seen first. Moreover, system buffers will be flushed faster.
*/
if (FD_ISSET(fd, ReadEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
fdtab[fd].read(fd);
}
if (FD_ISSET(fd, WriteEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
fdtab[fd].write(fd);
}
}
}
else {
// fprintf(stderr,"select returned %d, maxfd=%d\n", status, maxfd);
}
}
return 1;
}
/*********************
* generic functions
*********************/
/* Deletes an FD from the fdsets, and recomputes the maxfd limit.
* The file descriptor is also closed.
*/
void fd_delete(int fd)
{
FD_CLR(fd, StaticReadEvent);
FD_CLR(fd, StaticWriteEvent);
#if defined(ENABLE_EPOLL)
if (PrevReadEvent) {
FD_CLR(fd, PrevReadEvent);
FD_CLR(fd, PrevWriteEvent);
}
#endif
close(fd);
fdtab[fd].state = FD_STCLOSE;
while ((maxfd-1 >= 0) && (fdtab[maxfd-1].state == FD_STCLOSE))
maxfd--;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

869
src/haproxy.c Normal file
View File

@ -0,0 +1,869 @@
/*
* HA-Proxy : High Availability-enabled HTTP/TCP proxy
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Please refer to RFC2068 or RFC2616 for informations about HTTP protocol, and
* RFC2965 for informations about cookies usage. More generally, the IETF HTTP
* Working Group's web site should be consulted for protocol related changes :
*
* http://ftp.ics.uci.edu/pub/ietf/http/
*
* Pending bugs (may be not fixed because never reproduced) :
* - solaris only : sometimes, an HTTP proxy with only a dispatch address causes
* the proxy to terminate (no core) if the client breaks the connection during
* the response. Seen on 1.1.8pre4, but never reproduced. May not be related to
* the snprintf() bug since requests were simple (GET / HTTP/1.0), but may be
* related to missing setsid() (fixed in 1.1.15)
* - a proxy with an invalid config will prevent the startup even if disabled.
*
* ChangeLog has moved to the CHANGELOG file.
*
* TODO:
* - handle properly intermediate incomplete server headers. Done ?
* - handle hot-reconfiguration
* - fix client/server state transition when server is in connect or headers state
* and client suddenly disconnects. The server *should* switch to SHUT_WR, but
* still handle HTTP headers.
* - remove MAX_NEWHDR
* - cut this huge file into several ones
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <sys/resource.h>
#include <time.h>
#include <syslog.h>
#ifdef DEBUG_FULL
#include <assert.h>
#endif
#include <haproxy/appsession.h>
#include <haproxy/base64.h>
#include <haproxy/cfgparse.h>
#include <haproxy/compat.h>
#include <haproxy/config.h>
#include <haproxy/defaults.h>
#include <haproxy/memory.h>
#include <haproxy/mini-clist.h>
#include <haproxy/regex.h>
#include <haproxy/standard.h>
#include <haproxy/time.h>
#include <haproxy/uri_auth.h>
#include <haproxy/version.h>
#include <types/capture.h>
#include <types/global.h>
#include <types/httperr.h>
#include <types/proto_http.h>
#include <proto/backend.h>
#include <proto/buffers.h>
#include <proto/client.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/polling.h>
#include <proto/proxy.h>
#include <proto/queue.h>
#include <proto/server.h>
#include <proto/stream_sock.h>
#include <proto/task.h>
/*********************************************************************/
/*********************************************************************/
char *cfg_cfgfile = NULL; /* configuration file */
char *progname = NULL; /* program name */
int pid; /* current process id */
/* global options */
struct global global = {
logfac1 : -1,
logfac2 : -1,
loglev1 : 7, /* max syslog level : debug */
loglev2 : 7,
/* others NULL OK */
};
/*********************************************************************/
int stopping; /* non zero means stopping in progress */
/* Here we store informations about the pids of the processes we may pause
* or kill. We will send them a signal every 10 ms until we can bind to all
* our ports. With 200 retries, that's about 2 seconds.
*/
#define MAX_START_RETRIES 200
static int nb_oldpids = 0;
static int *oldpids = NULL;
static int oldpids_sig; /* use USR1 or TERM */
/* this is used to drain data, and as a temporary buffer for sprintf()... */
char trash[BUFSIZE];
const int zero = 0;
const int one = 1;
/*
* Syslog facilities and levels. Conforming to RFC3164.
*/
#define MAX_HOSTNAME_LEN 32
static char hostname[MAX_HOSTNAME_LEN] = "";
/*********************************************************************/
/* general purpose functions ***************************************/
/*********************************************************************/
void display_version()
{
printf("HA-Proxy version " HAPROXY_VERSION " " HAPROXY_DATE"\n");
printf("Copyright 2000-2006 Willy Tarreau <w@1wt.eu>\n\n");
}
/*
* This function prints the command line usage and exits
*/
void usage(char *name)
{
display_version();
fprintf(stderr,
"Usage : %s -f <cfgfile> [ -vdV"
"D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
" [ -p <pidfile> ] [ -m <max megs> ]\n"
" -v displays version\n"
" -d enters debug mode ; -db only disables background mode.\n"
" -V enters verbose mode (disables quiet mode)\n"
" -D goes daemon ; implies -q\n"
" -q quiet mode : don't display messages\n"
" -c check mode : only check config file and exit\n"
" -n sets the maximum total # of connections (%d)\n"
" -m limits the usable amount of memory (in MB)\n"
" -N sets the default, per-proxy maximum # of connections (%d)\n"
" -p writes pids of all children to this file\n"
#if defined(ENABLE_EPOLL)
" -de disables epoll() usage even when available\n"
#endif
#if defined(ENABLE_POLL)
" -dp disables poll() usage even when available\n"
#endif
" -sf/-st [pid ]* finishes/terminates old pids. Must be last arguments.\n"
"\n",
name, DEFAULT_MAXCONN, cfg_maxpconn);
exit(1);
}
/*********************************************************************/
/* more specific functions ***************************************/
/*********************************************************************/
/*
* upon SIGUSR1, let's have a soft stop.
*/
void sig_soft_stop(int sig)
{
soft_stop();
signal(sig, SIG_IGN);
}
/*
* upon SIGTTOU, we pause everything
*/
void sig_pause(int sig)
{
pause_proxies();
signal(sig, sig_pause);
}
/*
* upon SIGTTIN, let's have a soft stop.
*/
void sig_listen(int sig)
{
listen_proxies();
signal(sig, sig_listen);
}
/*
* this function dumps every server's state when the process receives SIGHUP.
*/
void sig_dump_state(int sig)
{
struct proxy *p = proxy;
Warning("SIGHUP received, dumping servers states.\n");
while (p) {
struct server *s = p->srv;
send_log(p, LOG_NOTICE, "SIGHUP received, dumping servers states for proxy %s.\n", p->id);
while (s) {
snprintf(trash, sizeof(trash),
"SIGHUP: Server %s/%s is %s. Conn: %d act, %d pend, %d tot.",
p->id, s->id,
(s->state & SRV_RUNNING) ? "UP" : "DOWN",
s->cur_sess, s->nbpend, s->cum_sess);
Warning("%s\n", trash);
send_log(p, LOG_NOTICE, "%s\n", trash);
s = s->next;
}
if (p->srv_act == 0) {
snprintf(trash, sizeof(trash),
"SIGHUP: Proxy %s %s ! Conn: %d act, %d pend (%d unass), %d tot.",
p->id,
(p->srv_bck) ? "is running on backup servers" : "has no server available",
p->nbconn, p->totpend, p->nbpend, p->cum_conn);
} else {
snprintf(trash, sizeof(trash),
"SIGHUP: Proxy %s has %d active servers and %d backup servers available."
" Conn: %d act, %d pend (%d unass), %d tot.",
p->id, p->srv_act, p->srv_bck,
p->nbconn, p->totpend, p->nbpend, p->cum_conn);
}
Warning("%s\n", trash);
send_log(p, LOG_NOTICE, "%s\n", trash);
p = p->next;
}
signal(sig, sig_dump_state);
}
void dump(int sig)
{
struct task *t, *tnext;
struct session *s;
tnext = ((struct task *)LIST_HEAD(wait_queue[0]))->next;
while ((t = tnext) != LIST_HEAD(wait_queue[0])) { /* we haven't looped ? */
tnext = t->next;
s = t->context;
qfprintf(stderr,"[dump] wq: task %p, still %ld ms, "
"cli=%d, srv=%d, cr=%d, cw=%d, sr=%d, sw=%d, "
"req=%d, rep=%d, clifd=%d\n",
s, tv_remain(&now, &t->expire),
s->cli_state,
s->srv_state,
FD_ISSET(s->cli_fd, StaticReadEvent),
FD_ISSET(s->cli_fd, StaticWriteEvent),
FD_ISSET(s->srv_fd, StaticReadEvent),
FD_ISSET(s->srv_fd, StaticWriteEvent),
s->req->l, s->rep?s->rep->l:0, s->cli_fd
);
}
}
#ifdef DEBUG_MEMORY
static void fast_stop(void)
{
struct proxy *p;
p = proxy;
while (p) {
p->grace = 0;
p = p->next;
}
soft_stop();
}
void sig_int(int sig)
{
/* This would normally be a hard stop,
but we want to be sure about deallocation,
and so on, so we do a soft stop with
0 GRACE time
*/
fast_stop();
/* If we are killed twice, we decide to die*/
signal(sig, SIG_DFL);
}
void sig_term(int sig)
{
/* This would normally be a hard stop,
but we want to be sure about deallocation,
and so on, so we do a soft stop with
0 GRACE time
*/
fast_stop();
/* If we are killed twice, we decide to die*/
signal(sig, SIG_DFL);
}
#endif
/*
* This function initializes all the necessary variables. It only returns
* if everything is OK. If something fails, it exits.
*/
void init(int argc, char **argv)
{
int i;
int arg_mode = 0; /* MODE_DEBUG, ... */
char *old_argv = *argv;
char *tmp;
char *cfg_pidfile = NULL;
if (1<<INTBITS != sizeof(int)*8) {
fprintf(stderr,
"Error: wrong architecture. Recompile so that sizeof(int)=%d\n",
(int)(sizeof(int)*8));
exit(1);
}
/*
* Initialize the previously static variables.
*/
totalconn = actconn = maxfd = listeners = stopping = 0;
#ifdef HAPROXY_MEMMAX
global.rlimit_memmax = HAPROXY_MEMMAX;
#endif
/* initialize the libc's localtime structures once for all so that we
* won't be missing memory if we want to send alerts under OOM conditions.
*/
tv_now(&now);
localtime(&now.tv_sec);
start_date = now;
init_log();
cfg_polling_mechanism = POLL_USE_SELECT; /* select() is always available */
#if defined(ENABLE_POLL)
cfg_polling_mechanism |= POLL_USE_POLL;
#endif
#if defined(ENABLE_EPOLL)
cfg_polling_mechanism |= POLL_USE_EPOLL;
#endif
pid = getpid();
progname = *argv;
while ((tmp = strchr(progname, '/')) != NULL)
progname = tmp + 1;
argc--; argv++;
while (argc > 0) {
char *flag;
if (**argv == '-') {
flag = *argv+1;
/* 1 arg */
if (*flag == 'v') {
display_version();
exit(0);
}
#if defined(ENABLE_EPOLL)
else if (*flag == 'd' && flag[1] == 'e')
cfg_polling_mechanism &= ~POLL_USE_EPOLL;
#endif
#if defined(ENABLE_POLL)
else if (*flag == 'd' && flag[1] == 'p')
cfg_polling_mechanism &= ~POLL_USE_POLL;
#endif
else if (*flag == 'V')
arg_mode |= MODE_VERBOSE;
else if (*flag == 'd' && flag[1] == 'b')
arg_mode |= MODE_FOREGROUND;
else if (*flag == 'd')
arg_mode |= MODE_DEBUG;
else if (*flag == 'c')
arg_mode |= MODE_CHECK;
else if (*flag == 'D')
arg_mode |= MODE_DAEMON | MODE_QUIET;
else if (*flag == 'q')
arg_mode |= MODE_QUIET;
else if (*flag == 's' && (flag[1] == 'f' || flag[1] == 't')) {
/* list of pids to finish ('f') or terminate ('t') */
if (flag[1] == 'f')
oldpids_sig = SIGUSR1; /* finish then exit */
else
oldpids_sig = SIGTERM; /* terminate immediately */
argv++; argc--;
if (argc > 0) {
oldpids = calloc(argc, sizeof(int));
while (argc > 0) {
oldpids[nb_oldpids] = atol(*argv);
if (oldpids[nb_oldpids] <= 0)
usage(old_argv);
argc--; argv++;
nb_oldpids++;
}
}
}
else { /* >=2 args */
argv++; argc--;
if (argc == 0)
usage(old_argv);
switch (*flag) {
case 'n' : cfg_maxconn = atol(*argv); break;
case 'm' : global.rlimit_memmax = atol(*argv); break;
case 'N' : cfg_maxpconn = atol(*argv); break;
case 'f' : cfg_cfgfile = *argv; break;
case 'p' : cfg_pidfile = *argv; break;
default: usage(old_argv);
}
}
}
else
usage(old_argv);
argv++; argc--;
}
global.mode = MODE_STARTING | /* during startup, we want most of the alerts */
(arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_VERBOSE
| MODE_QUIET | MODE_CHECK | MODE_DEBUG));
if (!cfg_cfgfile)
usage(old_argv);
gethostname(hostname, MAX_HOSTNAME_LEN);
have_appsession = 0;
global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
if (readcfgfile(cfg_cfgfile) < 0) {
Alert("Error reading configuration file : %s\n", cfg_cfgfile);
exit(1);
}
if (have_appsession)
appsession_init();
if (global.mode & MODE_CHECK) {
qfprintf(stdout, "Configuration file is valid : %s\n", cfg_cfgfile);
exit(0);
}
if (cfg_maxconn > 0)
global.maxconn = cfg_maxconn;
if (cfg_pidfile) {
if (global.pidfile)
free(global.pidfile);
global.pidfile = strdup(cfg_pidfile);
}
if (global.maxconn == 0)
global.maxconn = DEFAULT_MAXCONN;
global.maxsock += global.maxconn * 2; /* each connection needs two sockets */
if (arg_mode & (MODE_DEBUG | MODE_FOREGROUND)) {
/* command line debug mode inhibits configuration mode */
global.mode &= ~(MODE_DAEMON | MODE_QUIET);
}
global.mode |= (arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_QUIET |
MODE_VERBOSE | MODE_DEBUG | MODE_STATS | MODE_LOG));
if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) {
Warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n");
global.mode &= ~(MODE_DAEMON | MODE_QUIET);
}
if ((global.nbproc > 1) && !(global.mode & MODE_DAEMON)) {
if (!(global.mode & (MODE_FOREGROUND | MODE_DEBUG)))
Warning("<nbproc> is only meaningful in daemon mode. Setting limit to 1 process.\n");
global.nbproc = 1;
}
if (global.nbproc < 1)
global.nbproc = 1;
StaticReadEvent = (fd_set *)calloc(1,
sizeof(fd_set) *
(global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
StaticWriteEvent = (fd_set *)calloc(1,
sizeof(fd_set) *
(global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
fdtab = (struct fdtab *)calloc(1,
sizeof(struct fdtab) * (global.maxsock));
for (i = 0; i < global.maxsock; i++) {
fdtab[i].state = FD_STCLOSE;
}
}
void deinit(void)
{
struct proxy *p = proxy;
struct cap_hdr *h,*h_next;
struct server *s,*s_next;
struct listener *l,*l_next;
while (p) {
if (p->id)
free(p->id);
if (p->check_req)
free(p->check_req);
if (p->cookie_name)
free(p->cookie_name);
if (p->capture_name)
free(p->capture_name);
/* only strup if the user have set in config.
When should we free it?!
if (p->errmsg.msg400) free(p->errmsg.msg400);
if (p->errmsg.msg403) free(p->errmsg.msg403);
if (p->errmsg.msg408) free(p->errmsg.msg408);
if (p->errmsg.msg500) free(p->errmsg.msg500);
if (p->errmsg.msg502) free(p->errmsg.msg502);
if (p->errmsg.msg503) free(p->errmsg.msg503);
if (p->errmsg.msg504) free(p->errmsg.msg504);
*/
if (p->appsession_name)
free(p->appsession_name);
h = p->req_cap;
while (h) {
h_next = h->next;
if (h->name)
free(h->name);
pool_destroy(h->pool);
free(h);
h = h_next;
}/* end while(h) */
h = p->rsp_cap;
while (h) {
h_next = h->next;
if (h->name)
free(h->name);
pool_destroy(h->pool);
free(h);
h = h_next;
}/* end while(h) */
s = p->srv;
while (s) {
s_next = s->next;
if (s->id)
free(s->id);
if (s->cookie)
free(s->cookie);
free(s);
s = s_next;
}/* end while(s) */
l = p->listen;
while (l) {
l_next = l->next;
free(l);
l = l_next;
}/* end while(l) */
pool_destroy((void **) p->req_cap_pool);
pool_destroy((void **) p->rsp_cap_pool);
p = p->next;
}/* end while(p) */
if (global.chroot) free(global.chroot);
if (global.pidfile) free(global.pidfile);
if (StaticReadEvent) free(StaticReadEvent);
if (StaticWriteEvent) free(StaticWriteEvent);
if (fdtab) free(fdtab);
pool_destroy(pool_session);
pool_destroy(pool_buffer);
pool_destroy(pool_requri);
pool_destroy(pool_task);
pool_destroy(pool_capture);
pool_destroy(pool_appsess);
if (have_appsession) {
pool_destroy(apools.serverid);
pool_destroy(apools.sessid);
}
} /* end deinit() */
/* sends the signal <sig> to all pids found in <oldpids> */
static void tell_old_pids(int sig)
{
int p;
for (p = 0; p < nb_oldpids; p++)
kill(oldpids[p], sig);
}
int main(int argc, char **argv)
{
int err, retry;
struct rlimit limit;
FILE *pidfile = NULL;
init(argc, argv);
signal(SIGQUIT, dump);
signal(SIGUSR1, sig_soft_stop);
signal(SIGHUP, sig_dump_state);
#ifdef DEBUG_MEMORY
signal(SIGINT, sig_int);
signal(SIGTERM, sig_term);
#endif
/* on very high loads, a sigpipe sometimes happen just between the
* getsockopt() which tells "it's OK to write", and the following write :-(
*/
#ifndef MSG_NOSIGNAL
signal(SIGPIPE, SIG_IGN);
#endif
/* We will loop at most 100 times with 10 ms delay each time.
* That's at most 1 second. We only send a signal to old pids
* if we cannot grab at least one port.
*/
retry = MAX_START_RETRIES;
err = ERR_NONE;
while (retry >= 0) {
struct timeval w;
err = start_proxies(retry == 0 || nb_oldpids == 0);
if (err != ERR_RETRYABLE)
break;
if (nb_oldpids == 0)
break;
/* FIXME-20060514: Solaris and OpenBSD do not support shutdown() on
* listening sockets. So on those platforms, it would be wiser to
* simply send SIGUSR1, which will not be undoable.
*/
tell_old_pids(SIGTTOU);
/* give some time to old processes to stop listening */
w.tv_sec = 0;
w.tv_usec = 10*1000;
select(0, NULL, NULL, NULL, &w);
retry--;
}
/* Note: start_proxies() sends an alert when it fails. */
if (err != ERR_NONE) {
if (retry != MAX_START_RETRIES && nb_oldpids)
tell_old_pids(SIGTTIN);
exit(1);
}
if (listeners == 0) {
Alert("[%s.main()] No enabled listener found (check the <listen> keywords) ! Exiting.\n", argv[0]);
/* Note: we don't have to send anything to the old pids because we
* never stopped them. */
exit(1);
}
/* prepare pause/play signals */
signal(SIGTTOU, sig_pause);
signal(SIGTTIN, sig_listen);
if (global.mode & MODE_DAEMON) {
global.mode &= ~MODE_VERBOSE;
global.mode |= MODE_QUIET;
}
/* MODE_QUIET can inhibit alerts and warnings below this line */
global.mode &= ~MODE_STARTING;
if ((global.mode & MODE_QUIET) && !(global.mode & MODE_VERBOSE)) {
/* detach from the tty */
fclose(stdin); fclose(stdout); fclose(stderr);
close(0); close(1); close(2);
}
/* open log & pid files before the chroot */
if (global.mode & MODE_DAEMON && global.pidfile != NULL) {
int pidfd;
unlink(global.pidfile);
pidfd = open(global.pidfile, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (pidfd < 0) {
Alert("[%s.main()] Cannot create pidfile %s\n", argv[0], global.pidfile);
if (nb_oldpids)
tell_old_pids(SIGTTIN);
exit(1);
}
pidfile = fdopen(pidfd, "w");
}
/* chroot if needed */
if (global.chroot != NULL) {
if (chroot(global.chroot) == -1) {
Alert("[%s.main()] Cannot chroot(%s).\n", argv[0], global.chroot);
if (nb_oldpids)
tell_old_pids(SIGTTIN);
}
chdir("/");
}
/* ulimits */
if (!global.rlimit_nofile)
global.rlimit_nofile = global.maxsock;
if (global.rlimit_nofile) {
limit.rlim_cur = limit.rlim_max = global.rlimit_nofile;
if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
Warning("[%s.main()] Cannot raise FD limit to %d.\n", argv[0], global.rlimit_nofile);
}
}
if (global.rlimit_memmax) {
limit.rlim_cur = limit.rlim_max =
global.rlimit_memmax * 1048576 / global.nbproc;
#ifdef RLIMIT_AS
if (setrlimit(RLIMIT_AS, &limit) == -1) {
Warning("[%s.main()] Cannot fix MEM limit to %d megs.\n",
argv[0], global.rlimit_memmax);
}
#else
if (setrlimit(RLIMIT_DATA, &limit) == -1) {
Warning("[%s.main()] Cannot fix MEM limit to %d megs.\n",
argv[0], global.rlimit_memmax);
}
#endif
}
if (nb_oldpids)
tell_old_pids(oldpids_sig);
/* Note that any error at this stage will be fatal because we will not
* be able to restart the old pids.
*/
/* setgid / setuid */
if (global.gid && setgid(global.gid) == -1) {
Alert("[%s.main()] Cannot set gid %d.\n", argv[0], global.gid);
exit(1);
}
if (global.uid && setuid(global.uid) == -1) {
Alert("[%s.main()] Cannot set uid %d.\n", argv[0], global.uid);
exit(1);
}
/* check ulimits */
limit.rlim_cur = limit.rlim_max = 0;
getrlimit(RLIMIT_NOFILE, &limit);
if (limit.rlim_cur < global.maxsock) {
Warning("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. Please raise 'ulimit-n' to %d or more to avoid any trouble.\n",
argv[0], limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock);
}
if (global.mode & MODE_DAEMON) {
int ret = 0;
int proc;
/* the father launches the required number of processes */
for (proc = 0; proc < global.nbproc; proc++) {
ret = fork();
if (ret < 0) {
Alert("[%s.main()] Cannot fork.\n", argv[0]);
if (nb_oldpids)
exit(1); /* there has been an error */
}
else if (ret == 0) /* child breaks here */
break;
if (pidfile != NULL) {
fprintf(pidfile, "%d\n", ret);
fflush(pidfile);
}
}
/* close the pidfile both in children and father */
if (pidfile != NULL)
fclose(pidfile);
free(global.pidfile);
if (proc == global.nbproc)
exit(0); /* parent must leave */
/* if we're NOT in QUIET mode, we should now close the 3 first FDs to ensure
* that we can detach from the TTY. We MUST NOT do it in other cases since
* it would have already be done, and 0-2 would have been affected to listening
* sockets
*/
if (!(global.mode & MODE_QUIET)) {
/* detach from the tty */
fclose(stdin); fclose(stdout); fclose(stderr);
close(0); close(1); close(2); /* close all fd's */
global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */
}
pid = getpid(); /* update child's pid */
setsid();
}
#if defined(ENABLE_EPOLL)
if (cfg_polling_mechanism & POLL_USE_EPOLL) {
if (epoll_loop(POLL_LOOP_ACTION_INIT)) {
epoll_loop(POLL_LOOP_ACTION_RUN);
epoll_loop(POLL_LOOP_ACTION_CLEAN);
cfg_polling_mechanism &= POLL_USE_EPOLL;
}
else {
Warning("epoll() is not available. Using poll()/select() instead.\n");
cfg_polling_mechanism &= ~POLL_USE_EPOLL;
}
}
#endif
#if defined(ENABLE_POLL)
if (cfg_polling_mechanism & POLL_USE_POLL) {
if (poll_loop(POLL_LOOP_ACTION_INIT)) {
poll_loop(POLL_LOOP_ACTION_RUN);
poll_loop(POLL_LOOP_ACTION_CLEAN);
cfg_polling_mechanism &= POLL_USE_POLL;
}
else {
Warning("poll() is not available. Using select() instead.\n");
cfg_polling_mechanism &= ~POLL_USE_POLL;
}
}
#endif
if (cfg_polling_mechanism & POLL_USE_SELECT) {
if (select_loop(POLL_LOOP_ACTION_INIT)) {
select_loop(POLL_LOOP_ACTION_RUN);
select_loop(POLL_LOOP_ACTION_CLEAN);
cfg_polling_mechanism &= POLL_USE_SELECT;
}
}
/* Free all Hash Keys and all Hash elements */
appsession_cleanup();
/* Do some cleanup */
deinit();
exit(0);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -16,7 +16,8 @@
* *
*****************************************************************************/
#include <include/hashpjw.h>
#include <haproxy/hashpjw.h>
#include <haproxy/appsession.h>
/*****************************************************************************
* *
@ -26,37 +27,44 @@
int hashpjw(const void *key) {
const char *ptr;
unsigned int val;
appsess *appsession_temp;
const char *ptr;
unsigned int val;
appsess *appsession_temp;
/*****************************************************************************
* *
* Hash the key by performing a number of bit operations on it. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Hash the key by performing a number of bit operations on it. *
* *
*********************************************************************/
val = 0;
appsession_temp = (appsess *)key;
ptr = appsession_temp->sessid;
val = 0;
appsession_temp = (appsess *)key;
ptr = appsession_temp->sessid;
while (*ptr != '\0') {
while (*ptr != '\0') {
int tmp;
int tmp;
val = (val << 4) + (*ptr);
val = (val << 4) + (*ptr);
if((tmp = (val & 0xf0000000))) {
val = val ^ (tmp >> 24);
val = val ^ tmp;
}
ptr++;
}/* end while */
if((tmp = (val & 0xf0000000))) {
val = val ^ (tmp >> 24);
val = val ^ tmp;
}
ptr++;
}/* end while */
/*****************************************************************************
* *
* In practice, replace PRIME_TBLSIZ with the actual table size. *
* *
*****************************************************************************/
return val % PRIME_TBLSIZ;
/*********************************************************************
* *
* In practice, replace PRIME_TBLSIZ with the actual table size. *
* *
*********************************************************************/
return val % PRIME_TBLSIZ;
}/* end hashpjw */
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -18,7 +18,7 @@
#include <stdlib.h>
#include <string.h>
#include <include/list.h>
#include <haproxy/list.h>
/*****************************************************************************
* *
@ -28,17 +28,17 @@
void list_init(List *list, void (*destroy)(void *data)) {
/*****************************************************************************
* *
* Initialize the list. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Initialize the list. *
* *
*********************************************************************/
list->size = 0;
list->destroy = destroy;
list->head = NULL;
list->tail = NULL;
return;
list->size = 0;
list->destroy = destroy;
list->head = NULL;
list->tail = NULL;
return;
} /* end list_init() */
/*****************************************************************************
@ -49,39 +49,39 @@ void list_init(List *list, void (*destroy)(void *data)) {
void list_destroy(List *list) {
void *data;
int rc;
void *data;
int rc;
/*****************************************************************************
* *
* Remove each element. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Remove each element. *
* *
*********************************************************************/
while (list_size(list) > 0) {
while (list_size(list) > 0) {
rc = list_rem_next(list, NULL, (void **)&data);
rc = list_rem_next(list, NULL, (void **)&data);
if (( rc == 0) && (list->destroy != NULL)) {
if (( rc == 0) && (list->destroy != NULL)) {
/***********************************************************************
* *
* Call a user-defined function to free dynamically allocated data. *
* *
***********************************************************************/
/*******************************************************************
* *
* Call a user-defined function to free dynamically allocated data.*
* *
*******************************************************************/
list->destroy(data);
}/* end if() */
}/* end while() */
list->destroy(data);
}/* end if() */
}/* end while() */
/*****************************************************************************
* *
* No operations are allowed now, but clear the structure as a precaution. *
* *
*****************************************************************************/
/**************************************************************************
* *
* No operations are allowed now, but clear the structure as a precaution.*
* *
**************************************************************************/
memset(list, 0, sizeof(List));
return;
memset(list, 0, sizeof(List));
return;
} /* void list_destroy(List *list) */
/*****************************************************************************
@ -92,62 +92,62 @@ void list_destroy(List *list) {
int list_ins_next(List *list, ListElmt *element, const void *data) {
ListElmt *new_element;
ListElmt *new_element;
/*****************************************************************************
* *
* Allocate storage for the element. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Allocate storage for the element. *
* *
*********************************************************************/
if ((new_element = (ListElmt *)malloc(sizeof(ListElmt))) == NULL)
return -1;
if ((new_element = (ListElmt *)malloc(sizeof(ListElmt))) == NULL)
return -1;
/*****************************************************************************
* *
* Insert the element into the list. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Insert the element into the list. *
* *
*********************************************************************/
new_element->data = (void *)data;
new_element->data = (void *)data;
if (element == NULL) {
if (element == NULL) {
/**************************************************************************
* *
* Handle insertion at the head of the list. *
* *
**************************************************************************/
/*************************************************************
* *
* Handle insertion at the head of the list. *
* *
*************************************************************/
if (list_size(list) == 0)
list->tail = new_element;
if (list_size(list) == 0)
list->tail = new_element;
new_element->next = list->head;
list->head = new_element;
}/* end if (element == NULL) */
else {
new_element->next = list->head;
list->head = new_element;
}/* end if (element == NULL) */
else {
/**************************************************************************
* *
* Handle insertion somewhere other than at the head. *
* *
**************************************************************************/
/*************************************************************
* *
* Handle insertion somewhere other than at the head. *
* *
*************************************************************/
if (element->next == NULL)
list->tail = new_element;
if (element->next == NULL)
list->tail = new_element;
new_element->next = element->next;
element->next = new_element;
}/* end else */
new_element->next = element->next;
element->next = new_element;
}/* end else */
/*****************************************************************************
* *
* Adjust the size of the list to account for the inserted element. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Adjust the size of the list to account for the inserted element. *
* *
*********************************************************************/
list->size++;
return 0;
list->size++;
return 0;
} /* end list_ins_next() */
/*****************************************************************************
@ -158,71 +158,78 @@ int list_ins_next(List *list, ListElmt *element, const void *data) {
int list_rem_next(List *list, ListElmt *element, void **data) {
ListElmt *old_element;
ListElmt *old_element;
/*****************************************************************************
* *
* Do not allow removal from an empty list. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Do not allow removal from an empty list. *
* *
*********************************************************************/
if (list_size(list) == 0)
return -1;
if (list_size(list) == 0)
return -1;
/*****************************************************************************
* *
* Remove the element from the list. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Remove the element from the list. *
* *
*********************************************************************/
if (element == NULL) {
if (element == NULL) {
/**************************************************************************
* *
* Handle removal from the head of the list. *
* *
**************************************************************************/
/*************************************************************
* *
* Handle removal from the head of the list. *
* *
*************************************************************/
*data = list->head->data;
old_element = list->head;
list->head = list->head->next;
*data = list->head->data;
old_element = list->head;
list->head = list->head->next;
if (list_size(list) == 1)
list->tail = NULL;
}/* end if (element == NULL) */
else {
if (list_size(list) == 1)
list->tail = NULL;
}/* end if (element == NULL) */
else {
/**************************************************************************
* *
* Handle removal from somewhere other than the head. *
* *
**************************************************************************/
/*************************************************************
* *
* Handle removal from somewhere other than the head. *
* *
*************************************************************/
if (element->next == NULL)
return -1;
if (element->next == NULL)
return -1;
*data = element->next->data;
old_element = element->next;
element->next = element->next->next;
*data = element->next->data;
old_element = element->next;
element->next = element->next->next;
if (element->next == NULL)
list->tail = element;
}/* end else */
if (element->next == NULL)
list->tail = element;
}/* end else */
/*****************************************************************************
* *
* Free the storage allocated by the abstract data type. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Free the storage allocated by the abstract data type. *
* *
*********************************************************************/
free(old_element);
free(old_element);
/*****************************************************************************
* *
* Adjust the size of the list to account for the removed element. *
* *
*****************************************************************************/
/*********************************************************************
* *
* Adjust the size of the list to account for the removed element. *
* *
*********************************************************************/
list->size--;
return 0;
list->size--;
return 0;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

459
src/log.c Normal file
View File

@ -0,0 +1,459 @@
/*
* General logging functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
#include <haproxy/standard.h>
#include <types/backend.h>
#include <types/global.h>
#include <types/httperr.h>
#include <types/log.h>
#include <types/proxy.h>
#include <types/session.h>
const char *log_facilities[NB_LOG_FACILITIES] = {
"kern", "user", "mail", "daemon",
"auth", "syslog", "lpr", "news",
"uucp", "cron", "auth2", "ftp",
"ntp", "audit", "alert", "cron2",
"local0", "local1", "local2", "local3",
"local4", "local5", "local6", "local7"
};
const char *log_levels[NB_LOG_LEVELS] = {
"emerg", "alert", "crit", "err",
"warning", "notice", "info", "debug"
};
const char *monthname[12] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
const char sess_term_cond[8] = "-cCsSPRI"; /* normal, CliTo, CliErr, SrvTo, SrvErr, PxErr, Resource, Internal */
const char sess_fin_state[8] = "-RCHDLQ7"; /* cliRequest, srvConnect, srvHeader, Data, Last, Queue, unknown */
const char sess_cookie[4] = "NIDV"; /* No cookie, Invalid cookie, cookie for a Down server, Valid cookie */
const char sess_set_cookie[8] = "N1I3PD5R"; /* No set-cookie, unknown, Set-Cookie Inserted, unknown,
Set-cookie seen and left unchanged (passive), Set-cookie Deleted,
unknown, Set-cookie Rewritten */
void **pool_requri = NULL;
/*
* Displays the message on stderr with the date and pid. Overrides the quiet
* mode during startup.
*/
void Alert(char *fmt, ...)
{
va_list argp;
struct timeval tv;
struct tm *tm;
if (!(global.mode & MODE_QUIET) || (global.mode & (MODE_VERBOSE | MODE_STARTING))) {
va_start(argp, fmt);
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
fprintf(stderr, "[ALERT] %03d/%02d%02d%02d (%d) : ",
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
vfprintf(stderr, fmt, argp);
fflush(stderr);
va_end(argp);
}
}
/*
* Displays the message on stderr with the date and pid.
*/
void Warning(char *fmt, ...)
{
va_list argp;
struct timeval tv;
struct tm *tm;
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
va_start(argp, fmt);
gettimeofday(&tv, NULL);
tm = localtime(&tv.tv_sec);
fprintf(stderr, "[WARNING] %03d/%02d%02d%02d (%d) : ",
tm->tm_yday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)getpid());
vfprintf(stderr, fmt, argp);
fflush(stderr);
va_end(argp);
}
}
/*
* Displays the message on <out> only if quiet mode is not set.
*/
void qfprintf(FILE *out, char *fmt, ...)
{
va_list argp;
if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) {
va_start(argp, fmt);
vfprintf(out, fmt, argp);
fflush(out);
va_end(argp);
}
}
/*
* returns log level for <lev> or -1 if not found.
*/
int get_log_level(const char *lev)
{
int level;
level = NB_LOG_LEVELS - 1;
while (level >= 0 && strcmp(log_levels[level], lev))
level--;
return level;
}
/*
* returns log facility for <fac> or -1 if not found.
*/
int get_log_facility(const char *fac)
{
int facility;
facility = NB_LOG_FACILITIES - 1;
while (facility >= 0 && strcmp(log_facilities[facility], fac))
facility--;
return facility;
}
#define FD_SETS_ARE_BITFIELDS
#ifdef FD_SETS_ARE_BITFIELDS
/*
* This map is used with all the FD_* macros to check whether a particular bit
* is set or not. Each bit represents an ACSII code. FD_SET() sets those bytes
* which should be encoded. When FD_ISSET() returns non-zero, it means that the
* byte should be encoded. Be careful to always pass bytes from 0 to 255
* exclusively to the macros.
*/
fd_set hdr_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
fd_set url_encode_map[(sizeof(fd_set) > (256/8)) ? 1 : ((256/8) / sizeof(fd_set))];
#else
#error "Check if your OS uses bitfields for fd_sets"
#endif
/*
* This function sends a syslog message to both log servers of a proxy,
* or to global log servers if the proxy is NULL.
* It also tries not to waste too much time computing the message header.
* It doesn't care about errors nor does it report them.
*/
void send_log(struct proxy *p, int level, char *message, ...)
{
static int logfd = -1; /* syslog UDP socket */
static long tvsec = -1; /* to force the string to be initialized */
struct timeval tv;
va_list argp;
static char logmsg[MAX_SYSLOG_LEN];
static char *dataptr = NULL;
int fac_level;
int hdr_len, data_len;
struct sockaddr_in *sa[2];
int facilities[2], loglevel[2];
int nbloggers = 0;
char *log_ptr;
if (logfd < 0) {
if ((logfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
return;
}
if (level < 0 || progname == NULL || message == NULL)
return;
gettimeofday(&tv, NULL);
if (tv.tv_sec != tvsec || dataptr == NULL) {
/* this string is rebuild only once a second */
struct tm *tm = localtime(&tv.tv_sec);
tvsec = tv.tv_sec;
hdr_len = snprintf(logmsg, sizeof(logmsg),
"<<<<>%s %2d %02d:%02d:%02d %s[%d]: ",
monthname[tm->tm_mon],
tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
progname, pid);
/* WARNING: depending upon implementations, snprintf may return
* either -1 or the number of bytes that would be needed to store
* the total message. In both cases, we must adjust it.
*/
if (hdr_len < 0 || hdr_len > sizeof(logmsg))
hdr_len = sizeof(logmsg);
dataptr = logmsg + hdr_len;
}
va_start(argp, message);
data_len = vsnprintf(dataptr, logmsg + sizeof(logmsg) - dataptr, message, argp);
if (data_len < 0 || data_len > (logmsg + sizeof(logmsg) - dataptr))
data_len = logmsg + sizeof(logmsg) - dataptr;
va_end(argp);
dataptr[data_len - 1] = '\n'; /* force a break on ultra-long lines */
if (p == NULL) {
if (global.logfac1 >= 0) {
sa[nbloggers] = &global.logsrv1;
facilities[nbloggers] = global.logfac1;
loglevel[nbloggers] = global.loglev1;
nbloggers++;
}
if (global.logfac2 >= 0) {
sa[nbloggers] = &global.logsrv2;
facilities[nbloggers] = global.logfac2;
loglevel[nbloggers] = global.loglev2;
nbloggers++;
}
} else {
if (p->logfac1 >= 0) {
sa[nbloggers] = &p->logsrv1;
facilities[nbloggers] = p->logfac1;
loglevel[nbloggers] = p->loglev1;
nbloggers++;
}
if (p->logfac2 >= 0) {
sa[nbloggers] = &p->logsrv2;
facilities[nbloggers] = p->logfac2;
loglevel[nbloggers] = p->loglev2;
nbloggers++;
}
}
while (nbloggers-- > 0) {
/* we can filter the level of the messages that are sent to each logger */
if (level > loglevel[nbloggers])
continue;
/* For each target, we may have a different facility.
* We can also have a different log level for each message.
* This induces variations in the message header length.
* Since we don't want to recompute it each time, nor copy it every
* time, we only change the facility in the pre-computed header,
* and we change the pointer to the header accordingly.
*/
fac_level = (facilities[nbloggers] << 3) + level;
log_ptr = logmsg + 3; /* last digit of the log level */
do {
*log_ptr = '0' + fac_level % 10;
fac_level /= 10;
log_ptr--;
} while (fac_level && log_ptr > logmsg);
*log_ptr = '<';
/* the total syslog message now starts at logptr, for dataptr+data_len-logptr */
#ifndef MSG_NOSIGNAL
sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT,
(struct sockaddr *)sa[nbloggers], sizeof(**sa));
#else
sendto(logfd, log_ptr, dataptr + data_len - log_ptr, MSG_DONTWAIT | MSG_NOSIGNAL,
(struct sockaddr *)sa[nbloggers], sizeof(**sa));
#endif
}
}
/*
* send a log for the session when we have enough info about it
*/
void sess_log(struct session *s)
{
char pn[INET6_ADDRSTRLEN + strlen(":65535")];
struct proxy *p = s->proxy;
int log;
char *uri;
char *pxid;
char *srv;
struct tm *tm;
/* This is a first attempt at a better logging system.
* For now, we rely on send_log() to provide the date, although it obviously
* is the date of the log and not of the request, and most fields are not
* computed.
*/
log = p->to_log & ~s->logs.logwait;
if (s->cli_addr.ss_family == AF_INET)
inet_ntop(AF_INET,
(const void *)&((struct sockaddr_in *)&s->cli_addr)->sin_addr,
pn, sizeof(pn));
else
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)(&s->cli_addr))->sin6_addr,
pn, sizeof(pn));
uri = (log & LW_REQ) ? s->logs.uri ? s->logs.uri : "<BADREQ>" : "";
pxid = p->id;
srv = (p->to_log & LW_SVID) ?
(s->data_source != DATA_SRC_STATS) ?
(s->srv != NULL) ? s->srv->id : "<NOSRV>" : "<STATS>" : "-";
tm = localtime(&s->logs.tv_accept.tv_sec);
if (p->to_log & LW_REQ) {
char tmpline[MAX_SYSLOG_LEN], *h;
int hdr;
h = tmpline;
if (p->to_log & LW_REQHDR && (h < tmpline + sizeof(tmpline) - 10)) {
*(h++) = ' ';
*(h++) = '{';
for (hdr = 0; hdr < p->nb_req_cap; hdr++) {
if (hdr)
*(h++) = '|';
if (s->req_cap[hdr] != NULL)
h = encode_string(h, tmpline + sizeof(tmpline) - 7,
'#', hdr_encode_map, s->req_cap[hdr]);
}
*(h++) = '}';
}
if (p->to_log & LW_RSPHDR && (h < tmpline + sizeof(tmpline) - 7)) {
*(h++) = ' ';
*(h++) = '{';
for (hdr = 0; hdr < p->nb_rsp_cap; hdr++) {
if (hdr)
*(h++) = '|';
if (s->rsp_cap[hdr] != NULL)
h = encode_string(h, tmpline + sizeof(tmpline) - 4,
'#', hdr_encode_map, s->rsp_cap[hdr]);
}
*(h++) = '}';
}
if (h < tmpline + sizeof(tmpline) - 4) {
*(h++) = ' ';
*(h++) = '"';
h = encode_string(h, tmpline + sizeof(tmpline) - 1,
'#', url_encode_map, uri);
*(h++) = '"';
}
*h = '\0';
send_log(p, LOG_INFO,
"%s:%d [%02d/%s/%04d:%02d:%02d:%02d]"
" %s %s %d/%d/%d/%d/%s%d %d %s%lld"
" %s %s %c%c%c%c %d/%d/%d %d/%d%s\n",
pn,
(s->cli_addr.ss_family == AF_INET) ?
ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
tm->tm_hour, tm->tm_min, tm->tm_sec,
pxid, srv,
s->logs.t_request,
(s->logs.t_queue >= 0) ? s->logs.t_queue - s->logs.t_request : -1,
(s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
(s->logs.t_data >= 0) ? s->logs.t_data - s->logs.t_connect : -1,
(p->to_log & LW_BYTES) ? "" : "+", s->logs.t_close,
s->logs.status,
(p->to_log & LW_BYTES) ? "" : "+", s->logs.bytes,
s->logs.cli_cookie ? s->logs.cli_cookie : "-",
s->logs.srv_cookie ? s->logs.srv_cookie : "-",
sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT],
sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
(p->options & PR_O_COOK_ANY) ? sess_cookie[(s->flags & SN_CK_MASK) >> SN_CK_SHIFT] : '-',
(p->options & PR_O_COOK_ANY) ? sess_set_cookie[(s->flags & SN_SCK_MASK) >> SN_SCK_SHIFT] : '-',
s->srv ? s->srv->cur_sess : 0, p->nbconn, actconn,
s->logs.srv_queue_size, s->logs.prx_queue_size, tmpline);
}
else {
send_log(p, LOG_INFO, "%s:%d [%02d/%s/%04d:%02d:%02d:%02d]"
" %s %s %d/%d/%s%d %s%lld"
" %c%c %d/%d/%d %d/%d\n",
pn,
(s->cli_addr.ss_family == AF_INET) ?
ntohs(((struct sockaddr_in *)&s->cli_addr)->sin_port) :
ntohs(((struct sockaddr_in6 *)&s->cli_addr)->sin6_port),
tm->tm_mday, monthname[tm->tm_mon], tm->tm_year+1900,
tm->tm_hour, tm->tm_min, tm->tm_sec,
pxid, srv,
(s->logs.t_queue >= 0) ? s->logs.t_queue : -1,
(s->logs.t_connect >= 0) ? s->logs.t_connect - s->logs.t_queue : -1,
(p->to_log & LW_BYTES) ? "" : "+", s->logs.t_close,
(p->to_log & LW_BYTES) ? "" : "+", s->logs.bytes,
sess_term_cond[(s->flags & SN_ERR_MASK) >> SN_ERR_SHIFT],
sess_fin_state[(s->flags & SN_FINST_MASK) >> SN_FINST_SHIFT],
s->srv ? s->srv->cur_sess : 0, p->nbconn, actconn,
s->logs.srv_queue_size, s->logs.prx_queue_size);
}
s->logs.logwait = 0;
}
/*
* Initializes some data needed later.
*/
void init_log()
{
int i;
char *tmp;
/* initialize the log header encoding map : '{|}"#' should be encoded with
* '#' as prefix, as well as non-printable characters ( <32 or >= 127 ).
* URL encoding only requires '"', '#' to be encoded as well as non-
* printable characters above.
*/
memset(hdr_encode_map, 0, sizeof(hdr_encode_map));
memset(url_encode_map, 0, sizeof(url_encode_map));
for (i = 0; i < 32; i++) {
FD_SET(i, hdr_encode_map);
FD_SET(i, url_encode_map);
}
for (i = 127; i < 256; i++) {
FD_SET(i, hdr_encode_map);
FD_SET(i, url_encode_map);
}
tmp = "\"#{|}";
while (*tmp) {
FD_SET(*tmp, hdr_encode_map);
tmp++;
}
tmp = "\"#";
while (*tmp) {
FD_SET(*tmp, url_encode_map);
tmp++;
}
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

455
src/polling.c Normal file
View File

@ -0,0 +1,455 @@
/*
* File descriptors management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <haproxy/compat.h>
#include <haproxy/config.h>
#include <haproxy/time.h>
#include <types/fd.h>
#include <types/global.h>
#include <types/polling.h>
#include <proto/task.h>
fd_set *StaticReadEvent, *StaticWriteEvent;
int cfg_polling_mechanism = 0; /* POLL_USE_{SELECT|POLL|EPOLL} */
/*
* FIXME: this is dirty, but at the moment, there's no other solution to remove
* the old FDs from outside the loop. Perhaps we should export a global 'poll'
* structure with pointers to functions such as init_fd() and close_fd(), plus
* a private structure with several pointers to places such as below.
*/
#if defined(ENABLE_EPOLL)
fd_set *PrevReadEvent = NULL, *PrevWriteEvent = NULL;
#if defined(USE_MY_EPOLL)
_syscall1 (int, epoll_create, int, size);
_syscall4 (int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, event);
_syscall4 (int, epoll_wait, int, epfd, struct epoll_event *, events, int, maxevents, int, timeout);
#endif
#endif
#if defined(ENABLE_EPOLL)
/*
* Main epoll() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int epoll_loop(int action)
{
int next_time;
int status;
int fd;
int fds, count;
int pr, pw, sr, sw;
unsigned rn, ro, wn, wo; /* read new, read old, write new, write old */
struct epoll_event ev;
/* private data */
static struct epoll_event *epoll_events = NULL;
static int epoll_fd;
if (action == POLL_LOOP_ACTION_INIT) {
epoll_fd = epoll_create(global.maxsock + 1);
if (epoll_fd < 0)
return 0;
else {
epoll_events = (struct epoll_event*)
calloc(1, sizeof(struct epoll_event) * global.maxsock);
PrevReadEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
PrevWriteEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
}
return 1;
}
else if (action == POLL_LOOP_ACTION_CLEAN) {
if (PrevWriteEvent) free(PrevWriteEvent);
if (PrevReadEvent) free(PrevReadEvent);
if (epoll_events) free(epoll_events);
close(epoll_fd);
epoll_fd = 0;
return 1;
}
/* OK, it's POLL_LOOP_ACTION_RUN */
tv_now(&now);
while (1) {
next_time = process_runnable_tasks();
/* stop when there's no connection left and we don't allow them anymore */
if (!actconn && listeners == 0)
break;
for (fds = 0; (fds << INTBITS) < maxfd; fds++) {
rn = ((int*)StaticReadEvent)[fds]; ro = ((int*)PrevReadEvent)[fds];
wn = ((int*)StaticWriteEvent)[fds]; wo = ((int*)PrevWriteEvent)[fds];
if ((ro^rn) | (wo^wn)) {
for (count = 0, fd = fds << INTBITS; count < (1<<INTBITS) && fd < maxfd; count++, fd++) {
#define FDSETS_ARE_INT_ALIGNED
#ifdef FDSETS_ARE_INT_ALIGNED
#define WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
#ifdef WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
pr = (ro >> count) & 1;
pw = (wo >> count) & 1;
sr = (rn >> count) & 1;
sw = (wn >> count) & 1;
#else
pr = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&ro);
pw = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&wo);
sr = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&rn);
sw = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&wn);
#endif
#else
pr = FD_ISSET(fd, PrevReadEvent);
pw = FD_ISSET(fd, PrevWriteEvent);
sr = FD_ISSET(fd, StaticReadEvent);
sw = FD_ISSET(fd, StaticWriteEvent);
#endif
if (!((sr^pr) | (sw^pw)))
continue;
ev.events = (sr ? EPOLLIN : 0) | (sw ? EPOLLOUT : 0);
ev.data.fd = fd;
#ifdef EPOLL_CTL_MOD_WORKAROUND
/* I encountered a rarely reproducible problem with
* EPOLL_CTL_MOD where a modified FD (systematically
* the one in epoll_events[0], fd#7) would sometimes
* be set EPOLL_OUT while asked for a read ! This is
* with the 2.4 epoll patch. The workaround is to
* delete then recreate in case of modification.
* This is in 2.4 up to epoll-lt-0.21 but not in 2.6
* nor RHEL kernels.
*/
if ((pr | pw) && fdtab[fd].state != FD_STCLOSE)
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev);
if ((sr | sw))
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
#else
if ((pr | pw)) {
/* the file-descriptor already exists... */
if ((sr | sw)) {
/* ...and it will still exist */
if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &ev) < 0) {
// perror("epoll_ctl(MOD)");
// exit(1);
}
} else {
/* ...and it will be removed */
if (fdtab[fd].state != FD_STCLOSE &&
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &ev) < 0) {
// perror("epoll_ctl(DEL)");
// exit(1);
}
}
} else {
/* the file-descriptor did not exist, let's add it */
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
// perror("epoll_ctl(ADD)");
// exit(1);
}
}
#endif // EPOLL_CTL_MOD_WORKAROUND
}
((int*)PrevReadEvent)[fds] = rn;
((int*)PrevWriteEvent)[fds] = wn;
}
}
/* now let's wait for events */
status = epoll_wait(epoll_fd, epoll_events, maxfd, next_time);
tv_now(&now);
for (count = 0; count < status; count++) {
fd = epoll_events[count].data.fd;
if (FD_ISSET(fd, StaticReadEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (epoll_events[count].events & ( EPOLLIN | EPOLLERR | EPOLLHUP ))
fdtab[fd].read(fd);
}
if (FD_ISSET(fd, StaticWriteEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (epoll_events[count].events & ( EPOLLOUT | EPOLLERR | EPOLLHUP ))
fdtab[fd].write(fd);
}
}
}
return 1;
}
#endif
#if defined(ENABLE_POLL)
/*
* Main poll() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int poll_loop(int action)
{
int next_time;
int status;
int fd, nbfd;
int fds, count;
int sr, sw;
unsigned rn, wn; /* read new, write new */
/* private data */
static struct pollfd *poll_events = NULL;
if (action == POLL_LOOP_ACTION_INIT) {
poll_events = (struct pollfd*)
calloc(1, sizeof(struct pollfd) * global.maxsock);
return 1;
}
else if (action == POLL_LOOP_ACTION_CLEAN) {
if (poll_events)
free(poll_events);
return 1;
}
/* OK, it's POLL_LOOP_ACTION_RUN */
tv_now(&now);
while (1) {
next_time = process_runnable_tasks();
/* stop when there's no connection left and we don't allow them anymore */
if (!actconn && listeners == 0)
break;
nbfd = 0;
for (fds = 0; (fds << INTBITS) < maxfd; fds++) {
rn = ((int*)StaticReadEvent)[fds];
wn = ((int*)StaticWriteEvent)[fds];
if ((rn|wn)) {
for (count = 0, fd = fds << INTBITS; count < (1<<INTBITS) && fd < maxfd; count++, fd++) {
#define FDSETS_ARE_INT_ALIGNED
#ifdef FDSETS_ARE_INT_ALIGNED
#define WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
#ifdef WE_REALLY_NOW_THAT_FDSETS_ARE_INTS
sr = (rn >> count) & 1;
sw = (wn >> count) & 1;
#else
sr = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&rn);
sw = FD_ISSET(fd&((1<<INTBITS)-1), (typeof(fd_set*))&wn);
#endif
#else
sr = FD_ISSET(fd, StaticReadEvent);
sw = FD_ISSET(fd, StaticWriteEvent);
#endif
if ((sr|sw)) {
poll_events[nbfd].fd = fd;
poll_events[nbfd].events = (sr ? POLLIN : 0) | (sw ? POLLOUT : 0);
nbfd++;
}
}
}
}
/* now let's wait for events */
status = poll(poll_events, nbfd, next_time);
tv_now(&now);
for (count = 0; status > 0 && count < nbfd; count++) {
fd = poll_events[count].fd;
if (!(poll_events[count].revents & ( POLLOUT | POLLIN | POLLERR | POLLHUP )))
continue;
/* ok, we found one active fd */
status--;
if (FD_ISSET(fd, StaticReadEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (poll_events[count].revents & ( POLLIN | POLLERR | POLLHUP ))
fdtab[fd].read(fd);
}
if (FD_ISSET(fd, StaticWriteEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
if (poll_events[count].revents & ( POLLOUT | POLLERR | POLLHUP ))
fdtab[fd].write(fd);
}
}
}
return 1;
}
#endif
/*
* Main select() loop.
* does 3 actions :
* 0 (POLL_LOOP_ACTION_INIT) : initializes necessary private structures
* 1 (POLL_LOOP_ACTION_RUN) : runs the loop
* 2 (POLL_LOOP_ACTION_CLEAN) : cleans up
*
* returns 0 if initialization failed, !0 otherwise.
*/
int select_loop(int action)
{
int next_time;
int status;
int fd,i;
struct timeval delta;
int readnotnull, writenotnull;
static fd_set *ReadEvent = NULL, *WriteEvent = NULL;
if (action == POLL_LOOP_ACTION_INIT) {
ReadEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
WriteEvent = (fd_set *)
calloc(1, sizeof(fd_set) * (global.maxsock + FD_SETSIZE - 1) / FD_SETSIZE);
return 1;
}
else if (action == POLL_LOOP_ACTION_CLEAN) {
if (WriteEvent) free(WriteEvent);
if (ReadEvent) free(ReadEvent);
return 1;
}
/* OK, it's POLL_LOOP_ACTION_RUN */
tv_now(&now);
while (1) {
next_time = process_runnable_tasks();
/* stop when there's no connection left and we don't allow them anymore */
if (!actconn && listeners == 0)
break;
if (next_time > 0) { /* FIXME */
/* Convert to timeval */
/* to avoid eventual select loops due to timer precision */
next_time += SCHEDULER_RESOLUTION;
delta.tv_sec = next_time / 1000;
delta.tv_usec = (next_time % 1000) * 1000;
}
else if (next_time == 0) { /* allow select to return immediately when needed */
delta.tv_sec = delta.tv_usec = 0;
}
/* let's restore fdset state */
readnotnull = 0; writenotnull = 0;
for (i = 0; i < (maxfd + FD_SETSIZE - 1)/(8*sizeof(int)); i++) {
readnotnull |= (*(((int*)ReadEvent)+i) = *(((int*)StaticReadEvent)+i)) != 0;
writenotnull |= (*(((int*)WriteEvent)+i) = *(((int*)StaticWriteEvent)+i)) != 0;
}
// /* just a verification code, needs to be removed for performance */
// for (i=0; i<maxfd; i++) {
// if (FD_ISSET(i, ReadEvent) != FD_ISSET(i, StaticReadEvent))
// abort();
// if (FD_ISSET(i, WriteEvent) != FD_ISSET(i, StaticWriteEvent))
// abort();
//
// }
status = select(maxfd,
readnotnull ? ReadEvent : NULL,
writenotnull ? WriteEvent : NULL,
NULL,
(next_time >= 0) ? &delta : NULL);
/* this is an experiment on the separation of the select work */
// status = (readnotnull ? select(maxfd, ReadEvent, NULL, NULL, (next_time >= 0) ? &delta : NULL) : 0);
// status |= (writenotnull ? select(maxfd, NULL, WriteEvent, NULL, (next_time >= 0) ? &delta : NULL) : 0);
tv_now(&now);
if (status > 0) { /* must proceed with events */
int fds;
char count;
for (fds = 0; (fds << INTBITS) < maxfd; fds++)
if ((((int *)(ReadEvent))[fds] | ((int *)(WriteEvent))[fds]) != 0)
for (count = 1<<INTBITS, fd = fds << INTBITS; count && fd < maxfd; count--, fd++) {
/* if we specify read first, the accepts and zero reads will be
* seen first. Moreover, system buffers will be flushed faster.
*/
if (FD_ISSET(fd, ReadEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
fdtab[fd].read(fd);
}
if (FD_ISSET(fd, WriteEvent)) {
if (fdtab[fd].state == FD_STCLOSE)
continue;
fdtab[fd].write(fd);
}
}
}
else {
// fprintf(stderr,"select returned %d, maxfd=%d\n", status, maxfd);
}
}
return 1;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

2769
src/proto_http.c Normal file

File diff suppressed because it is too large Load Diff

357
src/proxy.c Normal file
View File

@ -0,0 +1,357 @@
/*
* Proxy variables and functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <haproxy/defaults.h>
#include <haproxy/compat.h>
#include <haproxy/time.h>
#include <types/global.h>
#include <types/polling.h>
#include <proto/client.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/proxy.h>
int listeners; /* # of listeners */
struct proxy *proxy = NULL; /* list of all existing proxies */
/*
* this function starts all the proxies. Its return value is composed from
* ERR_NONE, ERR_RETRYABLE and ERR_FATAL. Retryable errors will only be printed
* if <verbose> is not zero.
*/
int start_proxies(int verbose)
{
struct proxy *curproxy;
struct listener *listener;
int err = ERR_NONE;
int fd, pxerr;
for (curproxy = proxy; curproxy != NULL; curproxy = curproxy->next) {
if (curproxy->state != PR_STNEW)
continue; /* already initialized */
pxerr = 0;
for (listener = curproxy->listen; listener != NULL; listener = listener->next) {
if (listener->fd != -1)
continue; /* already initialized */
if ((fd = socket(listener->addr.ss_family, SOCK_STREAM, IPPROTO_TCP)) == -1) {
if (verbose)
Alert("cannot create listening socket for proxy %s. Aborting.\n",
curproxy->id);
err |= ERR_RETRYABLE;
pxerr |= 1;
continue;
}
if (fd >= global.maxsock) {
Alert("socket(): not enough free sockets for proxy %s. Raise -n argument. Aborting.\n",
curproxy->id);
close(fd);
err |= ERR_FATAL;
pxerr |= 1;
break;
}
if ((fcntl(fd, F_SETFL, O_NONBLOCK) == -1) ||
(setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
(char *) &one, sizeof(one)) == -1)) {
Alert("cannot make socket non-blocking for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
err |= ERR_FATAL;
pxerr |= 1;
break;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) == -1) {
Alert("cannot do so_reuseaddr for proxy %s. Continuing.\n",
curproxy->id);
}
#ifdef SO_REUSEPORT
/* OpenBSD supports this. As it's present in old libc versions of Linux,
* it might return an error that we will silently ignore.
*/
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one));
#endif
if (bind(fd,
(struct sockaddr *)&listener->addr,
listener->addr.ss_family == AF_INET6 ?
sizeof(struct sockaddr_in6) :
sizeof(struct sockaddr_in)) == -1) {
if (verbose)
Alert("cannot bind socket for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
err |= ERR_RETRYABLE;
pxerr |= 1;
continue;
}
if (listen(fd, curproxy->maxconn) == -1) {
if (verbose)
Alert("cannot listen to socket for proxy %s. Aborting.\n",
curproxy->id);
close(fd);
err |= ERR_RETRYABLE;
pxerr |= 1;
continue;
}
/* the socket is ready */
listener->fd = fd;
/* the function for the accept() event */
fdtab[fd].read = &event_accept;
fdtab[fd].write = NULL; /* never called */
fdtab[fd].owner = (struct task *)curproxy; /* reference the proxy instead of a task */
fdtab[fd].state = FD_STLISTEN;
FD_SET(fd, StaticReadEvent);
fd_insert(fd);
listeners++;
}
if (!pxerr) {
curproxy->state = PR_STRUN;
send_log(curproxy, LOG_NOTICE, "Proxy %s started.\n", curproxy->id);
}
}
return err;
}
/*
* this function enables proxies when there are enough free sessions,
* or stops them when the table is full. It is designed to be called from the
* select_loop(). It returns the time left before next expiration event
* during stop time, TIME_ETERNITY otherwise.
*/
int maintain_proxies(void)
{
struct proxy *p;
struct listener *l;
int tleft; /* time left */
p = proxy;
tleft = TIME_ETERNITY; /* infinite time */
/* if there are enough free sessions, we'll activate proxies */
if (actconn < global.maxconn) {
while (p) {
if (p->nbconn < p->maxconn) {
if (p->state == PR_STIDLE) {
for (l = p->listen; l != NULL; l = l->next) {
FD_SET(l->fd, StaticReadEvent);
}
p->state = PR_STRUN;
}
}
else {
if (p->state == PR_STRUN) {
for (l = p->listen; l != NULL; l = l->next) {
FD_CLR(l->fd, StaticReadEvent);
}
p->state = PR_STIDLE;
}
}
p = p->next;
}
}
else { /* block all proxies */
while (p) {
if (p->state == PR_STRUN) {
for (l = p->listen; l != NULL; l = l->next) {
FD_CLR(l->fd, StaticReadEvent);
}
p->state = PR_STIDLE;
}
p = p->next;
}
}
if (stopping) {
p = proxy;
while (p) {
if (p->state != PR_STSTOPPED) {
int t;
t = tv_remain2(&now, &p->stop_time);
if (t == 0) {
Warning("Proxy %s stopped.\n", p->id);
send_log(p, LOG_WARNING, "Proxy %s stopped.\n", p->id);
for (l = p->listen; l != NULL; l = l->next) {
fd_delete(l->fd);
listeners--;
}
p->state = PR_STSTOPPED;
}
else {
tleft = MINTIME(t, tleft);
}
}
p = p->next;
}
}
return tleft;
}
/*
* this function disables health-check servers so that the process will quickly be ignored
* by load balancers. Note that if a proxy was already in the PAUSED state, then its grace
* time will not be used since it would already not listen anymore to the socket.
*/
void soft_stop(void)
{
struct proxy *p;
stopping = 1;
p = proxy;
tv_now(&now); /* else, the old time before select will be used */
while (p) {
if (p->state != PR_STSTOPPED) {
Warning("Stopping proxy %s in %d ms.\n", p->id, p->grace);
send_log(p, LOG_WARNING, "Stopping proxy %s in %d ms.\n", p->id, p->grace);
tv_delayfrom(&p->stop_time, &now, p->grace);
}
p = p->next;
}
}
/*
* Linux unbinds the listen socket after a SHUT_RD, and ignores SHUT_WR.
* Solaris refuses either shutdown().
* OpenBSD ignores SHUT_RD but closes upon SHUT_WR and refuses to rebind.
* So a common validation path involves SHUT_WR && listen && SHUT_RD.
* If disabling at least one listener returns an error, then the proxy
* state is set to PR_STERROR because we don't know how to resume from this.
*/
void pause_proxy(struct proxy *p)
{
struct listener *l;
for (l = p->listen; l != NULL; l = l->next) {
if (shutdown(l->fd, SHUT_WR) == 0 &&
listen(l->fd, p->maxconn) == 0 &&
shutdown(l->fd, SHUT_RD) == 0) {
FD_CLR(l->fd, StaticReadEvent);
if (p->state != PR_STERROR)
p->state = PR_STPAUSED;
}
else
p->state = PR_STERROR;
}
}
/*
* This function temporarily disables listening so that another new instance
* can start listening. It is designed to be called upon reception of a
* SIGTTOU, after which either a SIGUSR1 can be sent to completely stop
* the proxy, or a SIGTTIN can be sent to listen again.
*/
void pause_proxies(void)
{
int err;
struct proxy *p;
err = 0;
p = proxy;
tv_now(&now); /* else, the old time before select will be used */
while (p) {
if (p->state != PR_STERROR &&
p->state != PR_STSTOPPED &&
p->state != PR_STPAUSED) {
Warning("Pausing proxy %s.\n", p->id);
send_log(p, LOG_WARNING, "Pausing proxy %s.\n", p->id);
pause_proxy(p);
if (p->state != PR_STPAUSED) {
err |= 1;
Warning("Proxy %s failed to enter pause mode.\n", p->id);
send_log(p, LOG_WARNING, "Proxy %s failed to enter pause mode.\n", p->id);
}
}
p = p->next;
}
if (err) {
Warning("Some proxies refused to pause, performing soft stop now.\n");
send_log(p, LOG_WARNING, "Some proxies refused to pause, performing soft stop now.\n");
soft_stop();
}
}
/*
* This function reactivates listening. This can be used after a call to
* sig_pause(), for example when a new instance has failed starting up.
* It is designed to be called upon reception of a SIGTTIN.
*/
void listen_proxies(void)
{
struct proxy *p;
struct listener *l;
p = proxy;
tv_now(&now); /* else, the old time before select will be used */
while (p) {
if (p->state == PR_STPAUSED) {
Warning("Enabling proxy %s.\n", p->id);
send_log(p, LOG_WARNING, "Enabling proxy %s.\n", p->id);
for (l = p->listen; l != NULL; l = l->next) {
if (listen(l->fd, p->maxconn) == 0) {
if (actconn < global.maxconn && p->nbconn < p->maxconn) {
FD_SET(l->fd, StaticReadEvent);
p->state = PR_STRUN;
}
else
p->state = PR_STIDLE;
} else {
int port;
if (l->addr.ss_family == AF_INET6)
port = ntohs(((struct sockaddr_in6 *)(&l->addr))->sin6_port);
else
port = ntohs(((struct sockaddr_in *)(&l->addr))->sin_port);
Warning("Port %d busy while trying to enable proxy %s.\n",
port, p->id);
send_log(p, LOG_WARNING, "Port %d busy while trying to enable proxy %s.\n",
port, p->id);
/* Another port might have been enabled. Let's stop everything. */
pause_proxy(p);
break;
}
}
}
p = p->next;
}
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

139
src/queue.c Normal file
View File

@ -0,0 +1,139 @@
/*
* Queue management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <haproxy/time.h>
#include <types/proxy.h>
#include <types/session.h>
#include <proto/queue.h>
#include <proto/server.h>
#include <proto/task.h>
void **pool_pendconn = NULL;
/* returns the effective dynamic maxconn for a server, considering the minconn
* and the proxy's usage relative to its saturation.
*/
unsigned int srv_dynamic_maxconn(struct server *s)
{
return s->minconn ?
((s->maxconn * s->proxy->nbconn / s->proxy->maxconn) < s->minconn) ? s->minconn :
(s->maxconn * s->proxy->nbconn / s->proxy->maxconn) : s->maxconn;
}
/*
* Manages a server's connection queue. If woken up, will try to dequeue as
* many pending sessions as possible, and wake them up. The task has nothing
* else to do, so it always returns TIME_ETERNITY.
*/
int process_srv_queue(struct task *t)
{
struct server *s = (struct server*)t->context;
struct proxy *p = s->proxy;
int xferred;
/* First, check if we can handle some connections queued at the proxy. We
* will take as many as we can handle.
*/
for (xferred = 0; s->cur_sess + xferred < srv_dynamic_maxconn(s); xferred++) {
struct session *sess;
sess = pendconn_get_next_sess(s, p);
if (sess == NULL)
break;
task_wakeup(&rq, sess->task);
}
return TIME_ETERNITY;
}
/* Detaches the next pending connection from either a server or a proxy, and
* returns its associated session. If no pending connection is found, NULL is
* returned. Note that neither <srv> nor <px> can be NULL.
*/
struct session *pendconn_get_next_sess(struct server *srv, struct proxy *px)
{
struct pendconn *p;
struct session *sess;
p = pendconn_from_srv(srv);
if (!p) {
p = pendconn_from_px(px);
if (!p)
return NULL;
p->sess->srv = srv;
}
sess = p->sess;
pendconn_free(p);
return sess;
}
/* Adds the session <sess> to the pending connection list of server <sess>->srv
* or to the one of <sess>->proxy if srv is NULL. All counters and back pointers
* are updated accordingly. Returns NULL if no memory is available, otherwise the
* pendconn itself.
*/
struct pendconn *pendconn_add(struct session *sess)
{
struct pendconn *p;
p = pool_alloc(pendconn);
if (!p)
return NULL;
sess->pend_pos = p;
p->sess = sess;
p->srv = sess->srv;
if (sess->srv) {
LIST_ADDQ(&sess->srv->pendconns, &p->list);
sess->logs.srv_queue_size += sess->srv->nbpend;
sess->srv->nbpend++;
if (sess->srv->nbpend > sess->srv->nbpend_max)
sess->srv->nbpend_max = sess->srv->nbpend;
} else {
LIST_ADDQ(&sess->proxy->pendconns, &p->list);
sess->logs.prx_queue_size += sess->proxy->nbpend;
sess->proxy->nbpend++;
if (sess->proxy->nbpend > sess->proxy->nbpend_max)
sess->proxy->nbpend_max = sess->proxy->nbpend;
}
sess->proxy->totpend++;
return p;
}
/*
* Detaches pending connection <p>, decreases the pending count, and frees
* the pending connection. The connection might have been queued to a specific
* server as well as to the proxy. The session also gets marked unqueued.
*/
void pendconn_free(struct pendconn *p)
{
LIST_DEL(&p->list);
p->sess->pend_pos = NULL;
if (p->srv)
p->srv->nbpend--;
else
p->sess->proxy->nbpend--;
p->sess->proxy->totpend--;
pool_free(pendconn, p);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

130
src/regex.c Normal file
View File

@ -0,0 +1,130 @@
/*
* Regex and string management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <haproxy/regex.h>
#include <haproxy/standard.h>
#include <proto/log.h>
/* regex trash buffer used by various regex tests */
regmatch_t pmatch[MAX_MATCH]; /* rm_so, rm_eo for regular expressions */
int exp_replace(char *dst, char *src, char *str, regmatch_t *matches)
{
char *old_dst = dst;
while (*str) {
if (*str == '\\') {
str++;
if (isdigit((int)*str)) {
int len, num;
num = *str - '0';
str++;
if (matches[num].rm_eo > -1 && matches[num].rm_so > -1) {
len = matches[num].rm_eo - matches[num].rm_so;
memcpy(dst, src + matches[num].rm_so, len);
dst += len;
}
} else if (*str == 'x') {
unsigned char hex1, hex2;
str++;
hex1 = toupper(*str++) - '0';
hex2 = toupper(*str++) - '0';
if (hex1 > 9) hex1 -= 'A' - '9' - 1;
if (hex2 > 9) hex2 -= 'A' - '9' - 1;
*dst++ = (hex1<<4) + hex2;
} else {
*dst++ = *str++;
}
} else {
*dst++ = *str++;
}
}
*dst = '\0';
return dst - old_dst;
}
/* returns NULL if the replacement string <str> is valid, or the pointer to the first error */
char *check_replace_string(char *str)
{
char *err = NULL;
while (*str) {
if (*str == '\\') {
err = str; /* in case of a backslash, we return the pointer to it */
str++;
if (!*str)
return err;
else if (isdigit((int)*str))
err = NULL;
else if (*str == 'x') {
str++;
if (!ishex(*str))
return err;
str++;
if (!ishex(*str))
return err;
err = NULL;
}
else {
Warning("'\\%c' : deprecated use of a backslash before something not '\\','x' or a digit.\n", *str);
err = NULL;
}
}
str++;
}
return err;
}
/* returns the pointer to an error in the replacement string, or NULL if OK */
char *chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace)
{
struct hdr_exp *exp;
if (replace != NULL) {
char *err;
err = check_replace_string(replace);
if (err)
return err;
}
while (*head != NULL)
head = &(*head)->next;
exp = calloc(1, sizeof(struct hdr_exp));
exp->preg = preg;
exp->replace = replace;
exp->action = action;
*head = exp;
return NULL;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

23
src/server.c Normal file
View File

@ -0,0 +1,23 @@
/*
* Server management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <types/backend.h>
#include <types/proxy.h>
#include <types/server.h>
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

73
src/session.c Normal file
View File

@ -0,0 +1,73 @@
/*
* Server management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <stdlib.h>
#include <haproxy/memory.h>
#include <types/backend.h>
#include <types/capture.h>
#include <types/log.h>
#include <types/proxy.h>
#include <types/server.h>
#include <proto/session.h>
#include <proto/queue.h>
void **pool_session = NULL;
/*
* frees the context associated to a session. It must have been removed first.
*/
void session_free(struct session *s)
{
if (s->pend_pos)
pendconn_free(s->pend_pos);
if (s->req)
pool_free(buffer, s->req);
if (s->rep)
pool_free(buffer, s->rep);
if (s->rsp_cap != NULL) {
struct cap_hdr *h;
for (h = s->proxy->rsp_cap; h; h = h->next) {
if (s->rsp_cap[h->index] != NULL)
pool_free_to(h->pool, s->rsp_cap[h->index]);
}
pool_free_to(s->proxy->rsp_cap_pool, s->rsp_cap);
}
if (s->req_cap != NULL) {
struct cap_hdr *h;
for (h = s->proxy->req_cap; h; h = h->next) {
if (s->req_cap[h->index] != NULL)
pool_free_to(h->pool, s->req_cap[h->index]);
}
pool_free_to(s->proxy->req_cap_pool, s->req_cap);
}
if (s->logs.uri)
pool_free(requri, s->logs.uri);
if (s->logs.cli_cookie)
pool_free(capture, s->logs.cli_cookie);
if (s->logs.srv_cookie)
pool_free(capture, s->logs.srv_cookie);
pool_free(session, s);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

214
src/standard.c Normal file
View File

@ -0,0 +1,214 @@
/*
* General purpose functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <haproxy/standard.h>
#include <proto/log.h>
/* enough to store 2^63=18446744073709551615 */
static char itoa_str[21];
/*
* copies at most <size-1> chars from <src> to <dst>. Last char is always
* set to 0, unless <size> is 0. The number of chars copied is returned
* (excluding the terminating zero).
* This code has been optimized for size and speed : on x86, it's 45 bytes
* long, uses only registers, and consumes only 4 cycles per char.
*/
int strlcpy2(char *dst, const char *src, int size)
{
char *orig = dst;
if (size) {
while (--size && (*dst = *src)) {
src++; dst++;
}
*dst = 0;
}
return dst - orig;
}
/*
* This function simply returns a statically allocated string containing
* the ascii representation for number 'n' in decimal.
*/
char *ultoa(unsigned long n)
{
char *pos;
pos = itoa_str + sizeof(itoa_str) - 1;
*pos-- = '\0';
do {
*pos-- = '0' + n % 10;
n /= 10;
} while (n && pos >= itoa_str);
return pos + 1;
}
/*
* Returns non-zero if character <s> is a hex digit (0-9, a-f, A-F), else zero.
*
* It looks like this one would be a good candidate for inlining, but this is
* not interesting because it around 35 bytes long and often called multiple
* times within the same function.
*/
int ishex(char s)
{
s -= '0';
if ((unsigned char)s <= 9)
return 1;
s -= 'A' - '0';
if ((unsigned char)s <= 5)
return 1;
s -= 'a' - 'A';
if ((unsigned char)s <= 5)
return 1;
return 0;
}
/*
* converts <str> to a struct sockaddr_in* which is locally allocated.
* The format is "addr:port", where "addr" can be a dotted IPv4 address,
* a host name, or empty or "*" to indicate INADDR_ANY.
*/
struct sockaddr_in *str2sa(char *str)
{
static struct sockaddr_in sa;
char *c;
int port;
memset(&sa, 0, sizeof(sa));
str = strdup(str);
if ((c = strrchr(str,':')) != NULL) {
*c++ = '\0';
port = atol(c);
}
else
port = 0;
if (*str == '*' || *str == '\0') { /* INADDR_ANY */
sa.sin_addr.s_addr = INADDR_ANY;
}
else if (!inet_pton(AF_INET, str, &sa.sin_addr)) {
struct hostent *he;
if ((he = gethostbyname(str)) == NULL) {
Alert("Invalid server name: '%s'\n", str);
}
else
sa.sin_addr = *(struct in_addr *) *(he->h_addr_list);
}
sa.sin_port = htons(port);
sa.sin_family = AF_INET;
free(str);
return &sa;
}
/*
* converts <str> to a two struct in_addr* which are locally allocated.
* The format is "addr[/mask]", where "addr" cannot be empty, and mask
* is optionnal and either in the dotted or CIDR notation.
* Note: "addr" can also be a hostname. Returns 1 if OK, 0 if error.
*/
int str2net(char *str, struct in_addr *addr, struct in_addr *mask)
{
char *c;
unsigned long len;
memset(mask, 0, sizeof(*mask));
memset(addr, 0, sizeof(*addr));
str = strdup(str);
if ((c = strrchr(str, '/')) != NULL) {
*c++ = '\0';
/* c points to the mask */
if (strchr(c, '.') != NULL) { /* dotted notation */
if (!inet_pton(AF_INET, c, mask))
return 0;
}
else { /* mask length */
char *err;
len = strtol(c, &err, 10);
if (!*c || (err && *err) || (unsigned)len > 32)
return 0;
if (len)
mask->s_addr = htonl(~0UL << (32 - len));
else
mask->s_addr = 0;
}
}
else {
mask->s_addr = ~0UL;
}
if (!inet_pton(AF_INET, str, addr)) {
struct hostent *he;
if ((he = gethostbyname(str)) == NULL) {
return 0;
}
else
*addr = *(struct in_addr *) *(he->h_addr_list);
}
free(str);
return 1;
}
/* will try to encode the string <string> replacing all characters tagged in
* <map> with the hexadecimal representation of their ASCII-code (2 digits)
* prefixed by <escape>, and will store the result between <start> (included)
* and <stop> (excluded), and will always terminate the string with a '\0'
* before <stop>. The position of the '\0' is returned if the conversion
* completes. If bytes are missing between <start> and <stop>, then the
* conversion will be incomplete and truncated. If <stop> <= <start>, the '\0'
* cannot even be stored so we return <start> without writing the 0.
* The input string must also be zero-terminated.
*/
const char hextab[16] = "0123456789ABCDEF";
char *encode_string(char *start, char *stop,
const char escape, const fd_set *map,
const char *string)
{
if (start < stop) {
stop--; /* reserve one byte for the final '\0' */
while (start < stop && *string != '\0') {
if (!FD_ISSET((unsigned char)(*string), map))
*start++ = *string;
else {
if (start + 3 >= stop)
break;
*start++ = escape;
*start++ = hextab[(*string >> 4) & 15];
*start++ = hextab[*string & 15];
}
string++;
}
*start = '\0';
}
return start;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

462
src/stream_sock.c Normal file
View File

@ -0,0 +1,462 @@
/*
* Functions operating on SOCK_STREAM and buffers.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <haproxy/compat.h>
#include <haproxy/time.h>
#include <types/backend.h>
#include <types/buffers.h>
#include <types/global.h>
#include <types/httperr.h>
#include <types/polling.h>
#include <types/proxy.h>
#include <types/server.h>
#include <types/session.h>
#include <proto/client.h>
#include <proto/fd.h>
#include <proto/log.h>
#include <proto/proto_http.h>
#include <proto/stream_sock.h>
#include <proto/task.h>
/*
* this function is called on a read event from a client socket.
* It returns 0.
*/
int event_cli_read(int fd) {
struct task *t = fdtab[fd].owner;
struct session *s = t->context;
struct buffer *b = s->req;
int ret, max;
#ifdef DEBUG_FULL
fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s);
#endif
if (fdtab[fd].state != FD_STERROR) {
#ifdef FILL_BUFFERS
while (1)
#else
do
#endif
{
if (b->l == 0) { /* let's realign the buffer to optimize I/O */
b->r = b->w = b->h = b->lr = b->data;
max = b->rlim - b->data;
}
else if (b->r > b->w) {
max = b->rlim - b->r;
}
else {
max = b->w - b->r;
/* FIXME: theorically, if w>0, we shouldn't have rlim < data+size anymore
* since it means that the rewrite protection has been removed. This
* implies that the if statement can be removed.
*/
if (max > b->rlim - b->data)
max = b->rlim - b->data;
}
if (max == 0) { /* not anymore room to store data */
FD_CLR(fd, StaticReadEvent);
break;
}
#ifndef MSG_NOSIGNAL
{
int skerr;
socklen_t lskerr = sizeof(skerr);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
if (skerr)
ret = -1;
else
ret = recv(fd, b->r, max, 0);
}
#else
ret = recv(fd, b->r, max, MSG_NOSIGNAL);
#endif
if (ret > 0) {
b->r += ret;
b->l += ret;
s->res_cr = RES_DATA;
if (b->r == b->data + BUFSIZE) {
b->r = b->data; /* wrap around the buffer */
}
b->total += ret;
/* we hope to read more data or to get a close on next round */
continue;
}
else if (ret == 0) {
s->res_cr = RES_NULL;
break;
}
else if (errno == EAGAIN) {/* ignore EAGAIN */
break;
}
else {
s->res_cr = RES_ERROR;
fdtab[fd].state = FD_STERROR;
break;
}
} /* while(1) */
#ifndef FILL_BUFFERS
while (0);
#endif
}
else {
s->res_cr = RES_ERROR;
fdtab[fd].state = FD_STERROR;
}
if (s->res_cr != RES_SILENT) {
if (s->proxy->clitimeout && FD_ISSET(fd, StaticReadEvent))
tv_delayfrom(&s->crexpire, &now, s->proxy->clitimeout);
else
tv_eternity(&s->crexpire);
task_wakeup(&rq, t);
}
return 0;
}
/*
* this function is called on a write event from a client socket.
* It returns 0.
*/
int event_cli_write(int fd) {
struct task *t = fdtab[fd].owner;
struct session *s = t->context;
struct buffer *b = s->rep;
int ret, max;
#ifdef DEBUG_FULL
fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s);
#endif
if (b->l == 0) { /* let's realign the buffer to optimize I/O */
b->r = b->w = b->h = b->lr = b->data;
// max = BUFSIZE; BUG !!!!
max = 0;
}
else if (b->r > b->w) {
max = b->r - b->w;
}
else
max = b->data + BUFSIZE - b->w;
if (fdtab[fd].state != FD_STERROR) {
if (max == 0) {
s->res_cw = RES_NULL;
task_wakeup(&rq, t);
tv_eternity(&s->cwexpire);
FD_CLR(fd, StaticWriteEvent);
return 0;
}
#ifndef MSG_NOSIGNAL
{
int skerr;
socklen_t lskerr = sizeof(skerr);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
if (skerr)
ret = -1;
else
ret = send(fd, b->w, max, MSG_DONTWAIT);
}
#else
ret = send(fd, b->w, max, MSG_DONTWAIT | MSG_NOSIGNAL);
#endif
if (ret > 0) {
b->l -= ret;
b->w += ret;
s->res_cw = RES_DATA;
if (b->w == b->data + BUFSIZE) {
b->w = b->data; /* wrap around the buffer */
}
}
else if (ret == 0) {
/* nothing written, just make as if we were never called */
// s->res_cw = RES_NULL;
return 0;
}
else if (errno == EAGAIN) /* ignore EAGAIN */
return 0;
else {
s->res_cw = RES_ERROR;
fdtab[fd].state = FD_STERROR;
}
}
else {
s->res_cw = RES_ERROR;
fdtab[fd].state = FD_STERROR;
}
if (s->proxy->clitimeout) {
tv_delayfrom(&s->cwexpire, &now, s->proxy->clitimeout);
/* FIXME: to prevent the client from expiring read timeouts during writes,
* we refresh it. A solution would be to merge read+write timeouts into a
* unique one, although that needs some study particularly on full-duplex
* TCP connections. */
s->crexpire = s->cwexpire;
}
else
tv_eternity(&s->cwexpire);
task_wakeup(&rq, t);
return 0;
}
/*
* this function is called on a read event from a server socket.
* It returns 0.
*/
int event_srv_read(int fd) {
struct task *t = fdtab[fd].owner;
struct session *s = t->context;
struct buffer *b = s->rep;
int ret, max;
#ifdef DEBUG_FULL
fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s);
#endif
if (fdtab[fd].state != FD_STERROR) {
#ifdef FILL_BUFFERS
while (1)
#else
do
#endif
{
if (b->l == 0) { /* let's realign the buffer to optimize I/O */
b->r = b->w = b->h = b->lr = b->data;
max = b->rlim - b->data;
}
else if (b->r > b->w) {
max = b->rlim - b->r;
}
else {
max = b->w - b->r;
/* FIXME: theorically, if w>0, we shouldn't have rlim < data+size anymore
* since it means that the rewrite protection has been removed. This
* implies that the if statement can be removed.
*/
if (max > b->rlim - b->data)
max = b->rlim - b->data;
}
if (max == 0) { /* not anymore room to store data */
FD_CLR(fd, StaticReadEvent);
break;
}
#ifndef MSG_NOSIGNAL
{
int skerr;
socklen_t lskerr = sizeof(skerr);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
if (skerr)
ret = -1;
else
ret = recv(fd, b->r, max, 0);
}
#else
ret = recv(fd, b->r, max, MSG_NOSIGNAL);
#endif
if (ret > 0) {
b->r += ret;
b->l += ret;
s->res_sr = RES_DATA;
if (b->r == b->data + BUFSIZE) {
b->r = b->data; /* wrap around the buffer */
}
b->total += ret;
/* we hope to read more data or to get a close on next round */
continue;
}
else if (ret == 0) {
s->res_sr = RES_NULL;
break;
}
else if (errno == EAGAIN) {/* ignore EAGAIN */
break;
}
else {
s->res_sr = RES_ERROR;
fdtab[fd].state = FD_STERROR;
break;
}
} /* while(1) */
#ifndef FILL_BUFFERS
while (0);
#endif
}
else {
s->res_sr = RES_ERROR;
fdtab[fd].state = FD_STERROR;
}
if (s->res_sr != RES_SILENT) {
if (s->proxy->srvtimeout && FD_ISSET(fd, StaticReadEvent))
tv_delayfrom(&s->srexpire, &now, s->proxy->srvtimeout);
else
tv_eternity(&s->srexpire);
task_wakeup(&rq, t);
}
return 0;
}
/*
* this function is called on a write event from a server socket.
* It returns 0.
*/
int event_srv_write(int fd) {
struct task *t = fdtab[fd].owner;
struct session *s = t->context;
struct buffer *b = s->req;
int ret, max;
#ifdef DEBUG_FULL
fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s);
#endif
if (b->l == 0) { /* let's realign the buffer to optimize I/O */
b->r = b->w = b->h = b->lr = b->data;
// max = BUFSIZE; BUG !!!!
max = 0;
}
else if (b->r > b->w) {
max = b->r - b->w;
}
else
max = b->data + BUFSIZE - b->w;
if (fdtab[fd].state != FD_STERROR) {
if (max == 0) {
/* may be we have received a connection acknowledgement in TCP mode without data */
if (s->srv_state == SV_STCONN) {
int skerr;
socklen_t lskerr = sizeof(skerr);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
if (skerr) {
s->res_sw = RES_ERROR;
fdtab[fd].state = FD_STERROR;
task_wakeup(&rq, t);
tv_eternity(&s->swexpire);
FD_CLR(fd, StaticWriteEvent);
return 0;
}
}
s->res_sw = RES_NULL;
task_wakeup(&rq, t);
fdtab[fd].state = FD_STREADY;
tv_eternity(&s->swexpire);
FD_CLR(fd, StaticWriteEvent);
return 0;
}
#ifndef MSG_NOSIGNAL
{
int skerr;
socklen_t lskerr = sizeof(skerr);
getsockopt(fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr);
if (skerr)
ret = -1;
else
ret = send(fd, b->w, max, MSG_DONTWAIT);
}
#else
ret = send(fd, b->w, max, MSG_DONTWAIT | MSG_NOSIGNAL);
#endif
fdtab[fd].state = FD_STREADY;
if (ret > 0) {
b->l -= ret;
b->w += ret;
s->res_sw = RES_DATA;
if (b->w == b->data + BUFSIZE) {
b->w = b->data; /* wrap around the buffer */
}
}
else if (ret == 0) {
/* nothing written, just make as if we were never called */
// s->res_sw = RES_NULL;
return 0;
}
else if (errno == EAGAIN) /* ignore EAGAIN */
return 0;
else {
s->res_sw = RES_ERROR;
fdtab[fd].state = FD_STERROR;
}
}
else {
s->res_sw = RES_ERROR;
fdtab[fd].state = FD_STERROR;
}
/* We don't want to re-arm read/write timeouts if we're trying to connect,
* otherwise it could loop indefinitely !
*/
if (s->srv_state != SV_STCONN) {
if (s->proxy->srvtimeout) {
tv_delayfrom(&s->swexpire, &now, s->proxy->srvtimeout);
/* FIXME: to prevent the server from expiring read timeouts during writes,
* we refresh it. A solution would be to merge read+write+connect timeouts
* into a unique one since we don't mind expiring on read or write, and none
* of them is enabled while waiting for connect(), although that needs some
* study particularly on full-duplex TCP connections. */
s->srexpire = s->swexpire;
}
else
tv_eternity(&s->swexpire);
}
task_wakeup(&rq, t);
return 0;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

201
src/task.c Normal file
View File

@ -0,0 +1,201 @@
/*
* Task management functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <haproxy/config.h>
#include <haproxy/mini-clist.h>
#include <haproxy/time.h>
#include <proto/task.h>
/* FIXME : this should be removed very quickly ! */
extern int maintain_proxies(void);
void **pool_task= NULL;
struct task *rq = NULL; /* global run queue */
struct task wait_queue[2] = { /* global wait queue */
{
prev:LIST_HEAD(wait_queue[0]), /* expirable tasks */
next:LIST_HEAD(wait_queue[0]),
},
{
prev:LIST_HEAD(wait_queue[1]), /* non-expirable tasks */
next:LIST_HEAD(wait_queue[1]),
},
};
/* inserts <task> into its assigned wait queue, where it may already be. In this case, it
* may be only moved or left where it was, depending on its timing requirements.
* <task> is returned.
*/
struct task *task_queue(struct task *task)
{
struct task *list = task->wq;
struct task *start_from;
/* This is a very dirty hack to queue non-expirable tasks in another queue
* in order to avoid pulluting the tail of the standard queue. This will go
* away with the new O(log(n)) scheduler anyway.
*/
if (tv_iseternity(&task->expire)) {
/* if the task was queued in the standard wait queue, we must dequeue it */
if (task->prev) {
if (task->wq == LIST_HEAD(wait_queue[1]))
return task;
else {
task_delete(task);
task->prev = NULL;
}
}
list = task->wq = LIST_HEAD(wait_queue[1]);
} else {
/* if the task was queued in the eternity queue, we must dequeue it */
if (task->prev && (task->wq == LIST_HEAD(wait_queue[1]))) {
task_delete(task);
task->prev = NULL;
list = task->wq = LIST_HEAD(wait_queue[0]);
}
}
/* next, test if the task was already in a list */
if (task->prev == NULL) {
// start_from = list;
start_from = list->prev;
/* insert the unlinked <task> into the list, searching back from the last entry */
while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
start_from = start_from->prev;
}
// while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
// start_from = start_from->next;
// stats_tsk_nsrch++;
// }
}
else if (task->prev == list ||
tv_cmp2(&task->expire, &task->prev->expire) >= 0) { /* walk right */
start_from = task->next;
if (start_from == list || tv_cmp2(&task->expire, &start_from->expire) <= 0) {
return task; /* it's already in the right place */
}
/* if the task is not at the right place, there's little chance that
* it has only shifted a bit, and it will nearly always be queued
* at the end of the list because of constant timeouts
* (observed in real case).
*/
#ifndef WE_REALLY_THINK_THAT_THIS_TASK_MAY_HAVE_SHIFTED
start_from = list->prev; /* assume we'll queue to the end of the list */
while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
start_from = start_from->prev;
}
#else /* WE_REALLY_... */
/* insert the unlinked <task> into the list, searching after position <start_from> */
while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
start_from = start_from->next;
}
#endif /* WE_REALLY_... */
/* we need to unlink it now */
task_delete(task);
}
else { /* walk left. */
#ifdef LEFT_TO_TOP /* not very good */
start_from = list;
while (start_from->next != list && tv_cmp2(&task->expire, &start_from->next->expire) > 0) {
start_from = start_from->next;
}
#else
start_from = task->prev->prev; /* valid because of the previous test above */
while (start_from != list && tv_cmp2(&task->expire, &start_from->expire) < 0) {
start_from = start_from->prev;
}
#endif
/* we need to unlink it now */
task_delete(task);
}
task->prev = start_from;
task->next = start_from->next;
task->next->prev = task;
start_from->next = task;
return task;
}
/*
* This does 4 things :
* - wake up all expired tasks
* - call all runnable tasks
* - call maintain_proxies() to enable/disable the listeners
* - return the delay till next event in ms, -1 = wait indefinitely
* Note: this part should be rewritten with the O(ln(n)) scheduler.
*
*/
int process_runnable_tasks()
{
int next_time;
int time2;
struct task *t, *tnext;
next_time = TIME_ETERNITY; /* set the timer to wait eternally first */
/* look for expired tasks and add them to the run queue.
*/
tnext = ((struct task *)LIST_HEAD(wait_queue[0]))->next;
while ((t = tnext) != LIST_HEAD(wait_queue[0])) { /* we haven't looped ? */
tnext = t->next;
if (t->state & TASK_RUNNING)
continue;
if (tv_iseternity(&t->expire))
continue;
/* wakeup expired entries. It doesn't matter if they are
* already running because of a previous event
*/
if (tv_cmp_ms(&t->expire, &now) <= 0) {
task_wakeup(&rq, t);
}
else {
/* first non-runnable task. Use its expiration date as an upper bound */
int temp_time = tv_remain(&now, &t->expire);
if (temp_time)
next_time = temp_time;
break;
}
}
/* process each task in the run queue now. Each task may be deleted
* since we only use the run queue's head. Note that any task can be
* woken up by any other task and it will be processed immediately
* after as it will be queued on the run queue's head.
*/
while ((t = rq) != NULL) {
int temp_time;
task_sleep(&rq, t);
temp_time = t->process(t);
next_time = MINTIME(temp_time, next_time);
}
/* maintain all proxies in a consistent state. This should quickly become a task */
time2 = maintain_proxies();
return MINTIME(time2, next_time);
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

172
src/time.c Normal file
View File

@ -0,0 +1,172 @@
/*
* Time calculation functions.
*
* Copyright 2000-2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
*/
#include <sys/time.h>
#include <haproxy/time.h>
struct timeval now; /* the current date at any moment */
struct timeval start_date; /* the process's start date */
/*
* adds <ms> ms to <from>, set the result to <tv> and returns a pointer <tv>
*/
struct timeval *tv_delayfrom(struct timeval *tv, struct timeval *from, int ms)
{
if (!tv || !from)
return NULL;
tv->tv_usec = from->tv_usec + (ms%1000)*1000;
tv->tv_sec = from->tv_sec + (ms/1000);
while (tv->tv_usec >= 1000000) {
tv->tv_usec -= 1000000;
tv->tv_sec++;
}
return tv;
}
/*
* compares <tv1> and <tv2> modulo 1ms: returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2
* Must not be used when either argument is eternity. Use tv_cmp2_ms() for that.
*/
int tv_cmp_ms(struct timeval *tv1, struct timeval *tv2)
{
if (tv1->tv_sec == tv2->tv_sec) {
if (tv2->tv_usec >= tv1->tv_usec + 1000)
return -1;
else if (tv1->tv_usec >= tv2->tv_usec + 1000)
return 1;
else
return 0;
}
else if ((tv2->tv_sec > tv1->tv_sec + 1) ||
((tv2->tv_sec == tv1->tv_sec + 1) && (tv2->tv_usec + 1000000 >= tv1->tv_usec + 1000)))
return -1;
else if ((tv1->tv_sec > tv2->tv_sec + 1) ||
((tv1->tv_sec == tv2->tv_sec + 1) && (tv1->tv_usec + 1000000 >= tv2->tv_usec + 1000)))
return 1;
else
return 0;
}
/*
* compares <tv1> and <tv2> : returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2,
* considering that 0 is the eternity.
*/
int tv_cmp2(struct timeval *tv1, struct timeval *tv2)
{
if (tv_iseternity(tv1))
if (tv_iseternity(tv2))
return 0; /* same */
else
return 1; /* tv1 later than tv2 */
else if (tv_iseternity(tv2))
return -1; /* tv2 later than tv1 */
if (tv1->tv_sec > tv2->tv_sec)
return 1;
else if (tv1->tv_sec < tv2->tv_sec)
return -1;
else if (tv1->tv_usec > tv2->tv_usec)
return 1;
else if (tv1->tv_usec < tv2->tv_usec)
return -1;
else
return 0;
}
/*
* compares <tv1> and <tv2> modulo 1 ms: returns 0 if equal, -1 if tv1 < tv2, 1 if tv1 > tv2,
* considering that 0 is the eternity.
*/
int tv_cmp2_ms(struct timeval *tv1, struct timeval *tv2)
{
if (tv_iseternity(tv1))
if (tv_iseternity(tv2))
return 0; /* same */
else
return 1; /* tv1 later than tv2 */
else if (tv_iseternity(tv2))
return -1; /* tv2 later than tv1 */
if (tv1->tv_sec == tv2->tv_sec) {
if (tv1->tv_usec >= tv2->tv_usec + 1000)
return 1;
else if (tv2->tv_usec >= tv1->tv_usec + 1000)
return -1;
else
return 0;
}
else if ((tv1->tv_sec > tv2->tv_sec + 1) ||
((tv1->tv_sec == tv2->tv_sec + 1) && (tv1->tv_usec + 1000000 >= tv2->tv_usec + 1000)))
return 1;
else if ((tv2->tv_sec > tv1->tv_sec + 1) ||
((tv2->tv_sec == tv1->tv_sec + 1) && (tv2->tv_usec + 1000000 >= tv1->tv_usec + 1000)))
return -1;
else
return 0;
}
/*
* returns the remaining time between tv1=now and event=tv2
* if tv2 is passed, 0 is returned.
* Returns TIME_ETERNITY if tv2 is eternity.
*/
unsigned long tv_remain2(struct timeval *tv1, struct timeval *tv2)
{
unsigned long ret;
if (tv_iseternity(tv2))
return TIME_ETERNITY;
if (tv_cmp_ms(tv1, tv2) >= 0)
return 0; /* event elapsed */
ret = (tv2->tv_sec - tv1->tv_sec) * 1000;
if (tv2->tv_usec > tv1->tv_usec)
ret += (tv2->tv_usec - tv1->tv_usec) / 1000;
else
ret -= (tv1->tv_usec - tv2->tv_usec) / 1000;
return (unsigned long) ret;
}
/*
* returns the absolute difference, in ms, between tv1 and tv2
* Must not be used when either argument is eternity.
*/
unsigned long tv_delta(struct timeval *tv1, struct timeval *tv2)
{
int cmp;
unsigned long ret;
cmp = tv_cmp(tv1, tv2);
if (!cmp)
return 0; /* same dates, null diff */
else if (cmp < 0) {
struct timeval *tmp = tv1;
tv1 = tv2;
tv2 = tmp;
}
ret = (tv1->tv_sec - tv2->tv_sec) * 1000;
if (tv1->tv_usec > tv2->tv_usec)
ret += (tv1->tv_usec - tv2->tv_usec) / 1000;
else
ret -= (tv2->tv_usec - tv1->tv_usec) / 1000;
return (unsigned long) ret;
}
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* End:
*/

View File

@ -1,7 +1,7 @@
/*
* URI-based user authentication using the HTTP basic method.
*
* Copyright 2006 Willy Tarreau <willy@w.ods.org>
* Copyright 2006 Willy Tarreau <w@1wt.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -12,8 +12,9 @@
#include <stdlib.h>
#include <string.h>
#include <include/uri_auth.h>
#include <include/base64.h>
#include <haproxy/base64.h>
#include <haproxy/uri_auth.h>
/*