Re: wwwlib and SSL

Hi,

There is my solution for www to support SSL: 

Changes are to HTInit.h HTInit.c and new files are: 

        HTSSL.h
        HTSSLMan.h
        HTSSLReader.h
        HTSSLReader.c
        HTSSLWriter.h
        HTSSLWriter.c

HTSSL.c has list of htssl structures (active ssl connections) that
can be used by multiple HTSSLReaders and HTSSLwriters. The active 
ssl connection is found by identifier which is a socket descriptor (there
should be a better way to do this). Pointer to htssl structure is
returned to interested and reference count is updated. As soon as ref_count
is 0 - structure is destroyed and removed from the list. 

If the socket is closed. Then ssl member "disconnected" is set saying that this
htssl structure should not be used by any reader or writer - should be
freed and new structure should be created (or found in list). (I think this part
is not completely implemented in this version. I am currently working on
another version that is very much company specific and will not work just with
wwwlib.)

This is my "try-version". So, please excuse possible errors. The new files could
be modified to add features or change solution completely.   

Regards,
Olga.

====================================================================
Changes to HTInit.c:

/* should be included somewhere else (Where HTReader and HTWriter
   are included...) */
#include "www/HTSSLReader.h"
#include "www/HTSSLWriter.h"


//////////////////////////////////////////// 
Initialize protocol

PUBLIC void HTProtocolInit (void)
{

        ....

    /* for ssl add line */
   HTProtocol_add("https", "secure_tcp", SSL_PORT, NO,     HTLoadHTTP,    
NULL);  
        ....
}

PUBLIC void HTProtocolPreemptiveInit (void)
{
        ....

    HTProtocol_add("https", "secure_tcp", SSL_PORT, YES, HTLoadHTTP, NULL); 
        ....

}


////////////////////////////////////////////////////////
Add Transport

PUBLIC void HTTransportInit (void)
{
        .....

    /* ssl */
    HTTransport_add("secure_tcp", HT_TP_SINGLE, HTSSLReader_new,
HTSSLWriter_new);

        ....
}

////////////////////////////////////////////////////
Initialize Before Filters to work with https:

PUBLIC void HTBeforeInit (void)
{
                ....

/* add these lines */
    HTNet_addBefore(HTCacheFilter,              "https://*",    NULL,
HT_FILTER_MIDDLE);
    HTNet_addBefore(HTCredentialsFilter,        "https://*",    NULL,
HT_FILTER_LATE);
    HTNet_addBefore(HTPEP_beforeFilter,         "https://*",    NULL,
HT_FILTER_LATE);
}


///////////////////////////////////////////////////

Initialize After Filters to work with https:

PUBLIC void HTAfterInit (void)
{
                ....

/* add these lines */
 HTNet_addAfter(HTAuthFilter,   "https://*",    NULL, HT_NO_ACCESS,    
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTAuthFilter,        "https://*",    NULL, HT_REAUTH,       
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTPEP_afterFilter,   "https://*",    NULL, HT_ALL,          
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTRedirectFilter,    "https://*",    NULL, HT_PERM_REDIRECT,
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTRedirectFilter,    "https://*",    NULL, HT_FOUND,        
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTRedirectFilter,    "https://*",    NULL, HT_SEE_OTHER,    
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTRedirectFilter,    "https://*",    NULL, HT_TEMP_REDIRECT,
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTUseProxyFilter,    "https://*",    NULL, HT_USE_PROXY,    
HT_FILTER_MIDDLE);
    HTNet_addAfter(HTCacheUpdateFilter, "https://*",    NULL, HT_NOT_MODIFIED, 
HT_FILTER_MIDDLE);
}

==========================================================
==========================================================


Change to HTInit.h:

#ifndef SSL_PORT
#define SSL_PORT        443
#endif

==========================================================
==========================================================

new file: HTSSL.h

/* This module declares a wrapper around SSLeay for added  SSL support.
   HTSSL will keep context of the application (SSL_CTX),
   provide SSLeay functinality. Cipher negotiation, certificate directory
   path setting could be added here.

   */

#ifndef _HTSSL_H
#define _HTSSL_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <memory.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

/*#include "e_os.h" */
#include "bio.h"
#include "crypto.h"
#include "x509.h"
#include "ssl.h"
#include "err.h"

#include "www/WWWCore.h"

#define PERROR(err,s) if (PROT_TRACE && ((err)==-1)) perror(s)

typedef struct _HTSSL HTSSL;

/* initialize SSL application context */
extern BOOL HTSSL_CTX_init (void);

#endif /* _HTSSL_H */

===============================================================
===============================================================

New File: HTSSLMan.h
=====================

/*
    THE HTSSL CLASS DEFINITION - 
    defines, manages and frees single SSL connection.
                                             
 */
   

