* start migration to buffered I/O
authorWilfried Göesgens <willi@citadel.org>
Mon, 8 Feb 2010 22:11:29 +0000 (22:11 +0000)
committerWilfried Göesgens <willi@citadel.org>
Mon, 8 Feb 2010 22:11:29 +0000 (22:11 +0000)
citadel/context.c
citadel/context.h
citadel/modules/crypto/serv_crypto.c
citadel/modules/crypto/serv_crypto.h
citadel/sysdep.c

index 70fdd2795051dc4c6caffd453eba4c3e814c7ddf..c20ec98f7654f873a7387799e617fb3a36fd0fc4 100644 (file)
@@ -327,6 +327,8 @@ void RemoveContext (CitContext *con)
                con->ldap_dn = NULL;
        }
 
+       FreeStrBuf(&con->MigrateBuf);
+       FreeStrBuf(&con->ReadBuf);
        CtdlLogPrintf(CTDL_DEBUG, "Done with RemoveContext()\n");
 }
 
@@ -362,6 +364,8 @@ CitContext *CreateNewContext(void) {
         * Generate a unique session number and insert this context into
         * the list.
         */
+       me->MigrateBuf = NewStrBuf();
+       me->ReadBuf = NewStrBuf();
        begin_critical_section(S_SESSION_TABLE);
        me->cs_pid = ++next_pid;
        me->prev = NULL;
index ff62295e708cfaf411e5f2c9033ebfdc94eb6004..f9c3aeada7a6241271a5ff513fe9ae6b04ace877 100644 (file)
@@ -21,19 +21,31 @@ struct CitContext {
        struct CitContext *prev;        /* Link to previous session in list */
        struct CitContext *next;        /* Link to next session in the list */
 
-       int state;              /* thread state (see CON_ values below) */
-       int kill_me;            /* Set to nonzero to flag for termination */
-       int client_socket;
        int cs_pid;             /* session ID */
        int dont_term;          /* for special activities like artv so we don't get killed */
        time_t lastcmd;         /* time of last command executed */
        time_t lastidle;        /* For computing idle time */
+       int state;              /* thread state (see CON_ values below) */
+       int kill_me;            /* Set to nonzero to flag for termination */
+
+       const char *Pos;        /* Our read position inside of the ReadBuf */
+       StrBuf *ReadBuf;        /* Our block buffered read buffer */
+       StrBuf *MigrateBuf;        /* Our block buffered read buffer */
+       int client_socket;
+       int is_local_socket;    /* set to 1 if client is on unix domain sock */
+       /* Redirect this session's output to a memory buffer? */
+       char *redirect_buffer;          /* the buffer */
+       size_t redirect_len;            /* length of data in buffer */
+       size_t redirect_alloc;          /* length of allocated buffer */
+#ifdef HAVE_OPENSSL
+       SSL *ssl;
+       int redirect_ssl;
+#endif
 
        char curr_user[USERNAME_SIZE];  /* name of current user */
        int logged_in;          /* logged in */
        int internal_pgm;       /* authenticated as internal program */
        int nologin;            /* not allowed to log in */
-       int is_local_socket;    /* set to 1 if client is on unix domain sock */
        int curr_view;          /* The view type for the current user/room */
        int is_master;          /* Is this session logged in using the master user? */
 
@@ -41,9 +53,6 @@ struct CitContext {
        time_t previous_login;  /* Date/time of previous login */
        char lastcmdname[5];    /* name of last command executed */
        unsigned cs_flags;      /* miscellaneous flags */
-       void (*h_command_function) (void) ;     /* service command function */
-       void (*h_async_function) (void) ;       /* do async msgs function */
-       void (*h_greeting_function) (void) ;    /* greeting function for session startup */
        int is_async;           /* Nonzero if client accepts async msgs */
        int async_waiting;      /* Nonzero if there are async msgs waiting */
        int input_waiting;      /* Nonzero if there is client input waiting */
@@ -81,14 +90,6 @@ struct CitContext {
        /* Beginning of cryptography - session nonce */
        char cs_nonce[NONCE_SIZE];      /* The nonce for this session's next auth transaction */
 
-       /* Redirect this session's output to a memory buffer? */
-       char *redirect_buffer;          /* the buffer */
-       size_t redirect_len;            /* length of data in buffer */
-       size_t redirect_alloc;          /* length of allocated buffer */
-#ifdef HAVE_OPENSSL
-       SSL *ssl;
-       int redirect_ssl;
-#endif
 
        /* A linked list of all instant messages sent to us. */
        struct ExpressMessage *FirstExpressMessage;
@@ -111,6 +112,11 @@ struct CitContext {
        const char *ServiceName;                /* readable purpose of this session */
        void *openid_data;                      /* Data stored by the OpenID module */
        char *ldap_dn;                          /* DN of user when using AUTHMODE_LDAP */
+
+
+       void (*h_command_function) (void) ;     /* service command function */
+       void (*h_async_function) (void) ;       /* do async msgs function */
+       void (*h_greeting_function) (void) ;    /* greeting function for session startup */
 };
 
 typedef struct CitContext CitContext;
index 5224aaac86e85ce93e7fbc8b81e908ea19487a98..f323be662c65c0150ec5ef9744095b581f140a1f 100644 (file)
@@ -431,62 +431,130 @@ void client_write_ssl(const char *buf, int nbytes)
 
 
 /*
- * client_read_ssl() - read data from the encrypted layer.
+ * read data from the encrypted layer.
  */
-int client_read_ssl(char *buf, int bytes, int timeout)
+int client_read_sslbuffer(StrBuf *buf, int timeout)
 {
-#if 0
-       fd_set rfds;
-       struct timeval tv;
-       int retval;
-       int s;
-#endif
-       int len, rlen;
+       char sbuf[16384]; /* OpenSSL communicates in 16k blocks, so let's speak its native tongue. */
+       int rlen;
        char junk[1];
+       SSL *pssl = CC->ssl;
 
-       len = 0;
-       while (len < bytes) {
-#if 0
-               /*
-                * This code is disabled because we don't need it when
-                * using blocking reads (which we are). -IO
-                */
-               FD_ZERO(&rfds);
-               s = BIO_get_fd(CC->ssl->rbio, NULL);
-               FD_SET(s, &rfds);
-               tv.tv_sec = timeout;
-               tv.tv_usec = 0;
-
-               retval = select(s + 1, &rfds, NULL, NULL, &tv);
-
-               if (FD_ISSET(s, &rfds) == 0) {
-                       return (0);
-               }
+       if (pssl == NULL) return(-1);
 
-#endif
-               if (SSL_want_read(CC->ssl)) {
-                       if ((SSL_write(CC->ssl, junk, 0)) < 1) {
-                               CtdlLogPrintf(CTDL_DEBUG, "SSL_write in client_read: %s\n", ERR_reason_error_string(ERR_get_error()));
+       while (1) {
+               if (SSL_want_read(pssl)) {
+                       if ((SSL_write(pssl, junk, 0)) < 1) {
+                               CtdlLogPrintf(CTDL_DEBUG, "SSL_write in client_read\n");
                        }
                }
-               rlen = SSL_read(CC->ssl, &buf[len], bytes - len);
+               rlen = SSL_read(pssl, sbuf, sizeof(sbuf));
                if (rlen < 1) {
                        long errval;
 
-                       errval = SSL_get_error(CC->ssl, rlen);
-                       if (errval == SSL_ERROR_WANT_READ ||
-                           errval == SSL_ERROR_WANT_WRITE) {
+                       errval = SSL_get_error(pssl, rlen);
+                       if (errval == SSL_ERROR_WANT_READ || errval == SSL_ERROR_WANT_WRITE) {
                                sleep(1);
                                continue;
                        }
                        CtdlLogPrintf(CTDL_DEBUG, "SSL_read got error %ld\n", errval);
                        endtls();
-                       return (client_read_to
-                               (&buf[len], bytes - len, timeout));
+                       return (-1);
+               }
+               StrBufAppendBufPlain(buf, sbuf, rlen, 0);
+               return rlen;
+       }
+       return (0);
+}
+
+int client_readline_sslbuffer(StrBuf *Target, StrBuf *Buffer, int timeout)
+{
+       int ntries = 0;
+       const char *pch, *pchs;
+       int rlen, len, retval = 0;
+       CitContext *CCC = CC;
+
+       if (StrLength(Target) > 0) {
+               pchs = ChrPtr(Buffer);
+               pch = strchr(pchs, '\n');
+               if (pch != NULL) {
+                       rlen = 0;
+                       len = pch - pchs;
+                       if (len > 0 && (*(pch - 1) == '\r') )
+                               rlen ++;
+                       StrBufSub(Target, Buffer, 0, len - rlen);
+                       StrBufCutLeft(Buffer, len + 1);
+                       return len - rlen;
+               }
+       }
+       
+       while ((retval == 0) && (CCC->ssl != NULL)) { 
+               pch = NULL;
+               pchs = ChrPtr(Buffer);
+               if (*pchs != '\0')
+                       pch = strchr(pchs, '\n');
+               if (pch == NULL) {
+                       retval = client_read_sslbuffer(Buffer, timeout);
+                       pchs = ChrPtr(Buffer);
+                       pch = strchr(pchs, '\n');
+               }
+               if (retval == 0) {
+                       sleep(1);
+                       ntries ++;
+               }
+               if (ntries > 10)
+                       return 0;
+       }
+       if ((retval > 0) && (pch != NULL)) {
+               rlen = 0;
+               len = pch - pchs;
+               if (len > 0 && (*(pch - 1) == '\r') )
+                       rlen ++;
+               StrBufSub(Target, Buffer, 0, len - rlen);
+               StrBufCutLeft(Buffer, len + 1);
+               return len - rlen;
+               
+       }
+       else 
+               return -1;
+}
+
+
+
+int client_read_sslblob(StrBuf *Target, long bytes, int timeout)
+{
+       long bufremain;
+       long baselen;
+       int retval;
+       CitContext *CCC = CC;
+
+       baselen = StrLength(Target);
+
+       if (CCC->Pos == NULL)
+               CCC->Pos = ChrPtr(CCC->ReadBuf);
+       bufremain = StrLength(CCC->ReadBuf) - 
+               (CCC->Pos - ChrPtr(CCC->ReadBuf));
+
+       if (bytes < bufremain)
+               bufremain = bytes;
+       StrBufAppendBufPlain(Target, CCC->Pos, bufremain, 0);
+       StrBufCutLeft(CCC->ReadBuf, bufremain);
+
+       if (bytes > bufremain) 
+       {
+               while ((StrLength(CCC->ReadBuf) + StrLength(Target) < bytes + baselen) &&
+                      (retval >= 0))
+                       retval = client_read_sslbuffer(CCC->ReadBuf, timeout);
+               if (retval >= 0) {
+                       StrBufAppendBuf(Target, CCC->ReadBuf, 0); /* todo: Buf > bytes? */
+                       return 1;
+               }
+               else {
+                       return -1;
                }
-               len += rlen;
        }
-       return (1);
+       else 
+               return 1;
 }
 
 
index ff20828be0654f24ec05b5506b45601363b022d1..40c5d4807cfcaf3902d50c90cd4d0e17f3b585f3 100644 (file)
@@ -15,7 +15,9 @@
 void destruct_ssl(void);
 void init_ssl(void);
 void client_write_ssl (const char *buf, int nbytes);
-int client_read_ssl (char *buf, int bytes, int timeout);
+int client_read_sslbuffer(StrBuf *buf, int timeout);
+int client_readline_sslbuffer(StrBuf *Target, StrBuf *Buffer, int timeout);
+int client_read_sslblob(StrBuf *Target, long want_len, int timeout);
 void cmd_stls(char *params);
 void cmd_gtls(char *params);
 void endtls(void);
index 2891baa21409f793e39f4dcc97e31843257f2ad7..595d9b5b12c60c9fd896c3f553e1f358ffe0cd84 100644 (file)
@@ -436,7 +436,14 @@ void flush_output(void) {
 #endif
 }
 
+static void flush_client_inbuf(void)
+{
+       CitContext *CCC=CC;
+
+       FlushStrBuf(CCC->ReadBuf);
+       CCC->Pos = NULL;
 
+}
 
 /*
  * client_write()   ...    Send binary data to the client.
@@ -452,6 +459,7 @@ int client_write(const char *buf, int nbytes)
        CitContext *Ctx;
        int fdflags;
 
+       flush_client_inbuf();
        Ctx = CC;
        if (Ctx->redirect_buffer != NULL) {
                if ((Ctx->redirect_len + nbytes + 2) >= Ctx->redirect_alloc) {
@@ -541,76 +549,69 @@ void cprintf(const char *format, ...) {
 
 /*
  * Read data from the client socket.
- * Return values are:
- *     1       Requested number of bytes has been read.
- *     0       Request timed out.
- *     -1      The socket is broken.
- * If the socket breaks, the session will be terminated.
+ *
+ * sock                socket fd to read from
+ * buf         buffer to read into 
+ * bytes       number of bytes to read
+ * timeout     Number of seconds to wait before timing out
+ *
+ * Possible return values:
+ *      1       Requested number of bytes has been read.
+ *      0       Request timed out.
+ *     -1      Connection is broken, or other error.
  */
-INLINE int client_read_backend(char *buf, int bytes, int timeout, CitContext *CCC)
+int client_read_blob(StrBuf *Target, int bytes, int timeout)
 {
-       int len,rlen;
-       fd_set rfds;
-       int fd;
-       struct timeval tv;
-       int retval;
+       CitContext *CCC=CC;
+       const char *Error;
+       int retval = 0;
 
 #ifdef HAVE_OPENSSL
        if (CCC->redirect_ssl) {
-               return (client_read_ssl(buf, bytes, timeout));
+               retval = client_read_sslblob(Target, bytes, timeout);
        }
+       else 
 #endif
-       len = 0;
-       fd = CCC->client_socket;
-       while(len<bytes) {
-               FD_ZERO(&rfds);
-               FD_SET(fd, &rfds);
-               tv.tv_sec = timeout;
-               tv.tv_usec = 0;
-
-               retval = select( (fd)+1, 
-                                &rfds, NULL, NULL, &tv);
-               if (retval < 0)
-               {
-                       if (errno == EINTR)
-                       {
-                               CtdlLogPrintf(CTDL_DEBUG, "Interrupted select() in client_read_to().\n");
-                               if (CtdlThreadCheckStop()) {
-                                       CC->kill_me = 1;
-                                       return (-1);
-                               } else {
-                                       /* can't trust fd's and stuff so we need to re-create them */
-                                       continue;
-                               }
-                       }
-                       else {
-                               CtdlLogPrintf(CTDL_DEBUG, "Failed select() in client_read_to().\n");
-                               CCC->kill_me = 1;
-                               return (-1);
-                       }
-               }
-
-               if (FD_ISSET(fd, &rfds) == 0) {
-                       return(0);
-               }
 
-               rlen = read(fd, &buf[len], bytes-len);
-               if (rlen<1) {
-                       /* The socket has been disconnected! */
-                       CCC->kill_me = 1;
-                       return(-1);
-               }
-               len = len + rlen;
+               retval = StrBufReadBLOBBuffered(Target, 
+                                               CCC->ReadBuf,
+                                               &CCC->Pos,
+                                               &CCC->client_socket,
+                                               1, 
+                                               bytes,
+                                               O_TERM,
+                                               &Error);
+       if (retval < 0) {
+               CtdlLogPrintf(CTDL_CRIT, 
+                             "%s failed: %s\n",
+                             __FUNCTION__,
+                             Error);
        }
-       return(1);
+       return retval;
 }
 
-
 int client_read_to(char *buf, int bytes, int timeout)
 {
-       return client_read_backend(buf, bytes, timeout, CC);
+       CitContext *CCC=CC;
+       int rc;
+
+       rc = client_read_blob(CCC->MigrateBuf, bytes, timeout);
+       if (rc < 0)
+       {
+               *buf = '\0';
+               return rc;
+       }
+       else
+       {
+               memcpy(buf, 
+                      ChrPtr(CCC->MigrateBuf),
+                      StrLength(CCC->MigrateBuf) + 1);
+               FlushStrBuf(CCC->MigrateBuf);
+               return rc;
+       }
 }
 
+
 /*
  * Read data from the client socket with default timeout.
  * (This is implemented in terms of client_read_to() and could be
@@ -621,6 +622,37 @@ INLINE int client_read(char *buf, int bytes)
        return(client_read_to(buf, bytes, config.c_sleeping));
 }
 
+int CtdlClientGetLine(StrBuf *Target)
+{
+       CitContext *CCC=CC;
+       const char *Error;
+       int rc;
+
+#ifdef HAVE_OPENSSL
+       if (CCC->redirect_ssl) {
+               return client_readline_sslbuffer(Target,
+                                                CCC->ReadBuf,
+                                                1);
+       }
+       else 
+#endif
+       {
+               rc = StrBufTCP_read_buffered_line_fast(Target, 
+                                                      CCC->ReadBuf,
+                                                      &CCC->Pos,
+                                                      &CCC->client_socket,
+                                                      5,
+                                                      1,
+                                                      &Error);
+               if ((rc < 0) && (Error != NULL))
+                       CtdlLogPrintf(CTDL_CRIT, 
+                                     "%s failed: %s\n",
+                                     __FUNCTION__,
+                                     Error);
+               return rc;
+       }
+}
+
 
 /*
  * client_getln()   ...   Get a LF-terminated line of text from the client.
@@ -631,32 +663,29 @@ int client_getln(char *buf, int bufsize)
 {
        int i, retval;
        CitContext *CCC=CC;
+       const char *pCh;
 
-       /* Read one character at a time.
-        */
-       for (i = 0;;i++) {
-               retval = client_read_backend(&buf[i], 1, config.c_sleeping, CCC);
-               if (retval != 1 || buf[i] == '\n' || i == (bufsize-1))
-                       break;
-       }
-
-       /* If we got a long line, discard characters until the newline.
-        */
-       if (i == (bufsize-1))
-               while (buf[i] != '\n' && retval == 1)
-                       retval = client_read(&buf[i], 1);
+       retval = CtdlClientGetLine(CCC->MigrateBuf);
 
+       i = StrLength(CCC->MigrateBuf);
+       pCh = ChrPtr(CCC->MigrateBuf);
        /* Strip the trailing LF, and the trailing CR if present.
         */
-       buf[i] = 0;
+       if (bufsize <= i)
+               i = bufsize - 1;
        while ( (i > 0)
-               && ( (buf[i - 1]==13)
-                    || ( buf[i - 1]==10)) ) {
+               && ( (pCh[i - 1]==13)
+                    || ( pCh[i - 1]==10)) ) {
                i--;
-               buf[i] = 0;
        }
-       if (retval < 0) safestrncpy(&buf[i], "000", bufsize - i);
-       return(retval);
+       memcpy(buf, pCh, i);
+       buf[i] = 0;
+
+       FlushStrBuf(CCC->MigrateBuf);
+       if (retval < 0) {
+               safestrncpy(&buf[i], "000", bufsize - i);
+       }
+       return(retval >= 0);
 }