/*
 * Pound - the reverse-proxy load-balancer
 * Copyright (C) 2002-2020 Apsis GmbH
 *
 * This file is part of Pound.
 *
 * Pound 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 3 of the License, or
 * (at your option) any later version.
 *
 * Pound 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
 *
 * Contact information:
 * Apsis GmbH
 * P.O.Box
 * 8707 Uetikon am See
 * Switzerland
 * EMail: roseg@apsis.ch
 */

#include    "pound.h"

#define EOF_LISTENER(S) {\
    char    *m;\
    int     f;\
\
    nn_send((S), "", 0, 0);\
    f = 100;\
    nn_setsockopt(S, NN_SOL_SOCKET, NN_RCVTIMEO, &f, sizeof(f));\
    if(nn_recv((S), &m, NN_MSG, 0) >= 0)\
        nn_freemsg(m);\
    nn_close(S);\
}

static int
backend_1(BACKEND *be, int s_listener, FILE *f_be, char *client_addr)
{
    char    buf[MAXBUF + 1], request[MAXBUF + 1], *msg;
    int     no_content, is_chunked, reply_code, n;
    long    content_length, total;
    regmatch_t  match[2];

    logmsg(1, "%lX start backend_1 %s:%d", pthread_self(), __FILE__, __LINE__);
    no_content = is_chunked = 0;
    content_length = total = 0L;
    /* receive the request from the listener and keep it for logging */
    if(nn_recv(s_listener, &msg, NN_MSG, 0) <= 0) {
        logmsg(2, "%lX can't get request %s:%d", pthread_self(), __FILE__, __LINE__);
        EOF_LISTENER(s_listener);
        return -1;
    }
    strcpy(request, msg);
    nn_freemsg(msg);
    no_content = !strncasecmp("HEAD", request, 4);
    fputs(request, f_be);
    for(n = 0; n < MAXBUF && request[n]; n++)
        if(request[n] == '\r' || request[n] == '\n') {
            request[n] = '\0';
            break;
        }
    logmsg(4, "%lX request:%s %s:%d", pthread_self(), request, __FILE__, __LINE__);

    /* receive the other headers from the listener and pass them to the back-end */
    while(nn_recv(s_listener, &msg, NN_MSG, 0) > 0) {
        logmsg(4, "%lX header:%s %s:%d", pthread_self(), msg, __FILE__, __LINE__);
        fputs(msg, f_be);
        nn_freemsg(msg);
    }
    nn_freemsg(msg);
    logmsg(2, "%lX end request %s:%d", pthread_self(), __FILE__, __LINE__);

    fflush(f_be);

    total = 0L;
    if(fgets(buf, MAXBUF - 1, f_be) == NULL) {
        logmsg(2, "%lX no reply from backend %s:%d", pthread_self(), __FILE__, __LINE__);
        close(fileno(f_be));
        fclose(f_be);
        EOF_LISTENER(s_listener);
        return -1;
    }
    nn_send(s_listener, buf, strlen(buf), 0);
    logmsg(4, "%lX reply %s %s:%d", pthread_self(), buf, __FILE__, __LINE__);

    for(;;) {
        if(regexec(&rex_Response, buf, 2, match, REG_ICASE))
            continue;
        sscanf(buf + match[1].rm_so, "%d", &reply_code);
        if(reply_code >= 200)
            break;
        /* a 1xx reply - pass and skip */
        while(fgets(buf, MAXBUF - 1, f_be) != NULL) {
            logmsg(4, "%lX 100 pass through %s:%d", pthread_self(), buf, __FILE__, __LINE__);
            nn_send(s_listener, buf, strlen(buf), 0);
            if(buf[0] == '\r' || buf[0] == '\n')
                break;
        }
        if(fgets(buf, MAXBUF, f_be) == NULL) {
            close(fileno(f_be));
            fclose(f_be);
            EOF_LISTENER(s_listener);
            time_stamp(buf);
            logmsg(0, "%s - - [%s] \"%s\" premature EOF", client_addr, buf, request);
            return -1;
        }
        logmsg(4, "%lX %s %s:%d", pthread_self(), buf, __FILE__, __LINE__);
        nn_send(s_listener, buf, strlen(buf), 0);
    }

    total += strlen(buf);
    /*
        * receive the reply headers
        * 
        * WARNING: we do not check here for malicious combinations, as we assume our servers to be OK!
        */
    if(be->add_header) {
        nn_send(s_listener, be->add_header, strlen(be->add_header), 0);
        logmsg(4, "%lX add header %s %s:%d", pthread_self(), be->add_header, __FILE__, __LINE__);
        nn_send(s_listener, "\r\n", 2, 0);
        total += strlen(be->add_header) + 2;
    }
    while(fgets(buf, MAXBUF, f_be) != NULL) {
        logmsg(4, "%lX header %s %s:%d", pthread_self, buf, __FILE__, __LINE__);
        nn_send(s_listener, buf, strlen(buf), 0);
        total += strlen(buf);
        if(buf[0] == '\r' || buf[0] == '\n')
            break;
        if(!no_content) {
            if(!regexec(&rex_Chunked, buf, 0, NULL, REG_ICASE))
                is_chunked = 1;
            else if(!regexec(&rex_ContentLength, buf, 2, match, REG_ICASE))
                sscanf(buf + match[1].rm_so, "%ld", &content_length);
        }
    }

    logmsg(3, "%lX no_content %d, is_chunked %d, content_length %d %s:%d", pthread_self, no_content, is_chunked, content_length, __FILE__, __LINE__);

    if(!no_content) {
        if(is_chunked) {
            for(;;) {
                if(fgets(buf, MAXBUF, f_be) == NULL)
                    break;
                nn_send(s_listener, buf, strlen(buf), 0);
                total += strlen(buf);
                sscanf(buf, "%lx", &content_length);
                logmsg(4, "%lX chunk %d %s:%d", pthread_self(), content_length, __FILE__, __LINE__);
                if(content_length == 0L)
                    break;
                while(content_length > 0L) {
                    if((n = fread(buf, sizeof(char), content_length > MAXBUF? MAXBUF: content_length, f_be)) > 0) {
                        logmsg(4, "%lX read %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                        nn_send(s_listener, buf, n, 0);
                        content_length -= n;
                        total += n;
                    } else {
                        content_length = -1L;
                        logmsg(4, "%lX read return %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                    }
                    
                }
                /* CR/LF separator */
                fgets(buf, MAXBUF, f_be);
            }

            /* possibly some more headers */
            while(fgets(buf, MAXBUF, f_be) != NULL) {
                nn_send(s_listener, buf, strlen(buf), 0);
                logmsg(4, "%lX additional header %s %s:%d", pthread_self(), buf, __FILE__, __LINE__);
                total += strlen(buf);
                if(buf[0] == '\r' || buf[0] == '\n')
                    break;
            }
        } else while(content_length > 0L) {
            memset(buf, '\0', MAXBUF + 1);
            if((n = fread(buf, sizeof(char), content_length > MAXBUF? MAXBUF: content_length, f_be)) > 0) {
                logmsg(4, "%lX read %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                nn_send(s_listener, buf, n, 0);
                content_length -= n;
                total += n;
            } else {
                logmsg(4, "%lX read return %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                content_length = -1L;
            }
        }
    }

    EOF_LISTENER(s_listener);
    time_stamp(buf);
    logmsg(0, "%s - - [%s] \"%s\" %d %ld", client_addr, buf, request, reply_code, total);
    return fileno(f_be);
}

static int
backend_2(BACKEND *be, int s_listener, FILE *f_be, char *client_addr)
{
    struct hpack_headerblock    *headers, *r_headers;
    struct hpack_header         *h;
    struct hpack_table          *tab;
    int                         TABSIZE, FRAMESIZE, n, no_content, is_chunked, reply_code;
    long                        content_length, total;
    size_t                      h_len;
    char                        buf[MAXBUF], *method, *path, *authority, *cp, *tp, *ttp, request[MAXBUF];
    unsigned char               *msg, *content;
    regmatch_t                  match[2];

    logmsg(1, "%lX start backend_2 %s:%d", pthread_self(), __FILE__, __LINE__);
    if(nn_recv(s_listener, &msg, NN_MSG, 0) != sizeof(int)) {
        logmsg(2, "%lX can't get FRAMESIZE %s:%d", pthread_self(), __FILE__, __LINE__);
        EOF_LISTENER(s_listener);
        return -1;
    }
    memcpy(&FRAMESIZE, msg, sizeof(int));
    nn_freemsg(msg);
    if(nn_recv(s_listener, &msg, NN_MSG, 0) != sizeof(int)) {
        logmsg(2, "%lX can't get TABSIZE %s:%d", pthread_self(), __FILE__, __LINE__);
        EOF_LISTENER(s_listener);
        return -1;
    }
    memcpy(&TABSIZE, msg, sizeof(int));
    nn_freemsg(msg);
    if(nn_recv(s_listener, &msg, NN_MSG, 0) != sizeof(struct hpack_headerblock *)) {
        logmsg(2, "%lX can't get headers %s:%d", pthread_self(), __FILE__, __LINE__);
        EOF_LISTENER(s_listener);
        return -1;
    }
    memcpy(&headers, msg, sizeof(struct hpack_headerblock *));
    nn_freemsg(msg);
    logmsg(3, "%lX FRAMESIZE %d, TABSIZE %d %s:%d", pthread_self(), FRAMESIZE, TABSIZE, __FILE__, __LINE__);

    method = path = authority = NULL;
    TAILQ_FOREACH(h, headers, hdr_entry) {
        if(!strcmp(h->hdr_name, ":method"))
            method = h->hdr_value;
        else if(!strcmp(h->hdr_name, ":authority"))
            authority = h->hdr_value;
        else if(!strcmp(h->hdr_name, ":path"))
            path = h->hdr_value;
    }
    if(method == NULL || path == NULL || authority == NULL) {
        logmsg(2, "%lX can't find method/path/authority %s:%d", pthread_self(), __FILE__, __LINE__);
        EOF_LISTENER(s_listener);
        return -1;
    }
    is_chunked = 0;
    snprintf(request, MAXBUF, "%s %s HTTP/1.1", method, path);
    fprintf(f_be, "%s\r\n", request);
    logmsg(4, "%lX %s %s HTTP/1.1 %s:%d", pthread_self(), method, path, __FILE__, __LINE__);
    fprintf(f_be, "host: %s\r\n", authority);
    logmsg(4, "%lX host: %s %s:%d", pthread_self(), authority, __FILE__, __LINE__);
    TAILQ_FOREACH(h, headers, hdr_entry) {
        if(!strcasecmp(h->hdr_name, ":method") || !strcasecmp(h->hdr_name, ":authority") || !strcasecmp(h->hdr_name, ":path") || !strcasecmp(h->hdr_name, ":scheme"))
            continue;
        if(!strcmp(h->hdr_name, "te") || !strcasecmp(h->hdr_name, "transfer-encoding")) {
            fprintf(f_be, "%s: %s, chunked\r\n", h->hdr_name, h->hdr_value);
            logmsg(4, "%lX %s: %s, chunked %s:%d", pthread_self(), h->hdr_name, h->hdr_value, __FILE__, __LINE__);
            is_chunked = 1;
        } else {
            fprintf(f_be, "%s: %s\r\n", h->hdr_name, h->hdr_value);
            logmsg(4, "%lX %s: %s %s:%d", pthread_self(), h->hdr_name, h->hdr_value, __FILE__, __LINE__);
        }
    }
    if(!is_chunked) {
        fprintf(f_be, "te: chunked\r\n");
        logmsg(4, "%lX te: chunked %s:%d", pthread_self(), __FILE__, __LINE__);
    }
    fprintf(f_be, "\r\n");
    logmsg(4, "%lX ---end of headers--- %s:%d", pthread_self(), __FILE__, __LINE__);

    while((n = nn_recv(s_listener, &msg, NN_MSG, 0)) > 0) {
        logmsg(4, "%lX chunk %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
        fprintf(f_be, "%X\r\n", n);
        fwrite(msg, 1, n, f_be);
        fwrite("\r\n", 1, 2, f_be);
        nn_freemsg(msg);
    }
    fprintf(f_be, "0\r\n");
    logmsg(4, "%lX ---end of content--- %s:%d", pthread_self(), __FILE__, __LINE__);
    if(nn_recv(s_listener, &msg, NN_MSG, 0) == sizeof(struct hpack_headerblock *)) {
        memcpy(&headers, msg, sizeof(struct hpack_headerblock *));
        TAILQ_FOREACH(h, headers, hdr_entry) {
            if(!strcasecmp(h->hdr_name, ":method") || !strcasecmp(h->hdr_name, ":authority") || !strcasecmp(h->hdr_name, ":path") || !strcasecmp(h->hdr_name, ":scheme"))
                continue;
            fprintf(f_be, "%s: %s\r\n", h->hdr_name, h->hdr_value);
            logmsg(4, "%lX trailing header %s: %s %s:%d", pthread_self(), h->hdr_name, h->hdr_value, __FILE__, __LINE__);
        }
    } else {
        logmsg(2, "%lX no trailing headers %s:%d", pthread_self(), __FILE__, __LINE__);
    }
    nn_freemsg(msg);
    fprintf(f_be, "\r\n");
    logmsg(4, "%lX ---end of trailing headers --- %s:%d", pthread_self(), __FILE__, __LINE__);

    no_content = !strncasecmp("HEAD", method, 4);
    total = 0L;
    r_headers = hpack_headerblock_new();
    tab = hpack_table_new(TABSIZE);

    for(;;) {
        do {
            if(fgets(buf, MAXBUF - 1, f_be) == NULL) {
                logmsg(2, "%lX no reply from backend %s:%d", pthread_self(), __FILE__, __LINE__);
                hpack_headerblock_free(r_headers);
                hpack_table_free(tab);
                close(fileno(f_be));
                fclose(f_be);
                EOF_LISTENER(s_listener);
                return -1;
            } else
                logmsg(4, "%lX read %s %s:%d", pthread_self(), buf, __FILE__, __LINE__);
        } while(regexec(&rex_Response, buf, 2, match, REG_ICASE));
        sscanf(buf + match[1].rm_so, "%d", &reply_code);
        logmsg(4, "%lX reply code %d %s:%d", pthread_self(), reply_code, __FILE__, __LINE__);
        if(reply_code >= 200) {
            sprintf(buf, "%d", reply_code);
            hpack_header_add(r_headers, ":status", buf, HPACK_INDEX);
            logmsg(4, "%lX reply header :status %s %s:%d", pthread_self(), buf, __FILE__, __LINE__);
            total += strlen(":status") + strlen(buf);
            break;
        }
    }

    if(be->add_header) {
        strncpy(buf, be->add_header, MAXBUF);
        cp = strtok_r(buf, ":", &tp);
        if((ttp = strtok_r(NULL, "\r", &tp)) != NULL)
            while(*ttp == ' ')
                ttp++;
        else
            ttp = "";
        hpack_header_add(r_headers, cp, ttp, HPACK_INDEX);
        logmsg(4, "%lX add header %s %s:%d", pthread_self(), be->add_header, __FILE__, __LINE__);
        total += strlen(cp) + strlen(ttp);
    }

    is_chunked = 0;
    content_length = 0L;
    while(fgets(buf, MAXBUF, f_be) != NULL) {
        if(buf[0] == '\r' || buf[0] == '\n')
            break;
        if(!no_content) {
            if(!regexec(&rex_Chunked, buf, 0, NULL, REG_ICASE))
                is_chunked = 1;
            else if(!regexec(&rex_ContentLength, buf, 2, match, REG_ICASE))
                sscanf(buf + match[1].rm_so, "%ld", &content_length);
        }
        cp = strtok_r(buf, ":", &tp);
        if(!strcasecmp(cp, "connection") || !strcasecmp(cp, "keep-alive") || !strcasecmp(cp, "proxy-connection") || !strcasecmp(cp, "transfer-encoding")
        || !strcasecmp(cp, "te") || !strcasecmp(cp, "content-length") || !strcasecmp(cp, "upgrade"))
            continue;
        if((ttp = strtok_r(NULL, "\r", &tp)) != NULL)
            while(*ttp == ' ')
                ttp++;
        else
            ttp = "";
        hpack_header_add(r_headers, cp, ttp, HPACK_INDEX);
        logmsg(4, "%lX header %s => %s: %s %s:%d", pthread_self(), buf, cp, ttp, __FILE__, __LINE__);
        total += strlen(cp) + strlen(ttp);
    }
    if((content = hpack_encode(r_headers, &h_len, tab)) == NULL) {
        logmsg(4, "%lX failed encode %s:%d", pthread_self(), __FILE__, __LINE__);
        hpack_headerblock_free(r_headers);
        hpack_table_free(tab);
        close(fileno(f_be));
        fclose(f_be);
        EOF_LISTENER(s_listener);
        return -1;
    }
    for(n = 0; h_len > 0; ) {
        nn_send(s_listener, content + n, h_len > FRAMESIZE? FRAMESIZE: h_len, 0);
        n += (h_len > FRAMESIZE? FRAMESIZE: h_len);
        h_len -= (h_len > FRAMESIZE? FRAMESIZE: h_len);
    }
    free(content);
    hpack_headerblock_free(r_headers);

    /* end of headers */
    nn_send(s_listener, "", 0, 0);
    logmsg(3, "%lX no_content %d, is_chunked %d, content_length %d %s:%d", pthread_self(), no_content, is_chunked, content_length, __FILE__, __LINE__);

    if(!no_content) {
        if((content = malloc(FRAMESIZE)) == NULL) {
            logmsg(4, "%lX out of memory for content %s:%d", pthread_self(), __FILE__, __LINE__);
            hpack_table_free(tab);
            close(fileno(f_be));
            fclose(f_be);
            EOF_LISTENER(s_listener);
            return -1;
        }

        if(is_chunked) {
            for(;;) {
                if(fgets(buf, MAXBUF, f_be) == NULL)
                    break;
                sscanf(buf, "%lx", &content_length);
                logmsg(4, "%lX chunk %d (%s) %s:%d", pthread_self(), content_length, buf, __FILE__, __LINE__);
                if(content_length == 0L)
                    break;
                while(content_length > 0L) {
                    if((n = fread(content, sizeof(char), content_length > FRAMESIZE? FRAMESIZE: content_length, f_be)) > 0) {
                        logmsg(4, "%lX read %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                        nn_send(s_listener, content, n, 0);
                        content_length -= n;
                        total += n;
                    } else {
                        content_length = -1L;
                        logmsg(4, "%lX read return %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                    }
                    
                }
                /* CR/LF separator */
                fgets(buf, MAXBUF, f_be);
            }
        } else while(content_length > 0L) {
            if((n = fread(content, sizeof(char), content_length > FRAMESIZE? FRAMESIZE: content_length, f_be)) > 0) {
                logmsg(4, "%lX read %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                nn_send(s_listener, content, n, 0);
                content_length -= n;
                total += n;
            } else {
                logmsg(4, "%lX read return %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
                content_length = -1L;
            }
        }

        free(content);
    }
    /* end of data */
    nn_send(s_listener, "", 0, 0);
    logmsg(4, "%lX sent end-of-content %s:%d", pthread_self(), __FILE__, __LINE__);

    r_headers = hpack_headerblock_new();
    while(fgets(buf, MAXBUF, f_be) != NULL) {
        if(buf[0] == '\r' || buf[0] == '\n')
            break;
        logmsg(4, "%lX have trailer %s %s:%d", pthread_self(), buf, __FILE__, __LINE__);
        cp = strtok_r(buf, ":", &tp);
        if(!strcasecmp(cp, "connection") || !strcasecmp(cp, "keep-alive") || !strcasecmp(cp, "proxy-connection") || !strcasecmp(cp, "transfer-encoding")
        || !strcasecmp(cp, "te") || !strcasecmp(cp, "content-length") || !strcasecmp(cp, "upgrade"))
            continue;
        if((ttp = strtok_r(NULL, "\r", &tp)) != NULL)
            while(*ttp == ' ')
                ttp++;
        else
            ttp = "";
        hpack_header_add(r_headers, cp, ttp, HPACK_INDEX);
        logmsg(4, "%lX trailer %s => %s: %s %s:%d", pthread_self(), buf, cp, ttp, __FILE__, __LINE__);
        total += strlen(cp) + strlen(ttp);
    }
    if((content = hpack_encode(r_headers, &h_len, tab)) == NULL) {
        logmsg(4, "%lX failed encode %s:%d", pthread_self(), __FILE__, __LINE__);
        hpack_headerblock_free(r_headers);
        hpack_table_free(tab);
        close(fileno(f_be));
        fclose(f_be);
        EOF_LISTENER(s_listener);
        return -1;
    }
    for(n = 0; h_len > 0; ) {
        nn_send(s_listener, content + n, h_len > FRAMESIZE? FRAMESIZE: h_len, 0);
        n += (h_len > FRAMESIZE? FRAMESIZE: h_len);
        h_len -= (h_len > FRAMESIZE? FRAMESIZE: h_len);
    }
    free(content);
    hpack_headerblock_free(r_headers);
    hpack_table_free(tab);    
    /* end of trailers */
    nn_send(s_listener, "", 0, 0);
    logmsg(4, "%lX sent end-of-trailers %s:%d", pthread_self(), __FILE__, __LINE__);

    EOF_LISTENER(s_listener);
    time_stamp(buf);
    logmsg(0, "%s - - [%s] \"%s\" %d %ld", client_addr, buf, request, reply_code, total);
    return fileno(f_be);
}

void *
thr_backend(void *arg)
{
    BACKEND *be;
    int     s_listener, s_be, res, flags, http_ver;
    char    *msg, client_addr[NI_MAXHOST], *cp, *saveptr;
    struct pollfd    be_poll;
    struct linger   s_linger;
    struct timeval  s_time;
    FILE    *f_be;
    regmatch_t  match[2];

    be = (BACKEND *)arg;
    sem_post(&sem_start);
    s_be = -1;
    for(;;) {
        msg = NULL;

        if(nn_recv(be->sock, &msg, NN_MSG, 0) <= 0) {
            logmsg(0, "Backend: can't receive from queue socket");
            continue;
        }
        if((s_listener = nn_socket(AF_SP, NN_PAIR)) < 0) {
            logmsg(0, "Backend: can't create direct socket");
            continue;
        }
        if(nn_connect(s_listener, msg) < 0) {
            logmsg(0, "Backend: can't connect to direct socket");
            continue;
        }
        nn_freemsg(msg);
        
        /* prepare the back-end socket */
        if(s_be >= 0) {
            be_poll.fd = s_be;
            be_poll.events = POLLIN;
            if(poll(&be_poll, 1, 1) == 1) {
                /* only EOF can be read! */
                close(s_be);
                s_be = -1;
            }
        }
        if(s_be < 0) {
            /* open the socket to the backend */
            if((s_be = socket(be->addr->ai_family, SOCK_STREAM, 0)) < 0) {
                logmsg(0, "Backend: can't create socket");
                s_be = -1;
                EOF_LISTENER(s_listener);
                continue;
            }

            /* wait at most 'timeout' seconds for the connection */
            flags = fcntl(s_be, F_GETFL, &flags);
            fcntl(s_be, F_SETFL, flags | O_NONBLOCK);
            if(connect(s_be, be->addr->ai_addr, be->addr->ai_addrlen)) {
                if(errno != EINPROGRESS) {
                    logmsg(0, "Backend: can't connect socket");
                    close(s_be);
                    s_be = -1;
                    EOF_LISTENER(s_listener);
                    continue;
                }
                be_poll.fd = s_be;
                be_poll.events = POLLOUT;
                if(poll(&be_poll, 1, be->timeout * 1000) != 1) {
                    logmsg(0, "Backend: can't connect");
                    close(s_be);
                    be->is_dead = 1;
                    s_be = -1;
                    EOF_LISTENER(s_listener);
                    continue;
                }
            }
            fcntl(s_be, F_SETFL, flags);
            s_linger.l_onoff = 1;
            s_linger.l_linger = 0;
            setsockopt(s_be, SOL_SOCKET, SO_LINGER, &s_linger, sizeof(s_linger));
            s_time.tv_sec = be->timeout;
            s_time.tv_usec = 0;
            setsockopt(s_be, SOL_SOCKET, SO_RCVTIMEO, &s_time, sizeof(s_time));
            setsockopt(s_be, SOL_SOCKET, SO_SNDTIMEO, &s_time, sizeof(s_time));
            f_be = fdopen(s_be, "r+");
        }

        /* received the protocol information from the listener */
        if(nn_recv(s_listener, &msg, NN_MSG, 0) != sizeof(int)) {
            logmsg(2, "%lX can't get protocol version %s:%d", pthread_self(), __FILE__, __LINE__);
            EOF_LISTENER(s_listener);
            continue;
        }
        memcpy(&http_ver, msg, sizeof(int));
        nn_freemsg(msg);
        logmsg(4, "%lX http_ver %d %s:%d", pthread_self(), http_ver, __FILE__, __LINE__);

        /* receive the client address from the listener and keep it for logging */
        if(nn_recv(s_listener, &msg, NN_MSG, 0) <= 0) {
            logmsg(2, "thr_backend: can't get client address %s:%d", pthread_self(), __FILE__, __LINE__);
            EOF_LISTENER(s_listener);
            continue;
        }
        strcpy(client_addr, msg);
        nn_freemsg(msg);
        logmsg(4, "%lX client_addr:%s %s:%d", pthread_self(), client_addr, __FILE__, __LINE__);

        /* receive the reply from the back-end and pass it to the listener  in the appropriate protocol */
        if(http_ver == 2)
            s_be = backend_2(be, s_listener, f_be, client_addr);
        else
            s_be = backend_1(be, s_listener, f_be, client_addr);
    }
}