#ifndef HTSSLMAN_H
#define HTSSLMAN_H

#ifdef __cplusplus
extern "C" {
#endif

#include "www/HTSSL.h"    

struct _HTSSL {
    SSL * ssl;
    int   sd;        /* socket descriptor */
    BOOL  connected;
    int   ref_count;
};

extern HTSSL * HTSSL_new(int sd);

extern void HTSSL_free(HTSSL *);

extern BOOL HTSSL_connected(HTSSL * ssl);

extern void HTSSL_set_disconnected(HTSSL * htssl);

extern BOOL HTSSL_connect(HTSSL * ssl, int sd);

extern int HTSSL_read(HTSSL * htssl, int sd, char * buff, int len);

extern int HTSSL_write(HTSSL * htssl, int sd, char * buff, int len);

#ifdef __cplusplus
}
#endif

#endif /*HTSSLMAN_H*/

===============================================================
===============================================================

New file: HTSSL.c

/* HTSSL.c
   Implementation of HTSSL.h and HTSSLMan.h declarations.

   */

#include "www/HTSSL.h"
#include "www/HTSSLMan.h"

/********************************************************************/
/*                        GLOBALS                                   */

PRIVATE SSL_CTX * app_ctx = NULL;
/*PRIVATE SSL * active_ssl = NULL;*/

/* list of all HTSSL structures currently used by readers and writers */
PRIVATE HTList *ssl_list = NULL;

/*********************************************************************/
/*
                     APPLICATION CONTEXT
*/

PUBLIC BOOL HTSSL_CTX_init()
{
    if (PROT_TRACE) HTTrace(" HTSSL_CTX_init()~~~~~~~~~~~~\n");
    
    /* Create application's SSL context if it doesn't exist yet */
    if (!app_ctx) {
        
        SSL_METHOD *meth = NULL;
        SSLeay_add_ssl_algorithms();
        SSL_load_error_strings();
        meth = SSLv2_method();
        
        app_ctx = SSL_CTX_new(meth);
        SSL_CTX_set_session_cache_mode(app_ctx, SSL_SESS_CACHE_CLIENT);

        if (app_ctx != NULL) {
            printf("context created!\n\n");
            return YES;
        }
        else 
          return NO;
    }
    return YES;
    
}

/*****************************************************************************/

PRIVATE BOOL HTSSL_verifyServerCertificate(SSL * ssl)
{
    int i = 0;
    char buff[100];
    
    X509_STORE * store = NULL;
    X509_STORE_CTX store_ctx;
    X509 * server_cert = NULL;
    
    store = X509_STORE_new();

    X509_STORE_set_default_paths(store);

    server_cert = SSL_get_peer_certificate(ssl);

    if (!server_cert)
      return NO;
        
    /* print out names of certificate's issuer and subject */
    if (PROT_TRACE) {
        
        memset(buff, 0, 100);
        
        HTTrace("HTSSL: === Certificate with subject = %s\n", 
                X509_NAME_oneline(X509_get_subject_name(server_cert), 
                                  buff, 99));
        memset(buff, 0, 100);
       
        HTTrace("HTSSL: === Certificate with issuer  = %s\n", 
                X509_NAME_oneline(X509_get_issuer_name(server_cert),
                                  buff, 99));
    }

    X509_STORE_CTX_init(&store_ctx, store, server_cert, NULL);

    i = X509_verify_cert(&store_ctx);

    X509_STORE_CTX_cleanup(&store_ctx);
    
    if (i)
      return YES;
    else {
        
      if (PROT_TRACE)
        HTTrace("HTSSL: === ERROR: Certificate verification failed.\n");
      
      return NO;
    }
    
}

/*****************************************************************************/

/* Initialization of HTSSL structure. Try to do SSL_connect. If fails
   still OK - will connect later.
*/

PRIVATE BOOL HTSSL_init(HTSSL * htssl, int sd)
{

    int err = 0;
    if (PROT_TRACE) HTTrace("HTSSL_init~~~~~~~~~~~~~~~~\n");
    htssl->sd = sd;
    htssl->connected = NO;
    htssl->ref_count = 0;

    htssl->ssl = SSL_new(app_ctx);

    if (!htssl->ssl)
      return NO;
    
    /* set socket descriptor */
    SSL_set_fd (htssl->ssl, sd);
    
    /* try to connect */

    /* do SSL using sertificate and key */
    err = SSL_connect (htssl->ssl);

    PERROR(err, "\n\nHTSSL_init(): SSL_connect failed");
      
    if (err != -1) {
        htssl->connected = YES;

        /* print out what kind of cipher is used */
        if (PROT_TRACE) 
          HTTrace("HTSSL_init === Connection using cipher = %s\n", 
                  SSL_get_cipher(htssl->ssl));
        
        if (HTSSL_verifyServerCertificate(htssl->ssl))
          return YES;
        else
          return NO;
    }
    else {
        SSL_free(htssl->ssl);
        htssl->ssl = NULL;
        return NO;
    }
        
    return YES;
}

