mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 23:27:04 +02:00
The target is per connection and is directly used by the connection, so we need it there. It's not needed anymore in the SI however.
1495 lines
44 KiB
C
1495 lines
44 KiB
C
/*
|
|
* Stick table synchro management.
|
|
*
|
|
* Copyright 2010 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
|
|
*
|
|
* 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 <string.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <common/compat.h>
|
|
#include <common/config.h>
|
|
#include <common/time.h>
|
|
|
|
#include <types/global.h>
|
|
#include <types/peers.h>
|
|
|
|
#include <proto/acl.h>
|
|
#include <proto/channel.h>
|
|
#include <proto/fd.h>
|
|
#include <proto/log.h>
|
|
#include <proto/hdr_idx.h>
|
|
#include <proto/protocols.h>
|
|
#include <proto/proto_tcp.h>
|
|
#include <proto/proto_http.h>
|
|
#include <proto/proxy.h>
|
|
#include <proto/session.h>
|
|
#include <proto/stream_interface.h>
|
|
#include <proto/task.h>
|
|
#include <proto/stick_table.h>
|
|
#include <proto/signal.h>
|
|
|
|
|
|
/*******************************/
|
|
/* Current peer learning state */
|
|
/*******************************/
|
|
|
|
/******************************/
|
|
/* Current table resync state */
|
|
/******************************/
|
|
#define SHTABLE_F_RESYNC_LOCAL 0x00000001 /* Learn from local finished or no more needed */
|
|
#define SHTABLE_F_RESYNC_REMOTE 0x00000002 /* Learn from remote finished or no more needed */
|
|
#define SHTABLE_F_RESYNC_ASSIGN 0x00000004 /* A peer was assigned to learn our lesson */
|
|
#define SHTABLE_F_RESYNC_PROCESS 0x00000008 /* The assigned peer was requested for resync */
|
|
#define SHTABLE_F_DONOTSTOP 0x00010000 /* Main table sync task block process during soft stop
|
|
to push data to new process */
|
|
|
|
#define SHTABLE_RESYNC_STATEMASK (SHTABLE_F_RESYNC_LOCAL|SHTABLE_F_RESYNC_REMOTE)
|
|
#define SHTABLE_RESYNC_FROMLOCAL 0x00000000
|
|
#define SHTABLE_RESYNC_FROMREMOTE SHTABLE_F_RESYNC_LOCAL
|
|
#define SHTABLE_RESYNC_FINISHED (SHTABLE_F_RESYNC_LOCAL|SHTABLE_F_RESYNC_REMOTE)
|
|
|
|
/******************************/
|
|
/* Remote peer teaching state */
|
|
/******************************/
|
|
#define PEER_F_TEACH_PROCESS 0x00000001 /* Teach a lesson to current peer */
|
|
#define PEER_F_TEACH_STAGE1 0x00000002 /* Teach state 1 complete */
|
|
#define PEER_F_TEACH_STAGE2 0x00000004 /* Teach stage 2 complete */
|
|
#define PEER_F_TEACH_FINISHED 0x00000008 /* Teach conclude, (wait for confirm) */
|
|
#define PEER_F_TEACH_COMPLETE 0x00000010 /* All that we know already taught to current peer, used only for a local peer */
|
|
#define PEER_F_LEARN_ASSIGN 0x00000100 /* Current peer was assigned for a lesson */
|
|
#define PEER_F_LEARN_NOTUP2DATE 0x00000200 /* Learn from peer finished but peer is not up to date */
|
|
|
|
#define PEER_TEACH_RESET ~(PEER_F_TEACH_PROCESS|PEER_F_TEACH_STAGE1|PEER_F_TEACH_STAGE2|PEER_F_TEACH_FINISHED) /* PEER_F_TEACH_COMPLETE should never be reset */
|
|
#define PEER_LEARN_RESET ~(PEER_F_LEARN_ASSIGN|PEER_F_LEARN_NOTUP2DATE)
|
|
|
|
|
|
/**********************************/
|
|
/* Peer Session IO handler states */
|
|
/**********************************/
|
|
|
|
#define PEER_SESSION_ACCEPT 1000 /* Initial state for session create by an accept */
|
|
#define PEER_SESSION_GETVERSION 1001 /* Validate supported protocol version*/
|
|
#define PEER_SESSION_GETHOST 1002 /* Validate host ID correspond to local host id */
|
|
#define PEER_SESSION_GETPEER 1003 /* Validate peer ID correspond to a known remote peer id */
|
|
#define PEER_SESSION_GETTABLE 1004 /* Search into registered table for a table with same id and
|
|
validate type and size */
|
|
#define PEER_SESSION_SENDSUCCESS 1005 /* Send ret code 200 (success) and wait for message */
|
|
/* next state is WAITMSG */
|
|
|
|
#define PEER_SESSION_CONNECT 2000 /* Initial state for session create on a connect,
|
|
push presentation into buffer */
|
|
#define PEER_SESSION_GETSTATUS 2001 /* Wait for the welcome message */
|
|
#define PEER_SESSION_WAITMSG 2002 /* Wait for datamessages*/
|
|
/* loop on WAITMSG */
|
|
|
|
#define PEER_SESSION_EXIT 10000 /* Exit with status code */
|
|
#define PEER_SESSION_END 10001 /* Killed session */
|
|
/* session ended */
|
|
|
|
|
|
/**********************************/
|
|
/* Peer Session status code */
|
|
/**********************************/
|
|
|
|
#define PEER_SESSION_CONNECTCODE 100 /* connect in progress */
|
|
#define PEER_SESSION_CONNECTEDCODE 110 /* tcp connect success */
|
|
|
|
#define PEER_SESSION_SUCCESSCODE 200 /* accept or connect successful */
|
|
|
|
#define PEER_SESSION_TRYAGAIN 300 /* try again later */
|
|
|
|
#define PEER_SESSION_ERRPROTO 501 /* error protocol */
|
|
#define PEER_SESSION_ERRVERSION 502 /* unknown protocol version */
|
|
#define PEER_SESSION_ERRHOST 503 /* bad host name */
|
|
#define PEER_SESSION_ERRPEER 504 /* unknown peer */
|
|
#define PEER_SESSION_ERRTYPE 505 /* table key type mismatch */
|
|
#define PEER_SESSION_ERRSIZE 506 /* table key size mismatch */
|
|
#define PEER_SESSION_ERRTABLE 507 /* unknown table */
|
|
|
|
#define PEER_SESSION_PROTO_NAME "HAProxyS"
|
|
|
|
struct peers *peers = NULL;
|
|
static void peer_session_forceshutdown(struct session * session);
|
|
|
|
|
|
/*
|
|
* This prepare the data update message of the stick session <ts>, <ps> is the the peer session
|
|
* where the data going to be pushed, <msg> is a buffer of <size> to recieve data message content
|
|
*/
|
|
static int peer_prepare_datamsg(struct stksess *ts, struct peer_session *ps, char *msg, size_t size)
|
|
{
|
|
uint32_t netinteger;
|
|
int len;
|
|
/* construct message */
|
|
if (ps->lastpush && ts->upd.key > ps->lastpush && (ts->upd.key - ps->lastpush) <= 127) {
|
|
msg[0] = 0x80 + ts->upd.key - ps->lastpush;
|
|
len = sizeof(char);
|
|
}
|
|
else {
|
|
msg[0] = 'D';
|
|
netinteger = htonl(ts->upd.key);
|
|
memcpy(&msg[sizeof(char)], &netinteger, sizeof(netinteger));
|
|
len = sizeof(char) + sizeof(netinteger);
|
|
}
|
|
|
|
if (ps->table->table->type == STKTABLE_TYPE_STRING) {
|
|
int stlen = strlen((char *)ts->key.key);
|
|
|
|
netinteger = htonl(strlen((char *)ts->key.key));
|
|
memcpy(&msg[len], &netinteger, sizeof(netinteger));
|
|
memcpy(&msg[len+sizeof(netinteger)], ts->key.key, stlen);
|
|
len += sizeof(netinteger) + stlen;
|
|
|
|
}
|
|
else if (ps->table->table->type == STKTABLE_TYPE_INTEGER) {
|
|
netinteger = htonl(*((uint32_t *)ts->key.key));
|
|
memcpy(&msg[len], &netinteger, sizeof(netinteger));
|
|
len += sizeof(netinteger);
|
|
}
|
|
else {
|
|
memcpy(&msg[len], ts->key.key, ps->table->table->key_size);
|
|
len += ps->table->table->key_size;
|
|
}
|
|
|
|
if (stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID))
|
|
netinteger = htonl(stktable_data_cast(stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID), server_id));
|
|
else
|
|
netinteger = 0;
|
|
|
|
memcpy(&msg[len], &netinteger , sizeof(netinteger));
|
|
len += sizeof(netinteger);
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
/*
|
|
* Callback to release a session with a peer
|
|
*/
|
|
static void peer_session_release(struct stream_interface *si)
|
|
{
|
|
struct task *t = (struct task *)si->owner;
|
|
struct session *s = (struct session *)t->context;
|
|
struct peer_session *ps = (struct peer_session *)si->conn.data_ctx;
|
|
|
|
/* si->conn.data_ctx is not a peer session */
|
|
if (si->applet.st0 < PEER_SESSION_SENDSUCCESS)
|
|
return;
|
|
|
|
/* peer session identified */
|
|
if (ps) {
|
|
if (ps->session == s) {
|
|
ps->session = NULL;
|
|
if (ps->flags & PEER_F_LEARN_ASSIGN) {
|
|
/* unassign current peer for learning */
|
|
ps->flags &= ~(PEER_F_LEARN_ASSIGN);
|
|
ps->table->flags &= ~(SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS);
|
|
|
|
/* reschedule a resync */
|
|
ps->table->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
|
|
}
|
|
/* reset teaching and learning flags to 0 */
|
|
ps->flags &= PEER_TEACH_RESET;
|
|
ps->flags &= PEER_LEARN_RESET;
|
|
}
|
|
task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* IO Handler to handle message exchance with a peer
|
|
*/
|
|
static void peer_io_handler(struct stream_interface *si)
|
|
{
|
|
struct task *t= (struct task *)si->owner;
|
|
struct session *s = (struct session *)t->context;
|
|
struct peers *curpeers = (struct peers *)s->fe->parent;
|
|
int reql = 0;
|
|
int repl = 0;
|
|
|
|
while (1) {
|
|
switchstate:
|
|
switch(si->applet.st0) {
|
|
case PEER_SESSION_ACCEPT:
|
|
si->conn.data_ctx = NULL;
|
|
si->applet.st0 = PEER_SESSION_GETVERSION;
|
|
/* fall through */
|
|
case PEER_SESSION_GETVERSION:
|
|
reql = bo_getline(si->ob, trash, trashlen);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
if (trash[reql-1] != '\n') {
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
else if (reql > 1 && (trash[reql-2] == '\r'))
|
|
trash[reql-2] = 0;
|
|
else
|
|
trash[reql-1] = 0;
|
|
|
|
bo_skip(si->ob, reql);
|
|
|
|
/* test version */
|
|
if (strcmp(PEER_SESSION_PROTO_NAME " 1.0", trash) != 0) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRVERSION;
|
|
/* test protocol */
|
|
if (strncmp(PEER_SESSION_PROTO_NAME " ", trash, strlen(PEER_SESSION_PROTO_NAME)+1) != 0)
|
|
si->applet.st1 = PEER_SESSION_ERRPROTO;
|
|
goto switchstate;
|
|
}
|
|
|
|
si->applet.st0 = PEER_SESSION_GETHOST;
|
|
/* fall through */
|
|
case PEER_SESSION_GETHOST:
|
|
reql = bo_getline(si->ob, trash, trashlen);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
if (trash[reql-1] != '\n') {
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
else if (reql > 1 && (trash[reql-2] == '\r'))
|
|
trash[reql-2] = 0;
|
|
else
|
|
trash[reql-1] = 0;
|
|
|
|
bo_skip(si->ob, reql);
|
|
|
|
/* test hostname match */
|
|
if (strcmp(localpeer, trash) != 0) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRHOST;
|
|
goto switchstate;
|
|
}
|
|
|
|
si->applet.st0 = PEER_SESSION_GETPEER;
|
|
/* fall through */
|
|
case PEER_SESSION_GETPEER: {
|
|
struct peer *curpeer;
|
|
char *p;
|
|
reql = bo_getline(si->ob, trash, trashlen);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
if (trash[reql-1] != '\n') {
|
|
/* Incomplete line, we quit */
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
else if (reql > 1 && (trash[reql-2] == '\r'))
|
|
trash[reql-2] = 0;
|
|
else
|
|
trash[reql-1] = 0;
|
|
|
|
bo_skip(si->ob, reql);
|
|
|
|
/* parse line "<peer name> <pid>" */
|
|
p = strchr(trash, ' ');
|
|
if (!p) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRPROTO;
|
|
goto switchstate;
|
|
}
|
|
*p = 0;
|
|
|
|
/* lookup known peer */
|
|
for (curpeer = curpeers->remote; curpeer; curpeer = curpeer->next) {
|
|
if (strcmp(curpeer->id, trash) == 0)
|
|
break;
|
|
}
|
|
|
|
/* if unknown peer */
|
|
if (!curpeer) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRPEER;
|
|
goto switchstate;
|
|
}
|
|
|
|
si->conn.data_ctx = curpeer;
|
|
si->applet.st0 = PEER_SESSION_GETTABLE;
|
|
/* fall through */
|
|
}
|
|
case PEER_SESSION_GETTABLE: {
|
|
struct peer *curpeer = (struct peer *)si->conn.data_ctx;
|
|
struct shared_table *st;
|
|
struct peer_session *ps = NULL;
|
|
unsigned long key_type;
|
|
size_t key_size;
|
|
char *p;
|
|
|
|
reql = bo_getline(si->ob, trash, trashlen);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0)
|
|
goto out;
|
|
si->conn.data_ctx = NULL;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
/* Re init si->conn.data_ctx to null, to handle correctly a release case */
|
|
si->conn.data_ctx = NULL;
|
|
|
|
if (trash[reql-1] != '\n') {
|
|
/* Incomplete line, we quit */
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
else if (reql > 1 && (trash[reql-2] == '\r'))
|
|
trash[reql-2] = 0;
|
|
else
|
|
trash[reql-1] = 0;
|
|
|
|
bo_skip(si->ob, reql);
|
|
|
|
/* Parse line "<table name> <type> <size>" */
|
|
p = strchr(trash, ' ');
|
|
if (!p) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRPROTO;
|
|
goto switchstate;
|
|
}
|
|
*p = 0;
|
|
key_type = (unsigned long)atol(p+1);
|
|
|
|
p = strchr(p+1, ' ');
|
|
if (!p) {
|
|
si->conn.data_ctx = NULL;
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRPROTO;
|
|
goto switchstate;
|
|
}
|
|
|
|
key_size = (size_t)atoi(p);
|
|
for (st = curpeers->tables; st; st = st->next) {
|
|
/* If table name matches */
|
|
if (strcmp(st->table->id, trash) == 0) {
|
|
/* If key size mismatches */
|
|
if (key_size != st->table->key_size) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRSIZE;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* If key type mismatches */
|
|
if (key_type != st->table->type) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRTYPE;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* lookup peer session of current peer */
|
|
for (ps = st->sessions; ps; ps = ps->next) {
|
|
if (ps->peer == curpeer) {
|
|
/* If session already active, replaced by new one */
|
|
if (ps->session && ps->session != s) {
|
|
if (ps->peer->local) {
|
|
/* Local connection, reply a retry */
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_TRYAGAIN;
|
|
goto switchstate;
|
|
}
|
|
peer_session_forceshutdown(ps->session);
|
|
}
|
|
ps->session = s;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If table not found */
|
|
if (!st){
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRTABLE;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* If no peer session for current peer */
|
|
if (!ps) {
|
|
si->applet.st0 = PEER_SESSION_EXIT;
|
|
si->applet.st1 = PEER_SESSION_ERRPEER;
|
|
goto switchstate;
|
|
}
|
|
|
|
si->conn.data_ctx = ps;
|
|
si->applet.st0 = PEER_SESSION_SENDSUCCESS;
|
|
/* fall through */
|
|
}
|
|
case PEER_SESSION_SENDSUCCESS:{
|
|
struct peer_session *ps = (struct peer_session *)si->conn.data_ctx;
|
|
|
|
repl = snprintf(trash, trashlen, "%d\n", PEER_SESSION_SUCCESSCODE);
|
|
repl = bi_putblk(si->ib, trash, repl);
|
|
if (repl <= 0) {
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* Register status code */
|
|
ps->statuscode = PEER_SESSION_SUCCESSCODE;
|
|
|
|
/* Awake main task */
|
|
task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG);
|
|
|
|
/* Init cursors */
|
|
ps->teaching_origin =ps->lastpush = ps->lastack = ps->pushack = 0;
|
|
ps->pushed = ps->update;
|
|
|
|
/* Init confirm counter */
|
|
ps->confirm = 0;
|
|
|
|
/* reset teaching and learning flags to 0 */
|
|
ps->flags &= PEER_TEACH_RESET;
|
|
ps->flags &= PEER_LEARN_RESET;
|
|
|
|
/* if current peer is local */
|
|
if (ps->peer->local) {
|
|
/* if table need resyncfrom local and no process assined */
|
|
if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMLOCAL &&
|
|
!(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) {
|
|
/* assign local peer for a lesson, consider lesson already requested */
|
|
ps->flags |= PEER_F_LEARN_ASSIGN;
|
|
ps->table->flags |= (SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS);
|
|
}
|
|
|
|
}
|
|
else if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE &&
|
|
!(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) {
|
|
/* assign peer for a lesson */
|
|
ps->flags |= PEER_F_LEARN_ASSIGN;
|
|
ps->table->flags |= SHTABLE_F_RESYNC_ASSIGN;
|
|
}
|
|
/* switch to waiting message state */
|
|
si->applet.st0 = PEER_SESSION_WAITMSG;
|
|
goto switchstate;
|
|
}
|
|
case PEER_SESSION_CONNECT: {
|
|
struct peer_session *ps = (struct peer_session *)si->conn.data_ctx;
|
|
|
|
/* Send headers */
|
|
repl = snprintf(trash, trashlen,
|
|
PEER_SESSION_PROTO_NAME " 1.0\n%s\n%s %d\n%s %lu %d\n",
|
|
ps->peer->id,
|
|
localpeer,
|
|
(int)getpid(),
|
|
ps->table->table->id,
|
|
ps->table->table->type,
|
|
(int)ps->table->table->key_size);
|
|
|
|
if (repl >= trashlen) {
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
|
|
repl = bi_putblk(si->ib, trash, repl);
|
|
if (repl <= 0) {
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* switch to the waiting statuscode state */
|
|
si->applet.st0 = PEER_SESSION_GETSTATUS;
|
|
/* fall through */
|
|
}
|
|
case PEER_SESSION_GETSTATUS: {
|
|
struct peer_session *ps = (struct peer_session *)si->conn.data_ctx;
|
|
|
|
if (si->ib->flags & CF_WRITE_PARTIAL)
|
|
ps->statuscode = PEER_SESSION_CONNECTEDCODE;
|
|
|
|
reql = bo_getline(si->ob, trash, trashlen);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
if (trash[reql-1] != '\n') {
|
|
/* Incomplete line, we quit */
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
else if (reql > 1 && (trash[reql-2] == '\r'))
|
|
trash[reql-2] = 0;
|
|
else
|
|
trash[reql-1] = 0;
|
|
|
|
bo_skip(si->ob, reql);
|
|
|
|
/* Register status code */
|
|
ps->statuscode = atoi(trash);
|
|
|
|
/* Awake main task */
|
|
task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG);
|
|
|
|
/* If status code is success */
|
|
if (ps->statuscode == PEER_SESSION_SUCCESSCODE) {
|
|
/* Init cursors */
|
|
ps->teaching_origin = ps->lastpush = ps->lastack = ps->pushack = 0;
|
|
ps->pushed = ps->update;
|
|
|
|
/* Init confirm counter */
|
|
ps->confirm = 0;
|
|
|
|
/* reset teaching and learning flags to 0 */
|
|
ps->flags &= PEER_TEACH_RESET;
|
|
ps->flags &= PEER_LEARN_RESET;
|
|
|
|
/* If current peer is local */
|
|
if (ps->peer->local) {
|
|
/* Init cursors to push a resync */
|
|
ps->teaching_origin = ps->pushed = ps->table->table->update;
|
|
/* flag to start to teach lesson */
|
|
ps->flags |= PEER_F_TEACH_PROCESS;
|
|
|
|
}
|
|
else if ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE &&
|
|
!(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN)) {
|
|
/* If peer is remote and resync from remote is needed,
|
|
and no peer currently assigned */
|
|
|
|
/* assign peer for a lesson */
|
|
ps->flags |= PEER_F_LEARN_ASSIGN;
|
|
ps->table->flags |= SHTABLE_F_RESYNC_ASSIGN;
|
|
}
|
|
|
|
}
|
|
else {
|
|
/* Status code is not success, abort */
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_WAITMSG;
|
|
/* fall through */
|
|
}
|
|
case PEER_SESSION_WAITMSG: {
|
|
struct peer_session *ps = (struct peer_session *)si->conn.data_ctx;
|
|
char c;
|
|
int totl = 0;
|
|
|
|
reql = bo_getblk(si->ob, (char *)&c, sizeof(c), totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
/* nothing to read */
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
|
|
if ((c & 0x80) || (c == 'D')) {
|
|
/* Here we have data message */
|
|
unsigned int pushack;
|
|
struct stksess *ts;
|
|
struct stksess *newts;
|
|
struct stktable_key stkey;
|
|
int srvid;
|
|
uint32_t netinteger;
|
|
|
|
/* Compute update remote version */
|
|
if (c & 0x80) {
|
|
pushack = ps->pushack + (unsigned int)(c & 0x7F);
|
|
}
|
|
else {
|
|
reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
pushack = ntohl(netinteger);
|
|
}
|
|
|
|
/* read key */
|
|
if (ps->table->table->type == STKTABLE_TYPE_STRING) {
|
|
/* type string */
|
|
stkey.key = stkey.data.buf;
|
|
|
|
reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
stkey.key_len = ntohl(netinteger);
|
|
|
|
reql = bo_getblk(si->ob, stkey.key, stkey.key_len, totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
}
|
|
else if (ps->table->table->type == STKTABLE_TYPE_INTEGER) {
|
|
/* type integer */
|
|
stkey.key_len = (size_t)-1;
|
|
stkey.key = &stkey.data.integer;
|
|
|
|
reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
stkey.data.integer = ntohl(netinteger);
|
|
}
|
|
else {
|
|
/* type ip */
|
|
stkey.key_len = (size_t)-1;
|
|
stkey.key = stkey.data.buf;
|
|
|
|
reql = bo_getblk(si->ob, (char *)&stkey.data.buf, ps->table->table->key_size, totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
|
|
}
|
|
|
|
/* read server id */
|
|
reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
srvid = ntohl(netinteger);
|
|
|
|
/* update entry */
|
|
newts = stksess_new(ps->table->table, &stkey);
|
|
if (newts) {
|
|
/* lookup for existing entry */
|
|
ts = stktable_lookup(ps->table->table, newts);
|
|
if (ts) {
|
|
/* the entry already exist, we can free ours */
|
|
stktable_touch(ps->table->table, ts, 0);
|
|
stksess_free(ps->table->table, newts);
|
|
}
|
|
else {
|
|
struct eb32_node *eb;
|
|
|
|
/* create new entry */
|
|
ts = stktable_store(ps->table->table, newts, 0);
|
|
ts->upd.key= (++ps->table->table->update)+(2^31);
|
|
eb = eb32_insert(&ps->table->table->updates, &ts->upd);
|
|
if (eb != &ts->upd) {
|
|
eb32_delete(eb);
|
|
eb32_insert(&ps->table->table->updates, &ts->upd);
|
|
}
|
|
}
|
|
|
|
/* update entry */
|
|
if (srvid && stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID))
|
|
stktable_data_cast(stktable_data_ptr(ps->table->table, ts, STKTABLE_DT_SERVER_ID), server_id) = srvid;
|
|
ps->pushack = pushack;
|
|
}
|
|
|
|
}
|
|
else if (c == 'R') {
|
|
/* Reset message: remote need resync */
|
|
|
|
/* reinit counters for a resync */
|
|
ps->lastpush = 0;
|
|
ps->teaching_origin = ps->pushed = ps->table->table->update;
|
|
|
|
/* reset teaching flags to 0 */
|
|
ps->flags &= PEER_TEACH_RESET;
|
|
|
|
/* flag to start to teach lesson */
|
|
ps->flags |= PEER_F_TEACH_PROCESS;
|
|
}
|
|
else if (c == 'F') {
|
|
/* Finish message, all known updates have been pushed by remote */
|
|
/* and remote is up to date */
|
|
|
|
/* If resync is in progress with remote peer */
|
|
if (ps->flags & PEER_F_LEARN_ASSIGN) {
|
|
|
|
/* unassign current peer for learning */
|
|
ps->flags &= ~PEER_F_LEARN_ASSIGN;
|
|
ps->table->flags &= ~(SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS);
|
|
|
|
/* Consider table is now up2date, resync resync no more needed from local neither remote */
|
|
ps->table->flags |= (SHTABLE_F_RESYNC_LOCAL|SHTABLE_F_RESYNC_REMOTE);
|
|
}
|
|
/* Increase confirm counter to launch a confirm message */
|
|
ps->confirm++;
|
|
}
|
|
else if (c == 'c') {
|
|
/* confirm message, remote peer is now up to date with us */
|
|
|
|
/* If stopping state */
|
|
if (stopping) {
|
|
/* Close session, push resync no more needed */
|
|
ps->flags |= PEER_F_TEACH_COMPLETE;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* reset teaching flags to 0 */
|
|
ps->flags &= PEER_TEACH_RESET;
|
|
}
|
|
else if (c == 'C') {
|
|
/* Continue message, all known updates have been pushed by remote */
|
|
/* but remote is not up to date */
|
|
|
|
/* If resync is in progress with current peer */
|
|
if (ps->flags & PEER_F_LEARN_ASSIGN) {
|
|
|
|
/* unassign current peer */
|
|
ps->flags &= ~PEER_F_LEARN_ASSIGN;
|
|
ps->table->flags &= ~(SHTABLE_F_RESYNC_ASSIGN|SHTABLE_F_RESYNC_PROCESS);
|
|
|
|
/* flag current peer is not up 2 date to try from an other */
|
|
ps->flags |= PEER_F_LEARN_NOTUP2DATE;
|
|
|
|
/* reschedule a resync */
|
|
ps->table->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
|
|
task_wakeup(ps->table->sync_task, TASK_WOKEN_MSG);
|
|
}
|
|
ps->confirm++;
|
|
}
|
|
else if (c == 'A') {
|
|
/* ack message */
|
|
uint32_t netinteger;
|
|
|
|
reql = bo_getblk(si->ob, (char *)&netinteger, sizeof(netinteger), totl);
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
if (reql == 0) {
|
|
goto incomplete;
|
|
}
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
totl += reql;
|
|
|
|
/* Consider remote is up to date with "acked" version */
|
|
ps->update = ntohl(netinteger);
|
|
}
|
|
else {
|
|
/* Unknown message */
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* skip consumed message */
|
|
bo_skip(si->ob, totl);
|
|
|
|
/* loop on that state to peek next message */
|
|
continue;
|
|
incomplete:
|
|
/* Nothing to read, now we start to write */
|
|
|
|
/* Confirm finished or partial messages */
|
|
while (ps->confirm) {
|
|
/* There is a confirm messages to send */
|
|
repl = bi_putchr(si->ib, 'c');
|
|
if (repl <= 0) {
|
|
/* no more write possible */
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
ps->confirm--;
|
|
}
|
|
|
|
/* Need to request a resync */
|
|
if ((ps->flags & PEER_F_LEARN_ASSIGN) &&
|
|
(ps->table->flags & SHTABLE_F_RESYNC_ASSIGN) &&
|
|
!(ps->table->flags & SHTABLE_F_RESYNC_PROCESS)) {
|
|
/* Current peer was elected to request a resync */
|
|
|
|
repl = bi_putchr(si->ib, 'R');
|
|
if (repl <= 0) {
|
|
/* no more write possible */
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
ps->table->flags |= SHTABLE_F_RESYNC_PROCESS;
|
|
}
|
|
|
|
/* It remains some updates to ack */
|
|
if (ps->pushack != ps->lastack) {
|
|
uint32_t netinteger;
|
|
|
|
trash[0] = 'A';
|
|
netinteger = htonl(ps->pushack);
|
|
memcpy(&trash[1], &netinteger, sizeof(netinteger));
|
|
|
|
repl = bi_putblk(si->ib, trash, 1+sizeof(netinteger));
|
|
if (repl <= 0) {
|
|
/* no more write possible */
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
ps->lastack = ps->pushack;
|
|
}
|
|
|
|
if (ps->flags & PEER_F_TEACH_PROCESS) {
|
|
/* current peer was requested for a lesson */
|
|
|
|
if (!(ps->flags & PEER_F_TEACH_STAGE1)) {
|
|
/* lesson stage 1 not complete */
|
|
struct eb32_node *eb;
|
|
|
|
eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1);
|
|
while (1) {
|
|
int msglen;
|
|
struct stksess *ts;
|
|
|
|
if (!eb) {
|
|
/* flag lesson stage1 complete */
|
|
ps->flags |= PEER_F_TEACH_STAGE1;
|
|
eb = eb32_first(&ps->table->table->updates);
|
|
if (eb)
|
|
ps->pushed = eb->key - 1;
|
|
break;
|
|
}
|
|
|
|
ts = eb32_entry(eb, struct stksess, upd);
|
|
msglen = peer_prepare_datamsg(ts, ps, trash, trashlen);
|
|
if (msglen) {
|
|
/* message to buffer */
|
|
repl = bi_putblk(si->ib, trash, msglen);
|
|
if (repl <= 0) {
|
|
/* no more write possible */
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
ps->lastpush = ps->pushed = ts->upd.key;
|
|
}
|
|
eb = eb32_next(eb);
|
|
}
|
|
} /* !TEACH_STAGE1 */
|
|
|
|
if (!(ps->flags & PEER_F_TEACH_STAGE2)) {
|
|
/* lesson stage 2 not complete */
|
|
struct eb32_node *eb;
|
|
|
|
eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1);
|
|
while (1) {
|
|
int msglen;
|
|
struct stksess *ts;
|
|
|
|
if (!eb || eb->key > ps->teaching_origin) {
|
|
/* flag lesson stage1 complete */
|
|
ps->flags |= PEER_F_TEACH_STAGE2;
|
|
ps->pushed = ps->teaching_origin;
|
|
break;
|
|
}
|
|
|
|
ts = eb32_entry(eb, struct stksess, upd);
|
|
msglen = peer_prepare_datamsg(ts, ps, trash, trashlen);
|
|
if (msglen) {
|
|
/* message to buffer */
|
|
repl = bi_putblk(si->ib, trash, msglen);
|
|
if (repl <= 0) {
|
|
/* no more write possible */
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
ps->lastpush = ps->pushed = ts->upd.key;
|
|
}
|
|
eb = eb32_next(eb);
|
|
}
|
|
} /* !TEACH_STAGE2 */
|
|
|
|
if (!(ps->flags & PEER_F_TEACH_FINISHED)) {
|
|
/* process final lesson message */
|
|
repl = bi_putchr(si->ib, ((ps->table->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FINISHED) ? 'F' : 'C');
|
|
if (repl <= 0) {
|
|
/* no more write possible */
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
|
|
/* flag finished message sent */
|
|
ps->flags |= PEER_F_TEACH_FINISHED;
|
|
} /* !TEACH_FINISHED */
|
|
} /* TEACH_PROCESS */
|
|
|
|
if (!(ps->flags & PEER_F_LEARN_ASSIGN) &&
|
|
(int)(ps->pushed - ps->table->table->localupdate) < 0) {
|
|
/* Push local updates, only if no learning in progress (to avoid ping-pong effects) */
|
|
struct eb32_node *eb;
|
|
|
|
eb = eb32_lookup_ge(&ps->table->table->updates, ps->pushed+1);
|
|
while (1) {
|
|
int msglen;
|
|
struct stksess *ts;
|
|
|
|
/* push local updates */
|
|
if (!eb) {
|
|
eb = eb32_first(&ps->table->table->updates);
|
|
if (!eb || ((int)(eb->key - ps->pushed) <= 0)) {
|
|
ps->pushed = ps->table->table->localupdate;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((int)(eb->key - ps->table->table->localupdate) > 0) {
|
|
ps->pushed = ps->table->table->localupdate;
|
|
break;
|
|
}
|
|
|
|
ts = eb32_entry(eb, struct stksess, upd);
|
|
msglen = peer_prepare_datamsg(ts, ps, trash, trashlen);
|
|
if (msglen) {
|
|
/* message to buffer */
|
|
repl = bi_putblk(si->ib, trash, msglen);
|
|
if (repl <= 0) {
|
|
/* no more write possible */
|
|
if (repl == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
goto switchstate;
|
|
}
|
|
ps->lastpush = ps->pushed = ts->upd.key;
|
|
}
|
|
eb = eb32_next(eb);
|
|
}
|
|
} /* ! LEARN_ASSIGN */
|
|
/* noting more to do */
|
|
goto out;
|
|
}
|
|
case PEER_SESSION_EXIT:
|
|
repl = snprintf(trash, trashlen, "%d\n", si->applet.st1);
|
|
|
|
if (bi_putblk(si->ib, trash, repl) == -1)
|
|
goto out;
|
|
si->applet.st0 = PEER_SESSION_END;
|
|
/* fall through */
|
|
case PEER_SESSION_END: {
|
|
si_shutw(si);
|
|
si_shutr(si);
|
|
si->ib->flags |= CF_READ_NULL;
|
|
goto quit;
|
|
}
|
|
}
|
|
}
|
|
out:
|
|
si_update(si);
|
|
si->ob->flags |= CF_READ_DONTWAIT;
|
|
/* we don't want to expire timeouts while we're processing requests */
|
|
si->ib->rex = TICK_ETERNITY;
|
|
si->ob->wex = TICK_ETERNITY;
|
|
quit:
|
|
return;
|
|
}
|
|
|
|
static struct si_applet peer_applet = {
|
|
.name = "<PEER>", /* used for logging */
|
|
.fct = peer_io_handler,
|
|
.release = peer_session_release,
|
|
};
|
|
|
|
/*
|
|
* Use this function to force a close of a peer session
|
|
*/
|
|
static void peer_session_forceshutdown(struct session * session)
|
|
{
|
|
struct stream_interface *oldsi;
|
|
|
|
if (session->si[0].conn.target.type == TARG_TYPE_APPLET &&
|
|
session->si[0].conn.target.ptr.a == &peer_applet) {
|
|
oldsi = &session->si[0];
|
|
}
|
|
else {
|
|
oldsi = &session->si[1];
|
|
}
|
|
|
|
/* call release to reinit resync states if needed */
|
|
peer_session_release(oldsi);
|
|
oldsi->applet.st0 = PEER_SESSION_END;
|
|
oldsi->conn.data_ctx = NULL;
|
|
task_wakeup(session->task, TASK_WOKEN_MSG);
|
|
}
|
|
|
|
/*
|
|
* 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 a positive value upon success, 0 if the connection needs to be
|
|
* closed and ignored, or a negative value upon critical failure.
|
|
*/
|
|
int peer_accept(struct session *s)
|
|
{
|
|
/* we have a dedicated I/O handler for the stats */
|
|
stream_int_register_handler(&s->si[1], &peer_applet);
|
|
copy_target(&s->target, &s->si[1].conn.target); // for logging only
|
|
s->si[1].conn.data_ctx = s;
|
|
s->si[1].applet.st0 = PEER_SESSION_ACCEPT;
|
|
|
|
tv_zero(&s->logs.tv_request);
|
|
s->logs.t_queue = 0;
|
|
s->logs.t_connect = 0;
|
|
s->logs.t_data = 0;
|
|
s->logs.t_close = 0;
|
|
s->logs.bytes_in = s->logs.bytes_out = 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->req->flags |= CF_READ_DONTWAIT; /* we plan to read small requests */
|
|
|
|
if (s->listener->timeout) {
|
|
s->req->rto = *s->listener->timeout;
|
|
s->rep->wto = *s->listener->timeout;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Create a new peer session in assigned state (connect will start automatically)
|
|
*/
|
|
static struct session *peer_session_create(struct peer *peer, struct peer_session *ps)
|
|
{
|
|
struct listener *l = ((struct proxy *)peer->peers->peers_fe)->listen;
|
|
struct proxy *p = (struct proxy *)l->frontend; /* attached frontend */
|
|
struct session *s;
|
|
struct http_txn *txn;
|
|
struct task *t;
|
|
|
|
if ((s = pool_alloc2(pool2_session)) == NULL) { /* disable this proxy for a while */
|
|
Alert("out of memory in event_accept().\n");
|
|
goto out_close;
|
|
}
|
|
|
|
LIST_ADDQ(&sessions, &s->list);
|
|
LIST_INIT(&s->back_refs);
|
|
|
|
s->flags = SN_ASSIGNED|SN_ADDR_SET;
|
|
s->term_trace = 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.
|
|
*/
|
|
if ((t = task_new()) == NULL) { /* disable this proxy for a while */
|
|
Alert("out of memory in event_accept().\n");
|
|
goto out_free_session;
|
|
}
|
|
|
|
ps->reconnect = tick_add(now_ms, MS_TO_TICKS(5000));
|
|
ps->statuscode = PEER_SESSION_CONNECTCODE;
|
|
|
|
t->process = l->handler;
|
|
t->context = s;
|
|
t->nice = l->nice;
|
|
|
|
memcpy(&s->si[1].addr.to, &peer->addr, sizeof(s->si[1].addr.to));
|
|
s->task = t;
|
|
s->listener = l;
|
|
|
|
/* Note: initially, the session's backend points to the frontend.
|
|
* This changes later when switching rules are executed or
|
|
* when the default backend is assigned.
|
|
*/
|
|
s->be = s->fe = p;
|
|
|
|
s->req = s->rep = NULL; /* will be allocated later */
|
|
|
|
s->si[0].conn.peeraddr = NULL;
|
|
s->si[0].conn.peerlen = 0;
|
|
s->si[0].conn.t.sock.fd = -1;
|
|
s->si[0].conn.flags = CO_FL_NONE;
|
|
s->si[0].owner = t;
|
|
s->si[0].state = s->si[0].prev_state = SI_ST_EST;
|
|
s->si[0].err_type = SI_ET_NONE;
|
|
s->si[0].err_loc = NULL;
|
|
s->si[0].release = NULL;
|
|
s->si[0].send_proxy_ofs = 0;
|
|
set_target_client(&s->si[0].conn.target, l);
|
|
s->si[0].exp = TICK_ETERNITY;
|
|
s->si[0].flags = SI_FL_NONE;
|
|
if (s->fe->options2 & PR_O2_INDEPSTR)
|
|
s->si[0].flags |= SI_FL_INDEP_STR;
|
|
|
|
stream_int_register_handler(&s->si[0], &peer_applet);
|
|
s->si[0].applet.st0 = PEER_SESSION_CONNECT;
|
|
s->si[0].conn.data_ctx = (void *)ps;
|
|
|
|
s->si[1].conn.peeraddr = NULL;
|
|
s->si[1].conn.peerlen = 0;
|
|
s->si[1].conn.t.sock.fd = -1; /* just to help with debugging */
|
|
s->si[1].conn.flags = CO_FL_NONE;
|
|
s->si[1].owner = t;
|
|
s->si[1].state = s->si[1].prev_state = SI_ST_ASS;
|
|
s->si[1].conn_retries = p->conn_retries;
|
|
s->si[1].err_type = SI_ET_NONE;
|
|
s->si[1].err_loc = NULL;
|
|
s->si[1].release = NULL;
|
|
s->si[1].send_proxy_ofs = 0;
|
|
set_target_proxy(&s->si[1].conn.target, s->be);
|
|
si_prepare_conn(&s->si[1], peer->proto, peer->data);
|
|
s->si[1].exp = TICK_ETERNITY;
|
|
s->si[1].flags = SI_FL_NONE;
|
|
if (s->be->options2 & PR_O2_INDEPSTR)
|
|
s->si[1].flags |= SI_FL_INDEP_STR;
|
|
|
|
session_init_srv_conn(s);
|
|
set_target_proxy(&s->target, s->be);
|
|
s->pend_pos = NULL;
|
|
|
|
/* init store persistence */
|
|
s->store_count = 0;
|
|
s->stkctr1_entry = NULL;
|
|
s->stkctr2_entry = NULL;
|
|
|
|
/* FIXME: the logs are horribly complicated now, because they are
|
|
* defined in <p>, <p>, and later <be> and <be>.
|
|
*/
|
|
|
|
s->logs.logwait = 0;
|
|
s->do_log = NULL;
|
|
|
|
/* default error reporting function, may be changed by analysers */
|
|
s->srv_error = default_srv_error;
|
|
|
|
s->uniq_id = 0;
|
|
s->unique_id = NULL;
|
|
|
|
txn = &s->txn;
|
|
/* Those variables will be checked and freed if non-NULL in
|
|
* session.c:session_free(). It is important that they are
|
|
* properly initialized.
|
|
*/
|
|
txn->sessid = NULL;
|
|
txn->srv_cookie = NULL;
|
|
txn->cli_cookie = NULL;
|
|
txn->uri = NULL;
|
|
txn->req.cap = NULL;
|
|
txn->rsp.cap = NULL;
|
|
txn->hdr_idx.v = NULL;
|
|
txn->hdr_idx.size = txn->hdr_idx.used = 0;
|
|
|
|
if ((s->req = pool_alloc2(pool2_channel)) == NULL)
|
|
goto out_fail_req; /* no memory */
|
|
|
|
s->req->buf.size = global.tune.bufsize;
|
|
channel_init(s->req);
|
|
s->req->prod = &s->si[0];
|
|
s->req->cons = &s->si[1];
|
|
s->si[0].ib = s->si[1].ob = s->req;
|
|
|
|
s->req->flags |= CF_READ_ATTACHED; /* the producer is already connected */
|
|
|
|
/* activate default analysers enabled for this listener */
|
|
s->req->analysers = l->analysers;
|
|
|
|
/* note: this should not happen anymore since there's always at least the switching rules */
|
|
if (!s->req->analysers) {
|
|
channel_auto_connect(s->req);/* don't wait to establish connection */
|
|
channel_auto_close(s->req);/* let the producer forward close requests */
|
|
}
|
|
|
|
s->req->rto = s->fe->timeout.client;
|
|
s->req->wto = s->be->timeout.server;
|
|
|
|
if ((s->rep = pool_alloc2(pool2_channel)) == NULL)
|
|
goto out_fail_rep; /* no memory */
|
|
|
|
s->rep->buf.size = global.tune.bufsize;
|
|
channel_init(s->rep);
|
|
s->rep->prod = &s->si[1];
|
|
s->rep->cons = &s->si[0];
|
|
s->si[0].ob = s->si[1].ib = s->rep;
|
|
|
|
s->rep->rto = s->be->timeout.server;
|
|
s->rep->wto = s->fe->timeout.client;
|
|
|
|
s->req->rex = TICK_ETERNITY;
|
|
s->req->wex = TICK_ETERNITY;
|
|
s->req->analyse_exp = TICK_ETERNITY;
|
|
s->rep->rex = TICK_ETERNITY;
|
|
s->rep->wex = TICK_ETERNITY;
|
|
s->rep->analyse_exp = TICK_ETERNITY;
|
|
t->expire = TICK_ETERNITY;
|
|
|
|
s->rep->flags |= CF_READ_DONTWAIT;
|
|
/* it is important not to call the wakeup function directly but to
|
|
* pass through task_wakeup(), because this one knows how to apply
|
|
* priorities to tasks.
|
|
*/
|
|
task_wakeup(t, TASK_WOKEN_INIT);
|
|
|
|
l->nbconn++; /* warning! right now, it's up to the handler to decrease this */
|
|
p->feconn++;/* beconn will be increased later */
|
|
jobs++;
|
|
if (!(s->listener->options & LI_O_UNLIMITED))
|
|
actconn++;
|
|
totalconn++;
|
|
|
|
return s;
|
|
|
|
/* Error unrolling */
|
|
out_fail_rep:
|
|
pool_free2(pool2_channel, s->req);
|
|
out_fail_req:
|
|
task_free(t);
|
|
out_free_session:
|
|
LIST_DEL(&s->list);
|
|
pool_free2(pool2_session, s);
|
|
out_close:
|
|
return s;
|
|
}
|
|
|
|
/*
|
|
* Task processing function to manage re-connect and peer session
|
|
* tasks wakeup on local update.
|
|
*/
|
|
static struct task *process_peer_sync(struct task * task)
|
|
{
|
|
struct shared_table *st = (struct shared_table *)task->context;
|
|
struct peer_session *ps;
|
|
|
|
task->expire = TICK_ETERNITY;
|
|
|
|
if (!stopping) {
|
|
/* Normal case (not soft stop)*/
|
|
if (((st->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMLOCAL) &&
|
|
(!nb_oldpids || tick_is_expired(st->resync_timeout, now_ms)) &&
|
|
!(st->flags & SHTABLE_F_RESYNC_ASSIGN)) {
|
|
/* Resync from local peer needed
|
|
no peer was assigned for the lesson
|
|
and no old local peer found
|
|
or resync timeout expire */
|
|
|
|
/* flag no more resync from local, to try resync from remotes */
|
|
st->flags |= SHTABLE_F_RESYNC_LOCAL;
|
|
|
|
/* reschedule a resync */
|
|
st->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
|
|
}
|
|
|
|
/* For each session */
|
|
for (ps = st->sessions; ps; ps = ps->next) {
|
|
/* For each remote peers */
|
|
if (!ps->peer->local) {
|
|
if (!ps->session) {
|
|
/* no active session */
|
|
if (ps->statuscode == 0 ||
|
|
ps->statuscode == PEER_SESSION_SUCCESSCODE ||
|
|
((ps->statuscode == PEER_SESSION_CONNECTCODE ||
|
|
ps->statuscode == PEER_SESSION_CONNECTEDCODE) &&
|
|
tick_is_expired(ps->reconnect, now_ms))) {
|
|
/* connection never tried
|
|
* or previous session established with success
|
|
* or previous session failed during connection
|
|
* and reconnection timer is expired */
|
|
|
|
/* retry a connect */
|
|
ps->session = peer_session_create(ps->peer, ps);
|
|
}
|
|
else if (ps->statuscode == PEER_SESSION_CONNECTCODE ||
|
|
ps->statuscode == PEER_SESSION_CONNECTEDCODE) {
|
|
/* If previous session failed during connection
|
|
* but reconnection timer is not expired */
|
|
|
|
/* reschedule task for reconnect */
|
|
task->expire = tick_first(task->expire, ps->reconnect);
|
|
}
|
|
/* else do nothing */
|
|
} /* !ps->session */
|
|
else if (ps->statuscode == PEER_SESSION_SUCCESSCODE) {
|
|
/* current session is active and established */
|
|
if (((st->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE) &&
|
|
!(st->flags & SHTABLE_F_RESYNC_ASSIGN) &&
|
|
!(ps->flags & PEER_F_LEARN_NOTUP2DATE)) {
|
|
/* Resync from a remote is needed
|
|
* and no peer was assigned for lesson
|
|
* and current peer may be up2date */
|
|
|
|
/* assign peer for the lesson */
|
|
ps->flags |= PEER_F_LEARN_ASSIGN;
|
|
st->flags |= SHTABLE_F_RESYNC_ASSIGN;
|
|
|
|
/* awake peer session task to handle a request of resync */
|
|
task_wakeup(ps->session->task, TASK_WOKEN_MSG);
|
|
}
|
|
else if ((int)(ps->pushed - ps->table->table->localupdate) < 0) {
|
|
/* awake peer session task to push local updates */
|
|
task_wakeup(ps->session->task, TASK_WOKEN_MSG);
|
|
}
|
|
/* else do nothing */
|
|
} /* SUCCESSCODE */
|
|
} /* !ps->peer->local */
|
|
} /* for */
|
|
|
|
/* Resync from remotes expired: consider resync is finished */
|
|
if (((st->flags & SHTABLE_RESYNC_STATEMASK) == SHTABLE_RESYNC_FROMREMOTE) &&
|
|
!(st->flags & SHTABLE_F_RESYNC_ASSIGN) &&
|
|
tick_is_expired(st->resync_timeout, now_ms)) {
|
|
/* Resync from remote peer needed
|
|
* no peer was assigned for the lesson
|
|
* and resync timeout expire */
|
|
|
|
/* flag no more resync from remote, consider resync is finished */
|
|
st->flags |= SHTABLE_F_RESYNC_REMOTE;
|
|
}
|
|
|
|
if ((st->flags & SHTABLE_RESYNC_STATEMASK) != SHTABLE_RESYNC_FINISHED) {
|
|
/* Resync not finished*/
|
|
/* reschedule task to resync timeout, to ended resync if needed */
|
|
task->expire = tick_first(task->expire, st->resync_timeout);
|
|
}
|
|
} /* !stopping */
|
|
else {
|
|
/* soft stop case */
|
|
if (task->state & TASK_WOKEN_SIGNAL) {
|
|
/* We've just recieved the signal */
|
|
if (!(st->flags & SHTABLE_F_DONOTSTOP)) {
|
|
/* add DO NOT STOP flag if not present */
|
|
jobs++;
|
|
st->flags |= SHTABLE_F_DONOTSTOP;
|
|
}
|
|
|
|
/* disconnect all connected peers */
|
|
for (ps = st->sessions; ps; ps = ps->next) {
|
|
if (ps->session) {
|
|
peer_session_forceshutdown(ps->session);
|
|
ps->session = NULL;
|
|
}
|
|
}
|
|
}
|
|
ps = st->local_session;
|
|
|
|
if (ps->flags & PEER_F_TEACH_COMPLETE) {
|
|
if (st->flags & SHTABLE_F_DONOTSTOP) {
|
|
/* resync of new process was complete, current process can die now */
|
|
jobs--;
|
|
st->flags &= ~SHTABLE_F_DONOTSTOP;
|
|
}
|
|
}
|
|
else if (!ps->session) {
|
|
/* If session is not active */
|
|
if (ps->statuscode == 0 ||
|
|
ps->statuscode == PEER_SESSION_SUCCESSCODE ||
|
|
ps->statuscode == PEER_SESSION_CONNECTEDCODE ||
|
|
ps->statuscode == PEER_SESSION_TRYAGAIN) {
|
|
/* connection never tried
|
|
* or previous session was successfully established
|
|
* or previous session tcp connect success but init state incomplete
|
|
* or during previous connect, peer replies a try again statuscode */
|
|
|
|
/* connect to the peer */
|
|
ps->session = peer_session_create(ps->peer, ps);
|
|
}
|
|
else {
|
|
/* Other error cases */
|
|
if (st->flags & SHTABLE_F_DONOTSTOP) {
|
|
/* unable to resync new process, current process can die now */
|
|
jobs--;
|
|
st->flags &= ~SHTABLE_F_DONOTSTOP;
|
|
}
|
|
}
|
|
}
|
|
else if (ps->statuscode == PEER_SESSION_SUCCESSCODE &&
|
|
(int)(ps->pushed - ps->table->table->localupdate) < 0) {
|
|
/* current session active and established
|
|
awake session to push remaining local updates */
|
|
task_wakeup(ps->session->task, TASK_WOKEN_MSG);
|
|
}
|
|
} /* stopping */
|
|
/* Wakeup for re-connect */
|
|
return task;
|
|
}
|
|
|
|
/*
|
|
* Function used to register a table for sync on a group of peers
|
|
*
|
|
*/
|
|
void peers_register_table(struct peers *peers, struct stktable *table)
|
|
{
|
|
struct shared_table *st;
|
|
struct peer * curpeer;
|
|
struct peer_session *ps;
|
|
|
|
st = (struct shared_table *)calloc(1,sizeof(struct shared_table));
|
|
st->table = table;
|
|
st->next = peers->tables;
|
|
st->resync_timeout = tick_add(now_ms, MS_TO_TICKS(5000));
|
|
peers->tables = st;
|
|
|
|
for (curpeer = peers->remote; curpeer; curpeer = curpeer->next) {
|
|
ps = (struct peer_session *)calloc(1,sizeof(struct peer_session));
|
|
ps->table = st;
|
|
ps->peer = curpeer;
|
|
if (curpeer->local)
|
|
st->local_session = ps;
|
|
ps->next = st->sessions;
|
|
ps->reconnect = now_ms;
|
|
st->sessions = ps;
|
|
peers->peers_fe->maxconn += 3;
|
|
}
|
|
|
|
peers->peers_fe->listen->maxconn = peers->peers_fe->maxconn;
|
|
st->sync_task = task_new();
|
|
st->sync_task->process = process_peer_sync;
|
|
st->sync_task->expire = TICK_ETERNITY;
|
|
st->sync_task->context = (void *)st;
|
|
table->sync_task =st->sync_task;
|
|
signal_register_task(0, table->sync_task, 0);
|
|
task_wakeup(st->sync_task, TASK_WOKEN_INIT);
|
|
}
|
|
|