Hi Alex,

> when a session expires, user gets connection error.  I find that quite
> annoying as the user then have to manually find/enter the starting
> page to log in again.  It would be better if the user was "redirected"
> to the starting page again to log in or got a message that his session
> expired with a link to the starting page (of the current application).
>
> I think that the above "user friendly" behaviour could be achieved if
> httpGate redirected to the default port even if it could not connect
> to the port specified via url (instead of giving a connection error).
> A picolisp application on the default port could then handle this case
> and redirect to the right starting page again.  Tricky bit is how to
> determine which application that request belonged to (that information
> is lost via httpGate in general) but the urls could be set up to
> contain that piece of information and make this possible.

The above behaviour can be achieved using httpGate0 (attached) and a
picolisp server bellow running on the default gate of httpGate0:

$ bin/httpGate0 65000 65001

########################################
#!bin/picolisp lib.l

(load "ext.l" "lib/http.l" "lib/xhtml.l")

(off *U) # not necessary?

(de rewriteUrl (U)
   (setq *U U)
   '`(chop "@start") )

(patch http # rewriteUrl + ignore session handling
   '(if (<> *ConId *SesId) @X)
   (append '(prog)
      (list '(setq @U (rewriteUrl @U)))
      (cdr @X) ) )

(redef http @
   (off *HtVars)
   (pass http) )

(redef _htSet @
   (push '*HtVars (cons (pack (next)) (next))) )

(allowed () "@start")

(de start ()
   (if (match '(@H "/" "a" "p" "p" "/" @A "/" @T) *U)
      (redirect *Gate "://logand.com/admin/" @A)
      (html 0 "Session Expired" NIL NIL "Your session expired.") ) )

(server 65001 "@start")
########################################

To make it work, all urls of all applications must contain the
substring /app/<app-name>/.

Now the question is whether it would not be better for httpGate in
picolisp distribution to behave as httpGate0 attached, i.e. fall back
to the default gate port instead giving connection error right away?

Cheers,

Tomas

/* 15oct08abu
 * (c) Software Lab. Alexander Burger
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <syslog.h>

#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

typedef enum {NO,YES} bool;

static int Http1;

static char Head_200[] =
   "HTTP/1.0 200 OK\r\n"
   "Server: PicoLisp\r\n"
   "Content-Type: text/html; charset=utf-8\r\n"
   "\r\n";

static void logger(char *fmt, ...) {
   va_list ap;

   va_start(ap,fmt);
   vsyslog(LOG_ERR, fmt, ap);
   va_end(ap);
}

static void giveup(char *msg) {
   fprintf(stderr, "httpGate: %s\n", msg);
   exit(2);
}

static inline bool pre(char *p, char *s) {
   while (*s)
      if (*p++ != *s++)
         return NO;
   return YES;
}

static char *ses(char *buf, int port, int *len) {
   int np;
   char *p, *q;

   if (Http1 == 0)
      return buf;
   if (pre(buf, "GET /")) {
      np = (int)strtol(buf+5, &q, 10);
      if (q == buf+5 || *q != '/' || np < 1024 || np > 65535)
         return buf;
      p = q++ - 4;
      do
         if (*q < '0' || *q > '9')
            return buf;
      while (*++q != '~');
      if (np == port) {
         p[0] = 'G',  p[1] = 'E',  p[2] = 'T',  p[3] = ' ';
         *len -= p - buf;
         return p;
      }
      return NULL;
   }
   if (pre(buf, "POST /")) {
      np = (int)strtol(buf+6, &q, 10);
      if (q == buf+6 || *q != '/' || np < 1024 || np > 65535)
         return buf;
      p = q++ - 5;
      do
         if (*q < '0' || *q > '9')
            return buf;
      while (*++q != '~');
      if (np == port) {
         p[0] = 'P',  p[1] = 'O',  p[2] = 'S',  p[3] = 'T',  p[4] = ' ';
         *len -= p - buf;
         return p;
      }
      return NULL;
   }
   return buf;
}

static int slow(SSL *ssl, int fd, char *p, int cnt) {
   int n;

   while ((n = ssl? SSL_read(ssl, p, cnt) : read(fd, p, cnt)) < 0)
      if (errno != EINTR)
         return 0;
   return n;
}

static void wrBytes(int fd, char *p, int cnt) {
   int n;

   do
      if ((n = write(fd, p, cnt)) >= 0)
         p += n, cnt -= n;
      else if (errno != EINTR) {
         logger("%d wrBytes error", fd);
         exit(1);
      }
   while (cnt);
}

static void sslWrite(SSL *ssl, void *p, int cnt) {
   if (SSL_write(ssl, p, cnt) <= 0) {
      logger("SSL_write error");
      exit(1);
   }
}

static int gateSocket(void) {
   int sd;

   if ((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      logger("socket error");
      exit(1);
   }
   return sd;
}

static int gatePort(int port) {
   int n, sd;
   struct sockaddr_in addr;

   memset(&addr, 0, sizeof(addr));
   addr.sin_family = AF_INET;
   addr.sin_addr.s_addr = htonl(INADDR_ANY);
   addr.sin_port = htons((unsigned short)port);
   n = 1,  setsockopt(sd = gateSocket(), SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
   if (bind(sd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
      logger("%d bind error", sd);
      exit(1);
   }
   if (listen(sd,5) < 0) {
      logger("%d listen error", sd);
      exit(1);
   }
   return sd;
}

static int gateConnect(int port, int port0) {
   int sd;
   struct sockaddr_in addr;

   memset(&addr, 0, sizeof(addr));
   addr.sin_addr.s_addr = inet_addr("127.0.0.1");
   sd = gateSocket();
   addr.sin_family = AF_INET;
   addr.sin_port = htons((unsigned short)port);
   if(connect(sd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
     addr.sin_port = htons((unsigned short)port0);
     return connect(sd, (struct sockaddr*)&addr, sizeof(addr)) < 0? -1 : sd;
   } else {
     return sd;
   }
}


static pid_t Buddy;

static void doSigAlarm(int n __attribute__((unused))) {
   logger("Timeout");
   kill(Buddy, SIGTERM);
   exit(0);
}

static void doSigUsr1(int n __attribute__((unused))) {
   alarm(420);
}

int main(int ac, char *av[]) {
   int cnt = ac>4? ac-3 : 1, ports[cnt], n, sd, cli, srv;
   struct sockaddr_in addr;
   char *gate;
   SSL_CTX *ctx;
   SSL *ssl;

   if (ac < 3)
      giveup("port dflt [pem [alt ..]]");

   sd = gatePort(atoi(av[1]));  // e.g. 80 or 443
   ports[0] = atoi(av[2]);  // e.g. 8080
   if (ac == 3 || *av[3] == '\0')
      ssl = NULL,  gate = "Gate: http %s\r\n";
   else {
      SSL_library_init();
      SSL_load_error_strings();
      if (!(ctx = SSL_CTX_new(SSLv23_server_method())) ||
            !SSL_CTX_use_certificate_file(ctx, av[3], SSL_FILETYPE_PEM) ||
               !SSL_CTX_use_PrivateKey_file(ctx, av[3], SSL_FILETYPE_PEM) ||
                                          !SSL_CTX_check_private_key(ctx) ) {
         ERR_print_errors_fp(stderr);
         giveup("SSL init");
      }
      ssl = SSL_new(ctx),  gate = "Gate: https %s\r\n";
   }
   for (n = 1; n < cnt; ++n)
      ports[n] = atoi(av[n+3]);

   signal(SIGCHLD,SIG_IGN);  /* Prevent zombies */
   if ((n = fork()) < 0)
      giveup("detach");
   if (n)
      return 0;
   setsid();

   openlog("httpGate", LOG_CONS|LOG_PID, 0);
   for (;;) {
      socklen_t len = sizeof(addr);
      if ((cli = accept(sd, (struct sockaddr*)&addr, &len)) >= 0 && (n = fork()) >= 0) {
         if (!n) {
            int fd, port;
            char *p, *q, buf[4096], buf2[64];

            close(sd);

            alarm(420);
            if (ssl) {
               SSL_set_fd(ssl, cli);
               if (SSL_accept(ssl) < 0)
                  return 1;
               n = SSL_read(ssl, buf, sizeof(buf));
            }
            else
               n = read(cli, buf, sizeof(buf));
            alarm(0);
            if (n < 6)
               return 1;

            /* "GET /url HTTP/1.x"
             * "GET /8080/url HTTP/1.x"
             * "POST /url HTTP/1.x"
             * "POST /8080/url HTTP/1.x"
             */
            if (pre(buf, "GET /"))
               p = buf + 5;
            else if (pre(buf, "POST /"))
               p = buf + 6;
            else
               return 1;

            port = (int)strtol(p, &q, 10);
            if (q == p  ||  *q != ' ' && *q != '/')
               port = ports[0],  q = p;
            else if (port < cnt)
               port = ports[port];
            else if (port < 1024)
               return 1;

            if ((srv = gateConnect(port, ports[0])) < 0) {
               logger("Can't connect to %d", port);
               if (!memchr(q,'~', buf + n - q)) {
                  buf[n] = '\0';
                  logger("Bad request: %s", buf);
                  return 1;
               }
               if ((fd = open("void", O_RDONLY)) < 0)
                  return 1;
               alarm(420);
               if (ssl)
                  sslWrite(ssl, Head_200, strlen(Head_200));
               else
                  wrBytes(cli, Head_200, strlen(Head_200));
               alarm(0);
               while ((n = read(fd, buf, sizeof(buf))) > 0) {
                  alarm(420);
                  if (ssl)
                     sslWrite(ssl, buf, n);
                  else
                     wrBytes(cli, buf, n);
                  alarm(0);
               }
               return 0;
            }

            Http1 = 0;
            wrBytes(srv, buf, p - buf);
            if (*q == '/')
               ++q;
            p = q;
            while (*p++ != '\n')
               if (p >= buf + n) {
                  buf[n] = '\0';
                  logger("Bad header: %s", buf);
                  return 1;
               }
            wrBytes(srv, q, p - q);
            if (pre(p-10, "HTTP/1."))
               Http1 = *(p-3) - '0';
            wrBytes(srv, buf2, sprintf(buf2, gate, inet_ntoa(addr.sin_addr)));
            wrBytes(srv, p, buf + n - p);

            signal(SIGALRM, doSigAlarm);
            signal(SIGUSR1, doSigUsr1);
            if (Buddy = fork()) {
               for (;;) {
                  alarm(420);
                  n = slow(ssl, cli, buf, sizeof(buf));
                  alarm(0);
                  if (!n || !(p = ses(buf, port, &n)))
                     break;
                  wrBytes(srv, p, n);
               }
               shutdown(cli, SHUT_RD);
               shutdown(srv, SHUT_WR);
            }
            else {
               Buddy = getppid();
               while ((n = read(srv, buf, sizeof(buf))) > 0) {
                  kill(Buddy, SIGUSR1);
                  alarm(420);
                  if (ssl)
                     sslWrite(ssl, buf, n);
                  else
                     wrBytes(cli, buf, n);
                  alarm(0);
               }
               shutdown(srv, SHUT_RD);
               shutdown(cli, SHUT_WR);
            }
            return 0;
         }
         close(cli);
      }
   }
}

Reply via email to