/*****************************************************************************/

/* This function should be called whenever HTSSL_new creates a new structure
   or returns a pointer to an existing structure to the caller.
   This function is not PUBLIC because I want to centralize reference counting
   in one module in order to minimize possibility of  errors. 
*/


PRIVATE void HTSSL_addRef(HTSSL * htssl)
{
    if (PROT_TRACE) HTTrace("HTSSL_addRef ~~~~~~~~~~~~~~~\n");
    (htssl->ref_count)++;
    if (PROT_TRACE) HTTrace("HTSSL_addRef: ref_count = %d ~~~~~~~~~~\n",
htssl->ref_count );
}


/*****************************************************************************/
/* 
   Using a socket descriptor as a key try to find the HTSSL object in ssl_table.
   If not there then create a new object and add it to ssl_table.
*/ 

PUBLIC HTSSL * HTSSL_new(int sd) 
{
    HTSSL * htssl = NULL;
    int err   = 0;
    HTList * ssls = NULL;

    if (PROT_TRACE) HTTrace("HTSSL_new ~~~~~~~~~~~~~~~~~~\n");

    /* if application context doesn't exist */
    if (!app_ctx) {
        if (!HTSSL_CTX_init())
          return NULL;
    }
    
    /* check ssl_table */
        if (!ssl_list) 
      ssl_list = HTList_new();

    ssls = ssl_list;
    
        while (htssl = HTList_nextObject(ssls)){
            if (htssl->sd == sd) {
            if (PROT_TRACE)
              HTTrace("HTSSL_new: found ssl %p with sd =  %d\n",
                      (void*) htssl, sd);
            /* add reference count before giving pointer to caller */
            HTSSL_addRef(htssl);
            return htssl;
            }
        }
        
        /* ssl not found : create new */
    if ((htssl = (HTSSL *) HT_CALLOC(1, sizeof (HTSSL))) == NULL)
      HT_OUTOFMEM("HTSSL_new");
    
    /* if initialization fails... */
    if (!HTSSL_init(htssl, sd)) {
        HTSSL_addRef(htssl);
        HTSSL_free(htssl);
        return NULL;
    }

    HTSSL_addRef(htssl);
    
        HTList_addObject (ssl_list, htssl);
    
    return htssl;    

}

/*****************************************************************************/

/* destroy HTSSL object if no references to it remain in allpication */

PUBLIC void HTSSL_free(HTSSL * htssl)
{
    if (PROT_TRACE) HTTrace("HTSSL_free ~~~~~~~~~~~~~~\n");
    (htssl->ref_count)--;
    if (PROT_TRACE) HTTrace("HTSSL_free: ref_count = %d ~~~~~~~~~~\n",
htssl->ref_count );

    if (htssl->ref_count == 0) {
        if (PROT_TRACE) HTTrace("HTSSL_free: FINAL RELEASE ~~~~~~~~~\n");

        if (htssl->ssl) {
            SSL_free(htssl->ssl);
            htssl->ssl = NULL;
        }
       
        HTList_removeObject(ssl_list, htssl);          

        /* releases itself */
        HT_FREE(htssl);
    }
}


/*****************************************************************************/

PUBLIC BOOL HTSSL_connected(HTSSL * htssl)
{
    return htssl->connected;
}

/*****************************************************************************/

PUBLIC void HTSSL_set_disconnected(HTSSL * htssl)
{
    if (PROT_TRACE) HTTrace("HTSSL_set_disconnected: ~~~~~~~~\n");
    htssl->connected = NO;

    SSL_free(htssl->ssl);
    htssl->ssl = NULL;
}


/*****************************************************************************/

