"Fossies" - the Fresh Open Source Software Archive

Member "Pound-3.0.2/src/http.c" (28 Nov 2021, 24926 Bytes) of package /linux/www/Pound-3.0.2.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "http.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 3.0d_vs_3.0e.

    1 /*
    2  * Pound - the reverse-proxy load-balancer
    3  * Copyright (C) 2002-2020 Apsis GmbH
    4  *
    5  * This file is part of Pound.
    6  *
    7  * Pound is free software; you can redistribute it and/or modify
    8  * it under the terms of the GNU General Public License as published by
    9  * the Free Software Foundation; either version 3 of the License, or
   10  * (at your option) any later version.
   11  *
   12  * Pound is distributed in the hope that it will be useful,
   13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15  * GNU General Public License for more details.
   16  *
   17  * You should have received a copy of the GNU General Public License
   18  * along with this program.  If not, see <http://www.gnu.org/licenses/> .
   19  *
   20  * Contact information:
   21  * Apsis GmbH
   22  * P.O.Box
   23  * 8707 Uetikon am See
   24  * Switzerland
   25  * EMail: roseg@apsis.ch
   26  */
   27 
   28 #include    "pound.h"
   29 
   30 void *
   31 thr_service(void *arg)
   32 {
   33     SERVICE *svc;
   34     struct SESSION {
   35         char            *addr;
   36         BACKEND         *be;
   37         time_t          last_access;
   38         UT_hash_handle  hh;
   39     } *sessions, *cur, *tmp;
   40     time_t  lastscan, now;
   41     struct nn_pollfd s_poll;
   42     char    *msg, addr[NI_MAXHOST];
   43     int     n, i;
   44 
   45     logmsg(1, "%lX start service %s:%d", pthread_self(), __FILE__, __LINE__);
   46     svc = (SERVICE *)arg;
   47     sessions = NULL;
   48     lastscan = time(NULL);
   49     sem_post(&sem_start);
   50     for(;;) {
   51         if(svc->session == 0) {
   52             logmsg(4, "%lX Null session: %s:%d", pthread_self(), __FILE__, __LINE__);
   53             /* accept a listener request, return an appropriate backend */
   54             if(nn_recv(svc->sock, &msg, NN_MSG, 0) <= 0) {
   55                 logmsg(0, "Session: bad receive %s", nn_strerror(nn_errno()));
   56                 continue;
   57             }
   58             nn_freemsg(msg);
   59             if(svc->backends_len > 1) {
   60                 n = random();
   61                 for(i = 0; i < svc->backends_len; i++) {
   62                     n = (n + i) % svc->backends_len;
   63                     if(!svc->backends[n]->is_dead)
   64                         break;
   65                 }
   66             } else
   67                 n = 0;
   68             if(!svc->backends[n]->is_dead) {
   69                 nn_send(svc->sock, &svc->backends[n], sizeof(svc->backends[n]), 0);
   70                 logmsg(4, "%lX Null session returns %d %s:%d", pthread_self(), n, __FILE__, __LINE__);
   71             } else {
   72                 nn_send(svc->sock, "", 0, 0);
   73                 logmsg(4, "%lX Null session no backend %s:%d", pthread_self(), __FILE__, __LINE__);
   74             }
   75         }
   76         now = time(NULL);
   77         if(now >= (lastscan + svc->session)) {
   78             /* prune expired sessions */
   79             HASH_ITER(hh, sessions, cur, tmp)
   80                 if((cur->last_access + svc->session) < now) {
   81                     logmsg(4, "%lX prune %s %s:%d", pthread_self(), cur->addr, __FILE__, __LINE__);
   82                     HASH_DEL(sessions, cur);
   83                     free(cur->addr);
   84                     free(cur);
   85                 }
   86             lastscan = time(NULL);
   87         }
   88         s_poll.fd = svc->sock;
   89         s_poll.events = NN_POLLIN;
   90         if(nn_poll(&s_poll, 1, (svc->session - (now - lastscan)) * 1000) != 1)
   91             continue;
   92         /* accept a listener request, return an appropriate backend */
   93         if(nn_recv(svc->sock, &msg, NN_MSG, 0) <= 0) {
   94             logmsg(0, "Session: bad receive %s", nn_strerror(nn_errno()));
   95             continue;
   96         }
   97         strcpy(addr, msg);
   98         nn_freemsg(msg);
   99         logmsg(4, "%lX find %s %s:%d", pthread_self(), addr, __FILE__, __LINE__);
  100         HASH_FIND_STR(sessions, msg, cur);
  101         if(cur && !cur->be->is_dead) {
  102             logmsg(4, "%lX found %s session %s:%d", pthread_self(), addr, __FILE__, __LINE__);
  103             nn_send(svc->sock, &cur->be, sizeof(cur->be), 0);
  104             cur->last_access = time(NULL);
  105             continue;
  106         }
  107         if(svc->backends_len > 1) {
  108             n = random();
  109             for(i = 0; i < svc->backends_len; i++) {
  110                 n = (n + i) % svc->backends_len;
  111                 if(!svc->backends[n]->is_dead)
  112                     break;
  113             }
  114         } else
  115             n = 0;
  116         if(!svc->backends[n]->is_dead) {
  117             if((cur = (struct SESSION *)malloc(sizeof(struct SESSION))) == NULL)
  118                 logmsg(0, "Session: out of memory");
  119             else {
  120                 cur->addr = strdup(addr);
  121                 cur->be = svc->backends[i];
  122                 cur->last_access = time(NULL);
  123                 HASH_ADD_KEYPTR(hh, sessions, cur->addr, strlen(cur->addr), cur);
  124                 logmsg(4, "%lX added %s %s:%d", pthread_self(), addr, __FILE__, __LINE__);
  125             }
  126             nn_send(svc->sock, &svc->backends[n], sizeof(svc->backends[n]), 0);
  127         } else {
  128             nn_send(svc->sock, "", 0, 0);
  129             logmsg(4, "%lX no backend %s:%d", pthread_self(), __FILE__, __LINE__);
  130         }
  131     }
  132 }
  133 
  134 int
  135 get_be(HTTP_LISTENER *http, char *peer_name, char *request, char *headers[], struct hpack_headerblock *h2)
  136 {
  137     int i, j, found, s_private;
  138     char    *msg, private_name[NI_MAXHOST], buf[MAXBUF], *method, *path;
  139     BACKEND *be;
  140     struct nn_pollfd    private_poll;
  141     struct hpack_header *header;
  142 
  143     logmsg(1, "%lX start get_be %s:%d", pthread_self(), __FILE__, __LINE__);
  144     if(request != NULL) {
  145         logmsg(2, "%lX get_be HTTP/1.1 %s:%d", pthread_self(), __FILE__, __LINE__);
  146         for(i = 0; i < http->services_len; i++) {
  147             if(http->services[i]->url != NULL && regexec(http->services[i]->url, request, 0, NULL, REG_ICASE))
  148                 continue;
  149             found = (http->services[i]->head_require == NULL);
  150             for(j = 0; j < MAXHEADERS; j++) {
  151                 if(headers[j] == NULL)
  152                     continue;
  153                 if(http->services[i]->head_deny != NULL && !regexec(http->services[i]->head_deny, headers[j], 0, NULL, REG_ICASE)) {
  154                     found = 0;
  155                     break;
  156                 }
  157                 if(http->services[i]->head_require != NULL && !regexec(http->services[i]->head_require, headers[j], 0, NULL, REG_ICASE))
  158                     found = 1;
  159             }
  160             if(found)
  161                 break;
  162         }
  163     } else {
  164         logmsg(2, "%lX get_be HTTP/2 %s:%d", pthread_self(), __FILE__, __LINE__);
  165         for(i = 0; i < http->services_len; i++) {
  166             found = (http->services[i]->head_require == NULL);
  167             method = path = NULL;
  168             TAILQ_FOREACH(header, h2, hdr_entry) {
  169                 if(!strcasecmp(header->hdr_name, ":method"))
  170                     method = header->hdr_value;
  171                 else if(!strcasecmp(header->hdr_name, ":path"))
  172                     path = header->hdr_value;
  173                 else if(strcasecmp(header->hdr_name, ":scheme")) {
  174                     /* we don't care about :scheme */
  175                     if(!strcasecmp(header->hdr_name, ":authority"))
  176                         snprintf(buf, MAXBUF, "Host: %s\r\n", header->hdr_value);
  177                     else
  178                         snprintf(buf, MAXBUF, "%s: %s\r\n", header->hdr_name, header->hdr_value);
  179                     logmsg(4, "%lX check %d header %s %s:%d", pthread_self(), i, buf, __FILE__, __LINE__);
  180                     if(http->services[i]->head_require != NULL && !regexec(http->services[i]->head_require, buf, 0, NULL, REG_ICASE))
  181                         found = 1;
  182                     if(http->services[i]->head_deny != NULL && !regexec(http->services[i]->head_deny, buf, 0, NULL, REG_ICASE)) {
  183                         found = 0;
  184                         break;
  185                     }
  186                 }
  187             }
  188             if(!found)
  189                 continue;
  190             if(http->services[i]->url == NULL)
  191                 break;
  192             snprintf(buf, MAXBUF, "%s %s HTTP/1.1\r\n", method, path);
  193             logmsg(4, "%lX check %d request %s %s:%d", pthread_self(), i, buf, __FILE__, __LINE__);
  194             if(!regexec(http->services[i]->url, buf, 0, NULL, REG_ICASE))
  195                 break;
  196         }
  197     }
  198 
  199     logmsg(2, "%lX %sfound %d %s:%d", pthread_self(), i >= http->services_len? "not ": "", i, __FILE__, __LINE__);
  200     if(i >= http->services_len)
  201         return -1;
  202     nn_send(http->services[i]->sock_in, peer_name, strlen(peer_name) + 1, 0);
  203     if(nn_recv(http->services[i]->sock_in, &msg, NN_MSG, 0) == sizeof(be))
  204         memcpy(&be, msg, sizeof(be));
  205     else
  206         be = NULL;
  207     nn_freemsg(msg);
  208 
  209     if(be == NULL || (s_private = nn_socket(AF_SP, NN_PAIR)) < 0)
  210         return -1;
  211         
  212     do {
  213         snprintf(private_name, NI_MAXHOST, "inproc://HTTP_%ld", random() % 10000);
  214     } while(nn_bind(s_private, private_name) < 0);
  215 
  216     if(nn_send(be->sock_in, private_name, strlen(private_name) + 1, 0) < 0) {
  217         nn_close(s_private);
  218         return -1;
  219     }
  220 
  221     private_poll.fd = s_private;
  222     private_poll.events = NN_POLLOUT;
  223     if(nn_poll(&private_poll, 1, http->client * 1000) != 1) {
  224         nn_close(s_private);
  225         return -1;
  226     }
  227 
  228     logmsg(2, "%lX done get_be %s:%d", pthread_self(), __FILE__, __LINE__);
  229     return s_private;
  230 }
  231 
  232 static int
  233 put_err(FILE *f_client, int code, char *reason, char *body)
  234 {
  235     static char *fmt_body = "HTTP/1.1 %d %s\r\nContent-length: %d\r\n\r\n";
  236     static char *fmt_empty = "HTTP/1.1 %d %s\r\r\n";
  237 
  238     if(body != NULL) {
  239         fprintf(f_client, fmt_body, code, reason, body);
  240         return strlen(fmt_body) + 1 + strlen(reason) + strlen(body);
  241     } else {
  242         fprintf(f_client, fmt_empty, code, reason);
  243         return strlen(fmt_empty) + 1 + strlen(reason);
  244     }
  245 }
  246 
  247 static void
  248 do_request(HTTP_LISTENER *http, FILE *f_client, char *peer_name, char *crt_buf)
  249 {
  250     BACKEND         *be;
  251     char            *msg, *headers[MAXHEADERS], private_name[NI_MAXHOST], request[MAXBUF], buf[MAXBUF];
  252     int             i, is_closed, close_at_end, upgrade_h2, is_chunked, is_expect, s_private, header_found;
  253     long            content_length;
  254     struct timespec t_wait;
  255     regmatch_t      match[2];
  256 
  257     logmsg(1, "%lX start do_request %s:%d", pthread_self(), __FILE__, __LINE__);
  258     for(;;) {
  259         logmsg(2, "%lX start loop %s:%d", pthread_self(), __FILE__, __LINE__);
  260         is_closed = 0;
  261         close_at_end = 0;
  262         content_length = 0L;
  263         is_chunked = 0;
  264         is_expect = 0;
  265         upgrade_h2 = 0;
  266         for(i = 0; i < MAXHEADERS; i++)
  267             headers[i] = NULL;
  268         while(!(is_closed = (fgets(buf, MAXBUF - 1, f_client) == NULL))) {
  269             if(buf[0] != '\r' && buf[0] != '\n')
  270                 break;
  271         }
  272         if(is_closed) {
  273             logmsg(2, "%lX client closed %s:%d", pthread_self(), __FILE__, __LINE__);
  274             return;
  275         }
  276         strcpy(request, buf);
  277         if(!strncasecmp(request, "CONNECT", strlen("CONNECT"))) {
  278             /* CONNECT is the only refused HTTP request type */
  279             time_stamp(buf);
  280             logmsg(0, "%s - - [%s] \"%s\" 405 %d CONNECT not allowed", peer_name, buf, request, put_err(f_client, 405, "CONNECT not allowed", global.err405));
  281             return;
  282         }
  283         logmsg(4, "%lX request %s %s:%d", pthread_self(), request, __FILE__, __LINE__);
  284         if(!strcasecmp(request, global.http2_preamble[0])) {
  285             /*  if the request looks like
  286                     PRI * HTTP/2.0\r\n
  287                 followed by
  288                     \r\n
  289                     SM\r\n
  290                     \r\n
  291                 then this is a direct HTTP/2 connection or a HTTP/2 over TLS connection
  292 
  293                 so call do_http2() and return
  294             */
  295            for(i = 1; global.http2_preamble[i]; i++) {
  296                 if(fgets(buf, MAXBUF - 1, f_client) == NULL) {
  297                     time_stamp(buf);
  298                     logmsg(0, "%s - - [%s] \"%s\" 405 %d HTTP/2 preamble premature EOF", peer_name, buf, request, put_err(f_client, 405, "HTTP/2 preamble premature EOF", global.err405));
  299                     return;
  300                 }
  301                 logmsg(4, "%lX preamble %d -> %s %s:%d", pthread_self(), i, buf, __FILE__, __LINE__);
  302                 if(strcasecmp(buf, global.http2_preamble[i])) {
  303                     fwrite(global.err405, 1, strlen(global.err405), f_client);
  304                     time_stamp(buf);
  305                     logmsg(0, "%s - - [%s] \"%s\" 405 %d HTTP/2 partial preamble", peer_name, buf, request, put_err(f_client, 405, "HTTP/2 partial preamble", global.err405));
  306                     return;
  307                 }
  308            }
  309            do_http2(http, f_client, peer_name, crt_buf, upgrade_h2);
  310            return;
  311         }
  312         for(i = 0; i < MAXHEADERS && !(is_closed = ((fgets(buf, MAXBUF - 1, f_client) == NULL))); )  {
  313             logmsg(4, "%lX header %s %s:%d", pthread_self(), buf, __FILE__, __LINE__);
  314             if(buf[0] == '\r' || buf[0] == '\n')
  315                 break;
  316             if(!strncasecmp(buf, "Upgrade:", strlen("Upgrade:"))) {
  317                 /* Upgrade: h2c ==> next request will be HTTP2 */
  318                 upgrade_h2 = upgrade_h2 || !regexec(&rex_Upgrade_HTTP2, buf, 0, NULL, 0);
  319                 continue;
  320             } else if(!strncasecmp(buf, "Connection:", strlen("Connection:"))) {
  321                 /*  Connection: Upgrade in conjunction with Upgrade: h2c ==> next request will be HTTP2
  322                     Response will be:
  323                         Connection: Upgrade
  324                         Upgrade: h2c
  325                     so call do_http2() on receipt of the preamble
  326                     do_http2() will issue a RST_STREAM/REFUSED_STREAM on stream 1 so the client will re-issue the request in HTTP/2
  327                 */
  328                 upgrade_h2 = upgrade_h2 || !regexec(&rex_Connection_HTTP2, buf, 0, NULL, 0);
  329                 close_at_end = !regexec(&rex_Connection_Closed, buf, 0, NULL, 0);
  330                 logmsg(3, "%lX upgrade_h2 %d close_at_end %d %s:%d", pthread_self(), upgrade_h2, close_at_end, __FILE__, __LINE__);
  331                 continue;
  332             } else if(!regexec(&rex_ContentLength, buf, 2, match, REG_ICASE))
  333                 sscanf(buf + match[1].rm_so, "%ld", &content_length);
  334             else if(!regexec(&rex_Chunked, buf, 0, NULL, REG_ICASE))
  335                 is_chunked = 1;
  336             else
  337                 is_expect = !regexec(&rex_Expect, buf, 0, NULL, REG_ICASE);
  338             if((headers[i++] = strdup(buf)) == NULL) {
  339                 logmsg(0, "Out of memory");
  340                 time_stamp(buf);
  341                 logmsg(0, "%s - - [%s] \"%s\" 500 %d Out of memory", peer_name, buf, request, put_err(f_client, 500, "Out of memory", global.err500));
  342                 is_closed = 1;
  343                 break;
  344             }
  345         }
  346         logmsg(3, "%lX content_length %d is_chunked %d is_expect %d %s:%d", pthread_self(), content_length, is_chunked, is_expect, __FILE__, __LINE__);
  347         if(is_closed) {
  348             for(i = 0; i < MAXHEADERS; i++)
  349                 if(headers[i])
  350                     free(headers[i]);
  351             return;
  352         }
  353         if(content_length > 0L && is_chunked) {
  354             time_stamp(buf);
  355             logmsg(0, "%s - - [%s] \"%s\" 405 %d Chunked and Content-length", peer_name, buf, request, put_err(f_client, 405, "Chunked and Content-length", global.err405));
  356             for(i = 0; i < MAXHEADERS; i++)
  357                 if(headers[i])
  358                     free(headers[i]);
  359             return;
  360         }
  361         if(is_expect)
  362             put_err(f_client, 100, "Continue", NULL);
  363         
  364         if(upgrade_h2) {
  365             logmsg(2, "%lX upgrade HTTP/2 consume request %s:%d", pthread_self(), __FILE__, __LINE__);
  366             if(content_length > 0L)
  367                 while(content_length > 0L) {
  368                     i = fread(buf, sizeof(char), content_length >= MAXBUF? MAXBUF - 1: content_length, f_client);
  369                     if(i <= 0)
  370                         content_length = -1L;
  371                 }
  372             else if(is_chunked)
  373                 while(fgets(buf, MAXBUF - 1, f_client) != NULL) {
  374                     sscanf(buf, "%ld", &content_length);
  375                     if(content_length > 0L) {
  376                         while(content_length > 0L) {
  377                             i = fread(buf, sizeof(char), content_length >= MAXBUF? MAXBUF - 1: content_length, f_client);
  378                             if(i <= 0)
  379                                 content_length = -1L;
  380                         }
  381                         fgets(buf, MAXBUF, f_client);
  382                     } else {
  383                         while(fgets(buf, MAXBUF, f_client) != NULL) {
  384                             if(buf[0] == '\r' || buf[0] == '\n')
  385                                 break;
  386                         }
  387                     }
  388                 }
  389             put_err(f_client, 101, "Switching protocols\r\nConnection: Upgrade\r\nUpgrade: h2c", NULL);
  390             continue;
  391         }
  392 
  393         if((s_private = get_be(http, peer_name, request, headers, NULL)) < 0) {
  394             time_stamp(buf);
  395             logmsg(0, "%s - - [%s] \"%s\" 500 %d No backend", peer_name, buf, request, put_err(f_client, 500, "No backend", global.err500));
  396             return;
  397         }
  398 
  399         logmsg(2, "%lX got backend %s:%d", pthread_self(), __FILE__, __LINE__);
  400         i = 1;
  401         nn_send(s_private, &i, sizeof(int), 0);
  402         nn_send(s_private, peer_name, strlen(peer_name) + 1, 0);
  403         nn_send(s_private, request, strlen(request) + 1, 0);
  404         if(crt_buf[0]) {
  405             logmsg(3, "%lX send cert %s:%d", pthread_self(), __FILE__, __LINE__);
  406             nn_send(s_private, "X-Pound-Cert: ", 15, 0);
  407             nn_send(s_private, crt_buf, strlen(crt_buf) + 1, 0);
  408             nn_send(s_private, "\r\n", 3, 0);
  409         }
  410         logmsg(3, "%lX send headers %s:%d", pthread_self(), __FILE__, __LINE__);
  411         for(i = 0; i < MAXHEADERS; i++)
  412             if(headers[i]) {
  413                 nn_send(s_private, headers[i], strlen(headers[i]) + 1, 0);
  414                 free(headers[i]);
  415             }
  416         nn_send(s_private, "\r\n", 3, 0);
  417 
  418         if(content_length > 0L) {
  419             logmsg(3, "%lX send content_length %s:%d", pthread_self(), __FILE__, __LINE__);
  420             while(content_length > 0L) {
  421                 memset(buf, '\0', MAXBUF);
  422                 i = fread(buf, sizeof(char), content_length >= MAXBUF? MAXBUF - 1: content_length, f_client);
  423                 if(i > 0) {
  424                     nn_send(s_private, buf, i, 0);
  425                     content_length -= i;
  426                 } else
  427                     content_length = -1L;
  428             }
  429         } else if(is_chunked) {
  430             logmsg(3, "%lX send is_chunked %s:%d", pthread_self(), __FILE__, __LINE__);
  431             while(fgets(buf, MAXBUF - 1, f_client) != NULL) {
  432                 sscanf(buf, "%ld", &content_length);
  433                 if(content_length > 0L) {
  434                     while(content_length > 0L) {
  435                         memset(buf, '\0', MAXBUF);
  436                         i = fread(buf, sizeof(char), content_length >= MAXBUF? MAXBUF - 1: content_length, f_client);
  437                         if(i > 0) {
  438                             nn_send(s_private, buf, i, 0);
  439                             content_length -= i;
  440                         } else
  441                             content_length = -1L;
  442                     }
  443                     if(fgets(buf, MAXBUF, f_client) != NULL)
  444                         nn_send(s_private, buf, strlen(buf) + 1, 0);
  445                 } else {
  446                     while(fgets(buf, MAXBUF, f_client) != NULL) {
  447                         if(regexec(&rex_ContentLength, buf, 2, match, REG_ICASE))
  448                             nn_send(s_private, buf, strlen(buf) + 1, 0);
  449                         if(buf[0] == '\r' || buf[0] == '\n')
  450                             break;
  451                     }
  452                 }
  453             }
  454         }
  455         nn_send(s_private, "", 0, 0);
  456 
  457         logmsg(2, "%lX pass response %s:%d", pthread_self(), __FILE__, __LINE__);
  458         while((i = nn_recv(s_private, &msg, NN_MSG, 0)) > 0) {
  459             fwrite(msg, i, 1, f_client);
  460             nn_freemsg(msg);
  461         }
  462 
  463         /* end of comms; has to be done from this side, as no LINGER available */
  464         nn_send(s_private, "", 0, 0);
  465         /* sleep to make sure all messages are through before closing the channel */
  466         t_wait.tv_sec = 0;
  467         t_wait.tv_nsec = 1000000;
  468         nanosleep(&t_wait, NULL);
  469 
  470         nn_close(s_private);
  471         logmsg(2, "%lX loop done %s:%d", pthread_self(), __FILE__, __LINE__);
  472         if(close_at_end)
  473             return ;
  474     }
  475 }
  476 
  477 typedef struct cookie {
  478     mbedtls_ssl_context *fd;
  479 }   COOKIE;
  480 
  481 static size_t
  482 c_read(void *cv, char *buf, size_t size)
  483 {
  484     COOKIE  *c;
  485     int     n;
  486     size_t  n_read;
  487 
  488     c = (COOKIE *)cv;
  489     n_read = 0;
  490     while(n_read < size && (n = mbedtls_ssl_read(c->fd, buf + n_read, size - n_read)) > 0)
  491         n_read += n;
  492     return n_read;
  493 }
  494 
  495 static size_t
  496 c_write(void *cv, char *buf, size_t size)
  497 {
  498     COOKIE  *c;
  499 
  500     c = (COOKIE *)cv;
  501     return mbedtls_ssl_write(c->fd, buf, size);
  502 }
  503 
  504 static int
  505 c_close(void *cv)
  506 {
  507     COOKIE  *c;
  508     int     res;
  509     mbedtls_net_context *ssl_fd;
  510 
  511     c = (COOKIE *)cv;
  512     res = mbedtls_ssl_close_notify(c->fd);
  513     ssl_fd = c->fd->p_bio;
  514     mbedtls_ssl_free(c->fd);
  515     mbedtls_net_free(ssl_fd);
  516     return res;
  517 }
  518 
  519 void *
  520 thr_http(void *arg)
  521 {
  522     HTTP_LISTENER   *http;
  523     BACKEND         *be;
  524     char            *msg, peer_name[NI_MAXHOST], crt_buf[MAXBUF];
  525     struct sockaddr peer_addr;
  526     int             sock_client, n;
  527     FILE            *f_client;
  528     struct linger   s_linger;
  529     struct timeval  s_time;
  530     mbedtls_ssl_context ssl;
  531     mbedtls_net_context ssl_client;
  532     COOKIE          c;
  533     cookie_io_functions_t   cio;
  534 
  535     logmsg(1, "%lX thr_http start %s:%d", pthread_self(), __FILE__, __LINE__);
  536     http = (HTTP_LISTENER *)arg;
  537     sem_post(&sem_start);
  538     for(;;) {
  539         logmsg(2, "%lX start loop %s:%d", pthread_self(), __FILE__, __LINE__);
  540         if(nn_recv(http->sock_fan, &msg, NN_MSG, 0) <= 0) {
  541             logmsg(0, "HTTP: bad receive %s", nn_strerror(nn_errno()));
  542             continue;
  543         }
  544         memcpy(&sock_client, msg, sizeof(sock_client));
  545         nn_freemsg(msg);
  546         n = sizeof(peer_addr);
  547         getpeername(sock_client, &peer_addr, &n);
  548         getnameinfo(&peer_addr, sizeof(peer_addr), peer_name, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
  549         logmsg(4, "%lX peer address %s %s:%d", pthread_self(), peer_name, __FILE__, __LINE__);
  550         if(http->conf == NULL) {
  551             s_linger.l_onoff = 1;
  552             s_linger.l_linger = 0;
  553             setsockopt(sock_client, SOL_SOCKET, SO_LINGER, &s_linger, sizeof(s_linger));
  554             s_time.tv_sec = http->client;
  555             s_time.tv_usec = 0;
  556             setsockopt(sock_client, SOL_SOCKET, SO_RCVTIMEO, &s_time, sizeof(s_time));
  557             setsockopt(sock_client, SOL_SOCKET, SO_SNDTIMEO, &s_time, sizeof(s_time));
  558             f_client = fdopen(sock_client, "r+");
  559         } else {
  560             mbedtls_net_init(&ssl_client);
  561             ssl_client.fd = sock_client;
  562             mbedtls_ssl_init(&ssl);
  563             mbedtls_ssl_setup(&ssl, http->conf);
  564             mbedtls_ssl_set_bio(&ssl, &sock_client, mbedtls_net_send, NULL, mbedtls_net_recv_timeout);
  565             if(n = mbedtls_ssl_handshake(&ssl)) {
  566                 mbedtls_strerror(n, crt_buf, MAXBUF);
  567                 logmsg(0, "Failed handshake from %s: %d - %s", peer_name, n, crt_buf);
  568                 mbedtls_ssl_free(&ssl);
  569                 close(sock_client);
  570                 continue;
  571             }
  572             logmsg(2, "%lX handshake OK %s:%d", pthread_self(), __FILE__, __LINE__);
  573             if(mbedtls_ssl_get_peer_cert(&ssl) != NULL) {
  574                 mbedtls_x509_crt_info(crt_buf, MAXBUF, "\t", mbedtls_ssl_get_peer_cert(&ssl));
  575                 logmsg(4, "%lX peer certificate %s %s:%d", pthread_self(), crt_buf, __FILE__, __LINE__);
  576                 for(n = 0; n < MAXBUF && crt_buf[n]; n++)
  577                     ;
  578                 crt_buf[--n] = '\0';
  579             } else
  580                 crt_buf[0] = '\0';
  581             /* for HTTP2: !strcmp(mbedtls_ssl_get_alpn_protocol(&ssl), "h2"), but we don't really need it */
  582             c.fd = &ssl;
  583             cio.read = (cookie_read_function_t *)c_read;
  584             cio.write = (cookie_write_function_t *)c_write;
  585             cio.seek = NULL;
  586             cio.close = (cookie_close_function_t *)c_close;
  587 
  588             if((f_client = fopencookie(&c, "w+", cio)) == NULL) {
  589                 logmsg(0, "fopencookie failed");
  590                 mbedtls_ssl_free(&ssl);
  591                 mbedtls_net_free(&ssl_client);
  592                 close(sock_client);
  593                 continue;
  594             }
  595             setvbuf(f_client, NULL, _IONBF, 0);
  596         }
  597         do_request(http, f_client, peer_name, crt_buf);
  598         fclose(f_client);
  599         logmsg(2, "%lX done loop %s:%d", pthread_self(), __FILE__, __LINE__);
  600     }
  601 }