PUBLIC BOOL HTSSL_connect(HTSSL * htssl, int sd)
{
    int err = 0;
    if (PROT_TRACE) HTTrace("HTSSL_connect ~~~~~~~~~~~\n");    
    if (htssl->connected)
      return YES;

    if (!htssl->ssl) {
 
        htssl->ssl = SSL_new(app_ctx);
        
        if (!htssl->ssl)
          return NO; 
      
        /* set socket descriptor */
        SSL_set_fd (htssl->ssl, sd);

        htssl->sd = sd;
    
        /* try to connect */

        /* do SSL using sertificate and key */
        err = SSL_connect (htssl->ssl);

        PERROR(err, "\n\nHTSSL_init(): SSL_connect failed");
        
        if (err != -1) {
            htssl->connected = YES;
            return YES;
        }
        
        return NO;
    }

    if (PROT_TRACE) HTTrace("HTSSL_connect WARNING: desconnected HTSSL has
non-NULL ssl\n");        
    return NO;
}

/*****************************************************************************/

PUBLIC int HTSSL_read(HTSSL * htssl, int sd, char * buff, int len)
{
    /* HTSSL_connect(htssl, sd); */
    return SSL_read(htssl->ssl, buff, len);
}

/*****************************************************************************/

PUBLIC int HTSSL_write(HTSSL * htssl, int sd, char * buff, int len)
{
    /* HTSSL_connect(htssl, sd); */
    return SSL_write(htssl->ssl, buff, len);
}








=====================================================
=====================================================

THESE FILES ARE ALMOST THE SAME AS HTReader.h HTReader.c
HTWriter.h HTWriter.c 

The main differences are:

- htssl member is added to stream structures 
- in HTSSL***_new htssl is initialized
- in HT***_free - htssl is destroyed and CloseNotiifcation is set
  (htssl should be destroyed in ***_abort too...)
- in ***_read (***_write) before reading htssl is created (if not yet)
  and read is done through it.
- in HTSSLWriter.h .c one function is renamed (to avoid duplicate with
  one in HTWriter.c)

Note that my HTWriter and HTReader were not latest wwwlib versions, so just copy
might not work.


New file: HTSSLReader.h
=======================

#ifndef HTSSLREADER_H
#define HTSSLREADER_H

#include "www/HTIOStream.h"
#include "www/HTSSLMan.h"


#define INPUT_BUFFER_SIZE    32*1024

extern HTInput_new HTSSLReader_new;

#endif

=====================================================
=====================================================

New file: HTSSLReader.c
=======================

/* Library Include files */
#include "www/sysdep.h"
#include "www/WWWUtil.h"
#include "www/WWWCore.h"
#include "www/HTNetMan.h"
#include "www/HTSSLReader.h"                                     


struct _HTStream {
    const HTStreamClass *       isa;
    /* ... */
};

struct _HTInputStream {
    const HTInputStreamClass *  isa;
    HTChannel *                         ch;
    HTHost *                            host;
    char *                                  write;                      /* Last
byte written */
    char *                              read;                      /* Last byte
read */
    int                                     b_read;
    char                                    data [INPUT_BUFFER_SIZE];      /*
buffer */


        /******************************* HERE IS NEW MEMEBER ***********/
    HTSSL *                     htssl;


};

/* ------------------------------------------------------------------------- */

  PRIVATE int HTReader_flush (HTInputStream * me)
{
    HTNet * net = HTHost_getReadNet(me->host);
    return net && net->readStream ?
(*net->readStream->isa->flush)(net->readStream) : HT_OK;
}

PRIVATE int HTReader_free (HTInputStream * me)
{
    HTNet * net = HTHost_getReadNet(me->host);


 /******************************* SSL STUFF *********************/

    if (PROT_TRACE) HTTrace("HTReader_free ~~~~~~~~~~\n");
    if (me->htssl){    
      HTSSL_free(me->htssl);
      me->htssl = NULL;

      /* do it just because SSLeay doesn't want to create a new ssl structure
         on a non-fresh socket */
      HTHost_setCloseNotification (me->host, YES);   
    }

    
    if (net && net->readStream) {
        int status = (*net->readStream->isa->_free)(net->readStream);
        if (status == HT_OK) net->readStream = NULL;
        return status;
    }
    return HT_OK;
}

PRIVATE int HTReader_abort (HTInputStream * me, HTList * e)
{
    HTNet * net = HTHost_getReadNet(me->host);

    if (PROT_TRACE) HTTrace("HTReader_abort ********* ~~~~~~~~~~\n");
    if (net && net->readStream) {
        int status = (*net->readStream->isa->abort)(net->readStream, NULL);
        if (status != HT_IGNORE) net->readStream = NULL;
    }
    return HT_ERROR;
}

/*      Push data from a socket down a stream
**      -------------------------------------
**
**   This routine is responsible for creating and PRESENTING any
**   graphic (or other) objects described by the file. As this function
**   max reads a chunk of data on size INPUT_BUFFER_SIZE, it can be used
**   with both blocking or non-blocking sockets. It will always return to
**   the event loop, however if we are using blocking I/O then we get a full
**   buffer read, otherwise we get what's available.
**
** Returns      HT_LOADED       if finished reading
**              HT_OK           if OK, but more to read
**              HT_ERROR        if error,
**              HT_WOULD_BLOCK  if read or write would block
**              HT_PAUSE        if stream is paused
*/
char * strnstr_1(char * haystack, int *pLen, char * needle)
{
    int found = 0;
    int need = strlen(needle);
    int i, start;
    for (start = i = 0; i < *pLen; i++)
        if (haystack[i] == needle[found]) {
            if (++found == need) {
                i -= need - 1; /* beginning of string */
                *pLen -= i;
                return haystack+i;
            }
        } else {
            found = 0;
        }
    *pLen = 0;
    return NULL;
}



/* int DebugBufferSize = INPUT_BUFFER_SIZE; */

PRIVATE int HTReader_read (HTInputStream * me)
{
    HTHost * host = me->host;
    SOCKET soc = HTChannel_socket(me->ch);
    HTNet * net = HTHost_getReadNet(host);
    HTRequest * request = HTNet_request(net);
    int status;
    if (!net->readStream) {
        if (PROT_TRACE) HTTrace("Read Socket. No read stream for net object
%p\n", net);
        return HT_ERROR;
    }
        
    /*    me->b_read = me->read - me->data; */
    /* Read from socket if we got rid of all the data previously read */
    do {
        /* don't read if we have to push unwritten data from last call */
        if (me->write >= me->read) {
        reread_after_interrupt:
            me->b_read = 0;
            me->data[0] ='\0';
/*        me->b_read = NETREAD(soc, me->data, INPUT_BUFFER_SIZE); */
            
            if (!me->htssl) {
                me->htssl = HTSSL_new(soc);
                if (!me->htssl) {
                    /* notify socket close */
                    HTHost_setCloseNotification (me->host, YES); 
                    host->broken_pipe = YES;
                    HTRequest_addSystemError(net->request, 
                                             ERR_FATAL, socerrno, NO,
                                             "NETWRITE");
                    return HT_ERROR;
                }
                
            }

 /******************************* read using SSL **********************/        
   
            me->b_read = HTSSL_read(me->htssl, soc, me->data,   
INPUT_BUFFER_SIZE);     
                
                if (errno == EINTR)
            {
                    if ( me->b_read < 0 )
                {
                    goto reread_after_interrupt;
                }
            }
            
            if(me->b_read < 0) {
            

#ifdef EAGAIN
                if (socerrno==EAGAIN || socerrno==EWOULDBLOCK)      /* POSIX */
#else
                if (socerrno==EWOULDBLOCK)                                  /*
BSD */
#endif  
                {
                    if (PROT_TRACE)
                        HTTrace("Read Socket. WOULD BLOCK fd %d\n",soc);
                    HTHost_register(host, net, HTEvent_READ);
                    return HT_WOULD_BLOCK;

#ifdef ECONNRESET
            } else if (!me->b_read || socerrno==ECONNRESET) {
            goto socketClosed;
#else
            } else if (!me->b_read) {
            goto socketClosed;
#endif
            
#ifdef EPIPE
                } else if (socerrno == EPIPE) {
                    goto socketClosed;
#endif /* EPIPE */
                } else {                             /* We have a real error */
            
                    /* HERE WE SHOULD RETURN target abort */
                    if (request)
              HTRequest_addSystemError(request, ERR_FATAL, socerrno,
                                       NO, "NETREAD");
            
            HTSSL_set_disconnected(me->htssl);   
                    return HT_ERROR;
                }
/*#ifdef ECONNRESET
  } else if (!me->b_read || socerrno==ECONNRESET) { 
#else */
            } else if (!me->b_read) {
/*#endif*/
                
            socketClosed:
                
                if (PROT_TRACE)
                    HTTrace("Read Socket. FIN received on socket %d\n", soc);
                if (request) {
                    HTAlertCallback *cbf = HTAlert_find(HT_PROG_DONE);
                    if (PROT_TRACE)
                        if (cbf) (*cbf)(request, HT_PROG_DONE,
                                        HT_MSG_NULL, NULL, NULL, NULL);
                }
                HTHost_unregister(host, net, HTEvent_READ);
                HTHost_register(host, net, HTEvent_CLOSE);

                HTSSL_set_disconnected(me->htssl);    
                HTSSL_free(me->htssl);
                me->htssl = NULL;
        
                return HT_CLOSED;
            }

            /* Remember how much we have read from the input socket */
            HTTraceData(me->data, me->b_read, "HTReader.... Reading");
            me->write = me->data;
            me->read = me->data + me->b_read;
#ifdef FIND_SIGNATURES
            {
                char * ptr = me->data;
                int len = me->b_read;
                while ((ptr = strnstr_1(ptr, &len, "HTTP/1.1 200 OK")) != NULL)
{
                    if (PROT_TRACE)
                        HTTrace("Read Socket. Signature found at 0x%x of
0x%x.\n", ptr - me->data, me->b_read);
                    ptr++;
                    len--;
                }
            }
#endif /* FIND_SIGNATURES */
#ifdef NOT_ASCII
            {
                char *p = me->data;
                while (p < me->read) {
                    *p = FROMASCII(*p);
                    p++;
                }
            }
#endif /* NOT_ASCII */

            if (PROT_TRACE)
                HTTrace("Read Socket. %d bytes read from socket %d\n",
                        me->b_read, soc);
            if (request) {
                HTAlertCallback * cbf = HTAlert_find(HT_PROG_READ);
#if 0
                /* byte account is done later */
                net->bytesRead += me->b_read;
#endif
                if (cbf) (*cbf)(request, HT_PROG_READ,
                                HT_MSG_NULL, NULL, NULL, NULL);
            }
        }

        /* Now push the data down the stream */
        if ((status = (*net->readStream->isa->put_block)
             (net->readStream, me->write, me->b_read)) != HT_OK) {
            if (status == HT_WOULD_BLOCK) {
                if (PROT_TRACE) HTTrace("Read Socket. Target WOULD BLOCK\n");
                HTHost_unregister(host, net, HTEvent_READ);
                return HT_WOULD_BLOCK;
            } else if (status == HT_PAUSE) {
                if (PROT_TRACE) HTTrace("Read Socket. Target PAUSED\n");
                HTHost_unregister(host, net, HTEvent_READ);
                return HT_PAUSE;
            /* CONTINUE code or stream code means data was consumed */
            } else if (status == HT_CONTINUE || status > 0) {
                if (status == HT_CONTINUE) {
                    if (PROT_TRACE) HTTrace("Read Socket. CONTINUE\n");
                } else
                    if (PROT_TRACE)
                        HTTrace("Read Socket. Target returns %d\n", status);
/*              me->write = me->read; */
                return status;
            } else {                                 /* We have a real error */
                if (PROT_TRACE) HTTrace("Read Socket. Target ERROR %d\n",
status);
                return status;
            }
        }
        me->write = me->read;
        {
            int remaining = HTHost_remainingRead(host);
            if (remaining > 0) {
                if (PROT_TRACE)
                    HTTrace("Read Socket. DIDN'T CONSUME %d BYTES: `%s\'\n",
                            remaining, me->read);
                HTHost_setConsumed(host, remaining);
            }
        }
    } while (net->preemptive);
    HTHost_register(host, net, HTEvent_READ);
    return HT_WOULD_BLOCK;
}

/*
**      The difference between the close and the free method is that we don't
**      close the connection in the free method - we only call the free method
**      of the target stream. That way, we can keep the input stream as long 
**      as the channel itself.
*/
PRIVATE int HTReader_close (HTInputStream * me)
{
    int status = HT_OK;
    HTNet * net = HTHost_getReadNet(me->host);
    if (net && net->readStream) {
        if ((status =
(*net->readStream->isa->_free)(net->readStream))==HT_WOULD_BLOCK)
            return HT_WOULD_BLOCK;
        net->readStream = NULL;
    }
    if (PROT_TRACE) HTTrace("Socket read. FREEING....\n");
    HT_FREE(me);
    return status;
}

PUBLIC int HTSSLReader_consumed (HTInputStream * me, size_t bytes)
{
    me->write += bytes;
    me->b_read -= bytes;
    HTHost_setRemainingRead(me->host, me->b_read);
    return HT_OK;
}

PRIVATE const HTInputStreamClass HTSSLReader =
{
    "SocketReader",
    HTReader_flush,
    HTReader_free,
    HTReader_abort,
    HTReader_read,
    HTReader_close,
    HTSSLReader_consumed
}; 

/*
**      Create a new input read stream. Before we actually create it we check
**      to see whether we already have an input stream for this channel and if
**      so we just return that. This means that we can reuse input streams 
**      in persistent connections, for example.
*/

PUBLIC HTInputStream * HTSSLReader_new (HTHost * host, HTChannel * ch,
                                     void * param, int mode)
{
    if (host && ch) {
        HTInputStream * me = HTChannel_input(ch);
        if (me == NULL) {
            if ((me=(HTInputStream *) HT_CALLOC(1, sizeof(HTInputStream))) ==
NULL)
              HT_OUTOFMEM("HTReader_new");
            me->isa = &HTSSLReader;
            me->ch = ch;
            me->host = host;
            me->htssl = NULL;
        }
        return me;
    }
    return NULL;
}  

=====================================================
=====================================================

New file: HTSSLWriter.h
=======================

#ifndef HTSSLWRITE_H
#define HTSSLWRITE_H

#include "www/HTIOStream.h"
#include "www/HTSSLMan.h"

/*

 */
extern HTOutput_new HTSSLWriter_new;

extern BOOL HTSSLWriter_set (HTOutputStream *      me,
                             HTNet *               net,
                             HTChannel *           ch,
                             void *                param,
                             int                   mode);

/*

 */
#endif

=====================================================
=====================================================

New file: HTSSLWriter.c
=======================

/* Library include files */
#include "www/sysdep.h"
#include "www/WWWUtil.h"
#include "www/WWWCore.h"
#include "www/HTNet.h"
#include "www/HTNetMan.h"
#include "www/HTSSLWriter.h"                                     /* Implemented
here */

#include "www/HTHstMan.h"

struct _HTStream {
    const HTStreamClass *       isa;
    /* ... */
};

struct _HTOutputStream {
    const HTOutputStreamClass * isa;
    HTChannel *                 ch;
    HTHost *                    host;
    int                         offset;
#ifdef NOT_ASCII
    char *                      ascbuf;     /* Buffer for TOASCII conversion */
#endif
    HTSSL *         htssl;
};

/* ------------------------------------------------------------------------- */

PRIVATE int HTWriter_flush (HTOutputStream * me)
{
    return HT_OK;                      /* As we don't have any output buffer */
}

PRIVATE int HTWriter_free (HTOutputStream * me)
{
    if (PROT_TRACE) HTTrace("HTWriter_free ~~~~~~~~~~\n");
    if (me->htssl){    
        HTSSL_free(me->htssl);
        me->htssl = NULL;

        /* do it just because SSLeay doesn't want to create a new ssl structure
           on a non-fresh socket */
        HTHost_setCloseNotification (me->host, YES);        
    }
    return HT_OK;
}

PRIVATE int HTWriter_abort (HTOutputStream * me, HTList * e)
{
    if (PROT_TRACE) HTTrace("HHWriter_abort *********~~~~~~~~~~\n");
    return HT_ERROR;
}

/*      Write to the socket
**
** According to Solaris 2.3 man on write:
**
**    o If O_NONBLOCK and O_NDELAY are clear, write() blocks
**      until the data can be accepted.
**
**    o If O_NONBLOCK or O_NDELAY is set, write()  does  not
**      block  the  process.   If  some  data  can be written
**      without blocking the process, write() writes what  it
**      can  and returns the number of bytes written.  Other-
**      wise, if O_NONBLOCK is set, it returns - 1  and  sets
**      errno to EAGAIN or if O_NDELAY is set, it returns 0.
**
** According to SunOS 4.1.1 man on write:
**
**   +  If the descriptor is  marked  for  non-blocking  I/O
**      using  fcntl()  to  set  the FNONBLOCK or O_NONBLOCK
**      flag (defined in  <sys/fcntl.h>),  write()  requests
**      for  {PIPE_BUF}  (see  pathconf(2V))  or fewer bytes
**      either  succeed  completely  and  return  nbyte,  or
**      return -1 and set errno to EAGAIN. A write() request
**      for greater than {PIPE_BUF} bytes  either  transfers
**      what it can and returns the number of bytes written,
**      or transfers no data and returns -1 and  sets  errno
**      to  EAGAIN.  If  a  write()  request is greater than
**      {PIPE_BUF} bytes and all data previously written  to
**      the  pipe  has been read, write() transfers at least
**      {PIPE_BUF} bytes.
*/

PRIVATE int HTWriter_write (HTOutputStream * me, const char * buf, int len)
{
    HTHost * host = me->host;
    SOCKET soc = HTChannel_socket(HTHost_channel(host));
    HTNet * net = HTHost_getWriteNet(host);
    int b_write;
    char * wrtp;
    const char *limit = buf+len;

/*#ifdef EAI_DBG
  printf ("\nIn HTWriter_write\n");
  fflush(stdout);
  fflush(stderr);
  #endif
  */
    /* If we don't have a Net object then return right away */
    if (!net) {
        if (STREAM_TRACE)
            HTTrace("Write Socket WOULD BLOCK %d (offset %d)\n",soc,
me->offset);
        return HT_ERROR;
    }

#ifdef NOT_ASCII
    if (len && !me->ascbuf) {                         /* Generate new buffer */
        const char *orig = buf;
        char *dest;
        int cnt;
        if ((me->ascbuf = (char  *) HT_MALLOC(len)) == NULL)
          HT_OUTOFMEM("HTWriter_write");
        dest = me->ascbuf;
        for (cnt=0; cnt<len; cnt++) {
            *dest = TOASCII(*orig);
            dest++, orig++;
        }
        wrtp = me->ascbuf;
        limit = me->ascbuf+len;
    }
#else
    if (!me->offset)
      wrtp = (char *) buf;
    else {
        wrtp = (char *) buf + me->offset;
        len -= me->offset;
        me->offset = 0;
    }
#endif

    /*HTTrace("\nGoinig to NETWRITE = %s\n\n", wrtp);*/
    
    /* setting ssl */

    if (!me->htssl) {
        me->htssl = HTSSL_new(soc);
        if (!me->htssl) {
            /* notify socket close - doesn't work... */
            HTHost_setCloseNotification (me->host, YES);  
            host->broken_pipe = YES;
            HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO,
                                     "NETWRITE");
            return HT_ERROR;
        }
    }


    /* Write data to the network */
    while (wrtp < limit) {
        /*b_write = NETWRITE(soc, wrtp, len);*/

        b_write = HTSSL_write(me->htssl, soc, wrtp, len);
        
        if (b_write < 0) {
            
#ifdef EAI_DBG
            printf ("\nIn SOCKERRNO = \n");
            fflush(stdout);
            fflush(stderr);
#endif


#ifdef EAGAIN
            if (socerrno == EAGAIN || socerrno == EWOULDBLOCK)/* POSIX, SVR4 */
#else
            if (socerrno == EWOULDBLOCK)                              /* BSD */
#endif
            {
                HTHost_register(host, net, HTEvent_WRITE);
                me->offset = wrtp - buf;
                if (STREAM_TRACE)
                    HTTrace("Write Socket WOULD BLOCK %d (offset %d)\n",soc,
me->offset);
                return HT_WOULD_BLOCK;
#ifdef EINTR
            } else if (socerrno == EINTR) {
                /*
                **      EINTR   A signal was caught during the  write  opera-
                **              tion and no data was transferred.
                */
                if (STREAM_TRACE)
                    HTTrace("Write Socket call interruted - try again\n");
                continue;
#endif
            } else {
#ifdef EPIPE
                if (socerrno == EPIPE)
                    if (STREAM_TRACE) HTTrace("Write Socket got EPIPE\n");
#endif /* EPIPE */
                host->broken_pipe = YES;
                HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO,
                                         "NETWRITE");

        /* notify HTSSL (shared) object that sock connection is broken 
           so that next read/write will reconnect */
        HTSSL_set_disconnected(me->htssl);
        
                return HT_ERROR;
            }
        }

        /* We do this unconditionally, should we check to see if we ever
blocked? */
        HTTraceData(wrtp, b_write, "Writing to socket %d", soc);
        HTNet_addBytesWritten(net, b_write);
        wrtp += b_write;
        len -= b_write;
        if (STREAM_TRACE) HTTrace("Write Socket %d bytes written to %d\n",
b_write, soc);
        {
            HTAlertCallback *cbf = HTAlert_find(HT_PROG_WRITE);
            if (cbf) (*cbf)(net->request, HT_PROG_WRITE,
                            HT_MSG_NULL, NULL, NULL, NULL);
        }
    }
#ifdef NOT_ASCII
    HT_FREE(me->ascbuf);
#endif
    return HT_OK;
}

/*      Character handling
**      ------------------
*/
PRIVATE int HTWriter_put_character (HTOutputStream * me, char c)
{
    return HTWriter_write(me, &c, 1);
}

/*      String handling
**      ---------------
**
**      Strings must be smaller than this buffer size.
*/
PRIVATE int HTWriter_put_string (HTOutputStream * me, const char * s)
{
    return HTWriter_write(me, s, (int) strlen(s));
}
/*
**      The difference between the close and the free method is that we don't
**      close the connection in the free method - we only call the free method
**      of the target stream. That way, we can keep the output stream as long 
**      as the channel itself.
*/
PRIVATE int HTWriter_close (HTOutputStream * me)
{
    if (STREAM_TRACE) HTTrace("Socket write FREEING....\n");
    HT_FREE(me);
    return HT_OK;
}

PRIVATE const HTOutputStreamClass HTWriter =
{               
    "SocketWriter",
    HTWriter_flush,
    HTWriter_free,
    HTWriter_abort,
    HTWriter_put_character,
    HTWriter_put_string,
    HTWriter_write,
    HTWriter_close
}; 


PUBLIC HTOutputStream * HTSSLWriter_new (HTHost * host, HTChannel * ch,
                                      void * param, int mode)
{
    if (CORE_TRACE) HTTrace("\nHTSSLWriter_new: creating
--------------------------\n\n ");
    
    if (host && ch) {
        HTOutputStream * me = HTChannel_output(ch);
        if (!me) {
            if ((me=(HTOutputStream *) HT_CALLOC(1,
sizeof(HTOutputStream)))==NULL)
              HT_OUTOFMEM("HTWriter_new");
            me->isa = &HTWriter;
            me->ch = ch;
            me->host = host;
            me->htssl = NULL;
        }
        
        return me;
    }

    return NULL;
}                                      

Received on Thursday, 1 April 1999 14:42:15 UTC