Index: /pjproject/trunk/pjlib/include/pj/ssl_sock.h
===================================================================
--- /pjproject/trunk/pjlib/include/pj/ssl_sock.h	(revision 5937)
+++ /pjproject/trunk/pjlib/include/pj/ssl_sock.h	(revision 5938)
@@ -706,4 +706,5 @@
      */
     PJ_SSL_SOCK_PROTO_SSL23   = (1 << 16) - 1,
+    PJ_SSL_SOCK_PROTO_ALL = PJ_SSL_SOCK_PROTO_SSL23,
 
     /**
Index: /pjproject/trunk/pjlib/src/pj/ssl_sock_gtls.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/ssl_sock_gtls.c	(revision 5937)
+++ /pjproject/trunk/pjlib/src/pj/ssl_sock_gtls.c	(revision 5938)
@@ -45,8 +45,10 @@
     (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_GNUTLS)
 
+#define SSL_SOCK_IMP_USE_CIRC_BUF
+
+#include "ssl_sock_imp_common.h"
+#include "ssl_sock_imp_common.c"
+
 #define THIS_FILE               "ssl_sock_gtls.c"
-
-/* Workaround for ticket #985 */
-#define DELAYED_CLOSE_TIMEOUT   200
 
 /* Maximum ciphers */
@@ -70,286 +72,16 @@
 
 
-/* TLS state enumeration. */
-enum tls_connection_state {
-    TLS_STATE_NULL,
-    TLS_STATE_HANDSHAKING,
-    TLS_STATE_ESTABLISHED
-};
-
-/* Internal timer types. */
-enum timer_id {
-    TIMER_NONE,
-    TIMER_HANDSHAKE_TIMEOUT,
-    TIMER_CLOSE
-};
-
-/* Structure of SSL socket read buffer. */
-typedef struct read_data_t {
-    void *data;
-    pj_size_t len;
-} read_data_t;
-
-/*
- * Get the offset of pointer to read-buffer of SSL socket from read-buffer
- * of active socket. Note that both SSL socket and active socket employ
- * different but correlated read-buffers (as much as async_cnt for each),
- * and to make it easier/faster to find corresponding SSL socket's read-buffer
- * from known active socket's read-buffer, the pointer of corresponding
- * SSL socket's read-buffer is stored right after the end of active socket's
- * read-buffer.
- */
-#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \
-                                        (read_data_t**) \
-                                        ((pj_int8_t *)(asock_rbuf) + \
-                                        ssock->param.read_buffer_size)
-
-/* Structure of SSL socket write data. */
-typedef struct write_data_t {
-    PJ_DECL_LIST_MEMBER(struct write_data_t);
-    pj_ioqueue_op_key_t  key;
-    pj_size_t            record_len;
-    pj_ioqueue_op_key_t *app_key;
-    pj_size_t            plain_data_len;
-    pj_size_t            data_len;
-    unsigned             flags;
-    union {
-        char             content[1];
-        const char      *ptr;
-    } data;
-} write_data_t;
-
-
-/* Structure of SSL socket write buffer (circular buffer). */
-typedef struct send_buf_t {
-    char                *buf;
-    pj_size_t            max_len;
-    char                *start;
-    pj_size_t            len;
-} send_buf_t;
-
-
-/* Circular buffer object */
-typedef struct circ_buf_t {
-    pj_size_t      cap;    /* maximum number of elements (must be power of 2) */
-    pj_size_t      readp;  /* index of oldest element */
-    pj_size_t      writep; /* index at which to write new element  */
-    pj_size_t      size;   /* number of elements */
-    pj_uint8_t    *buf;    /* data buffer */
-    pj_pool_t     *pool;   /* where new allocations will take place */
-} circ_buf_t;
-
-
 /* Secure socket structure definition. */
-struct pj_ssl_sock_t {
-    pj_pool_t            *pool;
-    pj_ssl_sock_t        *parent;
-    pj_ssl_sock_param     param;
-    pj_ssl_sock_param     newsock_param;
-    pj_ssl_cert_t        *cert;
-
-    pj_ssl_cert_info      local_cert_info;
-    pj_ssl_cert_info      remote_cert_info;
-
-    pj_bool_t             is_server;
-    enum tls_connection_state connection_state;
-    pj_ioqueue_op_key_t   handshake_op_key;
-    pj_timer_entry        timer;
-    pj_status_t           verify_status;
-
-    int                   last_err;
-
-    pj_sock_t             sock;
-    pj_activesock_t      *asock;
-
-    pj_sockaddr           local_addr;
-    pj_sockaddr           rem_addr;
-    int                   addr_len;
-
-    pj_bool_t             read_started;
-    pj_size_t             read_size;
-    pj_uint32_t           read_flags;
-    void                **asock_rbuf;
-    read_data_t          *ssock_rbuf;
-
-    write_data_t          write_pending;       /* list of pending writes */
-    write_data_t          write_pending_empty; /* cache for write_pending */
-    pj_bool_t             flushing_write_pend; /* flag of flushing is ongoing */
-    send_buf_t            send_buf;
-    write_data_t          send_pending; /* list of pending write to network */
+typedef struct gnutls_sock_t {
+    pj_ssl_sock_t  	  base;
 
     gnutls_session_t      session;
     gnutls_certificate_credentials_t xcred;
 
-    circ_buf_t            circ_buf_input;
-    pj_lock_t            *circ_buf_input_mutex;
-
-    circ_buf_t            circ_buf_output;
-    pj_lock_t            *circ_buf_output_mutex;
-
     int                   tls_init_count; /* library initialization counter */
-};
-
-/*
- * Certificate/credential structure definition.
- */
-struct pj_ssl_cert_t
-{
-    pj_str_t CA_file;
-    pj_str_t CA_path;
-    pj_str_t cert_file;
-    pj_str_t privkey_file;
-    pj_str_t privkey_pass;
-
-    /* Certificate buffer. */
-    pj_ssl_cert_buffer CA_buf;
-    pj_ssl_cert_buffer cert_buf;
-    pj_ssl_cert_buffer privkey_buf;
-};
-
-
-/* GnuTLS available ciphers */
-static unsigned tls_available_ciphers;
-
-/* Array of id/names for available ciphers */
-static struct tls_ciphers_t {
-    pj_ssl_cipher id;
-    const char *name;
-} tls_ciphers[MAX_CIPHERS];
+} gnutls_sock_t;
 
 /* Last error reported somehow */
 static int tls_last_error;
-
-
-/*
- *******************************************************************
- * Circular buffer functions.
- *******************************************************************
- */
-
-static pj_status_t circ_init(pj_pool_factory *factory,
-                             circ_buf_t *cb, pj_size_t cap)
-{
-    cb->cap    = cap;
-    cb->readp  = 0;
-    cb->writep = 0;
-    cb->size   = 0;
-
-    /* Initial pool holding the buffer elements */
-    cb->pool = pj_pool_create(factory, "tls-circ%p", cap, cap, NULL);
-    if (!cb->pool)
-        return PJ_ENOMEM;
-
-    /* Allocate circular buffer */
-    cb->buf = pj_pool_alloc(cb->pool, cap);
-    if (!cb->buf) {
-        pj_pool_release(cb->pool);
-        return PJ_ENOMEM;
-    }
-
-    return PJ_SUCCESS;
-}
-
-static void circ_deinit(circ_buf_t *cb)
-{
-    if (cb->pool) {
-        pj_pool_release(cb->pool);
-        cb->pool = NULL;
-    }
-}
-
-static pj_bool_t circ_empty(const circ_buf_t *cb)
-{
-    return cb->size == 0;
-}
-
-static pj_size_t circ_size(const circ_buf_t *cb)
-{
-    return cb->size;
-}
-
-static pj_size_t circ_avail(const circ_buf_t *cb)
-{
-    return cb->cap - cb->size;
-}
-
-static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len)
-{
-    pj_size_t size_after = cb->cap - cb->readp;
-    pj_size_t tbc = PJ_MIN(size_after, len);
-    pj_size_t rem = len - tbc;
-
-    pj_memcpy(dst, cb->buf + cb->readp, tbc);
-    pj_memcpy(dst + tbc, cb->buf, rem);
-
-    cb->readp += len;
-    cb->readp &= (cb->cap - 1);
-
-    cb->size -= len;
-}
-
-static pj_status_t circ_write(circ_buf_t *cb,
-                              const pj_uint8_t *src, pj_size_t len)
-{
-    /* Overflow condition: resize */
-    if (len > circ_avail(cb)) {
-        /* Minimum required capacity */
-        pj_size_t min_cap = len + cb->size;
-
-        /* Next 32-bit power of two */
-        min_cap--;
-        min_cap |= min_cap >> 1;
-        min_cap |= min_cap >> 2;
-        min_cap |= min_cap >> 4;
-        min_cap |= min_cap >> 8;
-        min_cap |= min_cap >> 16;
-        min_cap++;
-
-        /* Create a new pool to hold a bigger buffer, using the same factory */
-        pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p",
-                                         min_cap, min_cap, NULL);
-        if (!pool)
-            return PJ_ENOMEM;
-
-        /* Allocate our new buffer */
-        pj_uint8_t *buf = pj_pool_alloc(pool, min_cap);
-        if (!buf) {
-            pj_pool_release(pool);
-            return PJ_ENOMEM;
-        }
-
-        /* Save old size, which we shall restore after the next read */
-        pj_size_t old_size = cb->size;
-
-        /* Copy old data into beginning of new buffer */
-        circ_read(cb, buf, cb->size);
-
-        /* Restore old size now */
-        cb->size = old_size;
-
-        /* Release the previous pool */
-        pj_pool_release(cb->pool);
-
-        /* Update circular buffer members */
-        cb->pool = pool;
-        cb->buf = buf;
-        cb->readp = 0;
-        cb->writep = cb->size;
-        cb->cap = min_cap;
-    }
-
-    pj_size_t size_after = cb->cap - cb->writep;
-    pj_size_t tbc = PJ_MIN(size_after, len);
-    pj_size_t rem = len - tbc;
-
-    pj_memcpy(cb->buf + cb->writep, src, tbc);
-    pj_memcpy(cb->buf, src + tbc, rem);
-
-    cb->writep += len;
-    cb->writep &= (cb->cap - 1);
-
-    cb->size += len;
-
-    return PJ_SUCCESS;
-}
 
 
@@ -491,8 +223,8 @@
 
     /* Init available ciphers */
-    if (!tls_available_ciphers) {
+    if (!ssl_cipher_num) {
         unsigned int i;
 
-        for (i = 0; i<PJ_ARRAY_SIZE(tls_ciphers); i++) {
+        for (i = 0; i<PJ_ARRAY_SIZE(ssl_ciphers); i++) {
             unsigned char id[2];
             const char *suite;
@@ -500,17 +232,17 @@
             suite = gnutls_cipher_suite_info(i, (unsigned char *)id,
                                              NULL, NULL, NULL, NULL);
-            tls_ciphers[i].id = 0;
+            ssl_ciphers[i].id = 0;
             /* usually the array size is bigger than the number of available
              * ciphers anyway, so by checking here we can exit the loop as soon
              * as either all ciphers have been added or the array is full */
             if (suite) {
-                tls_ciphers[i].id = (pj_ssl_cipher)
+                ssl_ciphers[i].id = (pj_ssl_cipher)
                     (pj_uint32_t) ((id[0] << 8) | id[1]);
-                tls_ciphers[i].name = suite;
+                ssl_ciphers[i].name = suite;
             } else
                 break;
         }
 
-        tls_available_ciphers = i;
+        ssl_cipher_num = i;
     }
 
@@ -623,4 +355,5 @@
 {
     pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
 
     pj_lock_acquire(ssock->circ_buf_output_mutex);
@@ -628,5 +361,5 @@
         pj_lock_release(ssock->circ_buf_output_mutex);
 
-        gnutls_transport_set_errno(ssock->session, ENOMEM);
+        gnutls_transport_set_errno(gssock->session, ENOMEM);
         return -1;
     }
@@ -644,4 +377,5 @@
 {
     pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)ptr;
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
 
     pj_lock_acquire(ssock->circ_buf_input_mutex);
@@ -651,5 +385,5 @@
 
         /* Data buffers not yet filled */
-        gnutls_transport_set_errno(ssock->session, EAGAIN);
+        gnutls_transport_set_errno(gssock->session, EAGAIN);
         return -1;
     }
@@ -684,4 +418,5 @@
 static pj_status_t tls_priorities_set(pj_ssl_sock_t *ssock)
 {
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
     char buf[1024];
     char priority_buf[256];
@@ -783,5 +518,5 @@
 
     /* Set our priority string */
-    ret = gnutls_priority_set_direct(ssock->session,
+    ret = gnutls_priority_set_direct(gssock->session,
                                         cipher_list.ptr, &err);
     if (ret < 0) {
@@ -797,11 +532,12 @@
 static pj_status_t tls_trust_set(pj_ssl_sock_t *ssock)
 {
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
     int ntrusts = 0;
     int err;
 
-    err = gnutls_certificate_set_x509_system_trust(ssock->xcred);
+    err = gnutls_certificate_set_x509_system_trust(gssock->xcred);
     if (err > 0)
         ntrusts += err;
-    err = gnutls_certificate_set_x509_trust_file(ssock->xcred,
+    err = gnutls_certificate_set_x509_trust_file(gssock->xcred,
                                                  TRUST_STORE_FILE1,
                                                  GNUTLS_X509_FMT_PEM);
@@ -809,5 +545,5 @@
         ntrusts += err;
 
-    err = gnutls_certificate_set_x509_trust_file(ssock->xcred,
+    err = gnutls_certificate_set_x509_trust_file(gssock->xcred,
                                                  TRUST_STORE_FILE2,
                                                  GNUTLS_X509_FMT_PEM);
@@ -875,7 +611,13 @@
 #endif
 
+static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool)
+{
+    return (pj_ssl_sock_t *)PJ_POOL_ZALLOC_T(pool, gnutls_sock_t);
+}
+
 /* Create and initialize new GnuTLS context and instance */
-static pj_status_t tls_open(pj_ssl_sock_t *ssock)
-{
+static pj_status_t ssl_create(pj_ssl_sock_t *ssock)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
     pj_ssl_cert_t *cert;
     pj_status_t status;
@@ -888,6 +630,6 @@
     /* Even if reopening is harmless, having one instance only simplifies
      * deallocating it later on */
-    if (!ssock->tls_init_count) {
-        ssock->tls_init_count++;
+    if (!gssock->tls_init_count) {
+        gssock->tls_init_count++;
         ret = tls_init();
         if (ret < 0)
@@ -897,5 +639,5 @@
 
     /* Start this socket session */
-    ret = gnutls_init(&ssock->session, ssock->is_server ? GNUTLS_SERVER
+    ret = gnutls_init(&gssock->session, ssock->is_server ? GNUTLS_SERVER
                                                         : GNUTLS_CLIENT);
     if (ret < 0)
@@ -904,7 +646,7 @@
     /* Set the ssock object to be retrieved by transport (send/recv) and by
      * user data from this session */
-    gnutls_transport_set_ptr(ssock->session,
+    gnutls_transport_set_ptr(gssock->session,
                              (gnutls_transport_ptr_t) (uintptr_t) ssock);
-    gnutls_session_set_ptr(ssock->session,
+    gnutls_session_set_ptr(gssock->session,
                            (gnutls_transport_ptr_t) (uintptr_t) ssock);
 
@@ -921,6 +663,6 @@
     /* Set the callback that allows GnuTLS to PUSH and PULL data
      * TO and FROM the transport layer */
-    gnutls_transport_set_push_function(ssock->session, tls_data_push);
-    gnutls_transport_set_pull_function(ssock->session, tls_data_pull);
+    gnutls_transport_set_push_function(gssock->session, tls_data_push);
+    gnutls_transport_set_pull_function(gssock->session, tls_data_pull);
 
     /* Determine which cipher suite to support */
@@ -930,8 +672,8 @@
 
     /* Allocate credentials for handshaking and transmission */
-    ret = gnutls_certificate_allocate_credentials(&ssock->xcred);
+    ret = gnutls_certificate_allocate_credentials(&gssock->xcred);
     if (ret < 0)
         goto out;
-    gnutls_certificate_set_verify_function(ssock->xcred, tls_cert_verify_cb);
+    gnutls_certificate_set_verify_function(gssock->xcred, tls_cert_verify_cb);
 
     /* Load system trust file(s) */
@@ -944,10 +686,10 @@
         /* Load CA if one is specified. */
         if (cert->CA_file.slen) {
-            ret = gnutls_certificate_set_x509_trust_file(ssock->xcred,
+            ret = gnutls_certificate_set_x509_trust_file(gssock->xcred,
                                                          cert->CA_file.ptr,
                                                          GNUTLS_X509_FMT_PEM);
             if (ret < 0)
                 ret = gnutls_certificate_set_x509_trust_file(
-                		ssock->xcred,
+                		gssock->xcred,
                                 cert->CA_file.ptr,
                                 GNUTLS_X509_FMT_DER);
@@ -956,10 +698,10 @@
         }
         if (cert->CA_path.slen) {
-            ret = gnutls_certificate_set_x509_trust_dir(ssock->xcred,
+            ret = gnutls_certificate_set_x509_trust_dir(gssock->xcred,
                                                          cert->CA_path.ptr,
                                                          GNUTLS_X509_FMT_PEM);
             if (ret < 0)
                 ret = gnutls_certificate_set_x509_trust_dir(
-                		ssock->xcred,
+                		gssock->xcred,
                                 cert->CA_path.ptr,
                                 GNUTLS_X509_FMT_DER);
@@ -974,5 +716,5 @@
                                     ? cert->privkey_pass.ptr
                                     : NULL;
-            ret = gnutls_certificate_set_x509_key_file2(ssock->xcred,
+            ret = gnutls_certificate_set_x509_key_file2(gssock->xcred,
                                                         cert->cert_file.ptr,
                                                         prikey_file,
@@ -981,5 +723,5 @@
                                                         0);
             if (ret != GNUTLS_E_SUCCESS)
-                ret = gnutls_certificate_set_x509_key_file2(ssock->xcred,
+                ret = gnutls_certificate_set_x509_key_file2(gssock->xcred,
                                                             cert->cert_file.ptr,
                                                             prikey_file,
@@ -995,10 +737,10 @@
             ca.data = (unsigned char*)cert->CA_buf.ptr;
             ca.size = cert->CA_buf.slen;
-            ret = gnutls_certificate_set_x509_trust_mem(ssock->xcred,
+            ret = gnutls_certificate_set_x509_trust_mem(gssock->xcred,
                                                         &ca,
                                                         GNUTLS_X509_FMT_PEM);
             if (ret < 0)
                 ret = gnutls_certificate_set_x509_trust_mem(
-                		ssock->xcred, &ca, GNUTLS_X509_FMT_DER);
+                		gssock->xcred, &ca, GNUTLS_X509_FMT_DER);
             if (ret < 0)
                 goto out;
@@ -1017,5 +759,5 @@
                                     ? cert->privkey_pass.ptr
                                     : NULL;
-            ret = gnutls_certificate_set_x509_key_mem2(ssock->xcred,
+            ret = gnutls_certificate_set_x509_key_mem2(gssock->xcred,
                                                        &cert_buf,
                                                        &privkey_buf,
@@ -1026,5 +768,5 @@
             /*
             if (ret != GNUTLS_E_SUCCESS)
-                ret = gnutls_certificate_set_x509_key_mem2(ssock->xcred,
+                ret = gnutls_certificate_set_x509_key_mem2(gssock->xcred,
                                                            &cert_buf,
                                                            &privkey_buf,
@@ -1040,10 +782,10 @@
     /* Require client certificate if asked */
     if (ssock->is_server && ssock->param.require_client_cert)
-        gnutls_certificate_server_set_request(ssock->session,
+        gnutls_certificate_server_set_request(gssock->session,
                                               GNUTLS_CERT_REQUIRE);
 
     /* Finally set credentials for this session */
-    ret = gnutls_credentials_set(ssock->session,
-                                 GNUTLS_CRD_CERTIFICATE, ssock->xcred);
+    ret = gnutls_credentials_set(gssock->session,
+                                 GNUTLS_CRD_CERTIFICATE, gssock->xcred);
     if (ret < 0)
         goto out;
@@ -1056,20 +798,22 @@
 
 /* Destroy GnuTLS credentials and session. */
-static void tls_close(pj_ssl_sock_t *ssock)
-{
-    if (ssock->session) {
-        gnutls_bye(ssock->session, GNUTLS_SHUT_RDWR);
-        gnutls_deinit(ssock->session);
-        ssock->session = NULL;
-    }
-
-    if (ssock->xcred) {
-        gnutls_certificate_free_credentials(ssock->xcred);
-        ssock->xcred = NULL;
+static void ssl_destroy(pj_ssl_sock_t *ssock)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
+
+    if (gssock->session) {
+        gnutls_bye(gssock->session, GNUTLS_SHUT_RDWR);
+        gnutls_deinit(gssock->session);
+        gssock->session = NULL;
+    }
+
+    if (gssock->xcred) {
+        gnutls_certificate_free_credentials(gssock->xcred);
+        gssock->xcred = NULL;
     }
 
     /* Free GnuTLS library */
-    if (ssock->tls_init_count) {
-        ssock->tls_init_count--;
+    if (gssock->tls_init_count) {
+        gssock->tls_init_count--;
         tls_deinit();
     }
@@ -1082,21 +826,50 @@
 
 /* Reset socket state. */
-static void tls_sock_reset(pj_ssl_sock_t *ssock)
-{
-    ssock->connection_state = TLS_STATE_NULL;
-
-    tls_close(ssock);
-
-    if (ssock->asock) {
-        pj_activesock_close(ssock->asock);
-        ssock->asock = NULL;
-        ssock->sock = PJ_INVALID_SOCKET;
-    }
-    if (ssock->sock != PJ_INVALID_SOCKET) {
-        pj_sock_close(ssock->sock);
-        ssock->sock = PJ_INVALID_SOCKET;
-    }
+static void ssl_reset_sock_state(pj_ssl_sock_t *ssock)
+{
+    pj_lock_acquire(ssock->circ_buf_output_mutex);
+    ssock->ssl_state = SSL_STATE_NULL;
+    pj_lock_release(ssock->circ_buf_output_mutex);
+
+    ssl_close_sockets(ssock);
 
     ssock->last_err = tls_last_error = GNUTLS_E_SUCCESS;
+}
+
+
+static void ssl_ciphers_populate(void)
+{
+     if (!ssl_cipher_num) {
+         tls_init();
+         tls_deinit();
+     }
+}
+
+
+static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
+    int i;
+    gnutls_cipher_algorithm_t lookup;
+    gnutls_cipher_algorithm_t cipher;
+
+    /* Current cipher */
+    cipher = gnutls_cipher_get(gssock->session);
+    for (i = 0; ; i++) {
+        unsigned char id[2];
+        const char *suite;
+        
+        suite = gnutls_cipher_suite_info(i,(unsigned char *)id, NULL,
+                                         &lookup, NULL, NULL);
+        if (suite) {
+            if (lookup == cipher) {
+                return (pj_uint32_t) ((id[0] << 8) | id[1]);
+            }
+        } else {
+            break;
+        }
+    }
+
+    return PJ_TLS_UNKNOWN_CIPHER;
 }
 
@@ -1252,6 +1025,7 @@
 /* Update local & remote certificates info. This function should be
  * called after handshake or renegotiation successfully completed. */
-static void tls_cert_update(pj_ssl_sock_t *ssock)
-{
+static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
     gnutls_x509_crt_t cert = NULL;
     const gnutls_datum_t *us;
@@ -1260,8 +1034,8 @@
     int ret = GNUTLS_CERT_INVALID;
 
-    pj_assert(ssock->connection_state == TLS_STATE_ESTABLISHED);
+    pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED);
 
     /* Get active local certificate */
-    us = gnutls_certificate_get_ours(ssock->session);
+    us = gnutls_certificate_get_ours(gssock->session);
     if (!us)
         goto us_out;
@@ -1289,5 +1063,5 @@
 
     /* Get active remote certificate */
-    certs = gnutls_certificate_get_peers(ssock->session, &certslen);
+    certs = gnutls_certificate_get_peers(gssock->session, &certslen);
     if (certs == NULL || certslen == 0)
         goto peer_out;
@@ -1315,355 +1089,38 @@
 }
 
-
-/* When handshake completed:
- * - notify application
- * - if handshake failed, reset SSL state
- * - return PJ_FALSE when SSL socket instance is destroyed by application. */
-static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock,
-                                       pj_status_t status)
-{
-    pj_bool_t ret = PJ_TRUE;
-
-    /* Cancel handshake timer */
-    if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) {
-        pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
-        ssock->timer.id = TIMER_NONE;
-    }
-
-    /* Update certificates info on successful handshake */
-    if (status == PJ_SUCCESS)
-        tls_cert_update(ssock);
-
-    /* Accepting */
-    if (ssock->is_server) {
-        if (status != PJ_SUCCESS) {
-            /* Handshake failed in accepting, destroy our self silently. */
-
-            char errmsg[PJ_ERR_MSG_SIZE];
-            char buf[PJ_INET6_ADDRSTRLEN + 10];
-
-            pj_strerror(status, errmsg, sizeof(errmsg));
+static void ssl_set_state(pj_ssl_sock_t *ssock, pj_bool_t is_server)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(is_server);
+}
+
+static void ssl_set_peer_name(pj_ssl_sock_t *ssock)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
+
+    /* Set server name to connect */
+    if (ssock->param.server_name.slen) {
+        int ret;
+        /* Server name is null terminated already */
+        ret = gnutls_server_name_set(gssock->session, GNUTLS_NAME_DNS,
+                                     ssock->param.server_name.ptr,
+                                     ssock->param.server_name.slen);
+        if (ret < 0) {
             PJ_LOG(3, (ssock->pool->obj_name,
-                       "Handshake failed in accepting %s: %s",
-                       pj_sockaddr_print(&ssock->rem_addr, buf,sizeof(buf),3),
-                       errmsg));
-
-            /* Workaround for ticket #985 */
-#if (defined(PJ_WIN32)&& PJ_WIN32 != 0) || (defined(PJ_WIN64)&& PJ_WIN64 != 0)
-            if (ssock->param.timer_heap) {
-                pj_time_val interval = {0, DELAYED_CLOSE_TIMEOUT};
-
-                tls_sock_reset(ssock);
-
-                ssock->timer.id = TIMER_CLOSE;
-                pj_time_val_normalize(&interval);
-                if (pj_timer_heap_schedule(ssock->param.timer_heap,
-                                           &ssock->timer, &interval) != 0)
-                {
-                    ssock->timer.id = TIMER_NONE;
-                    pj_ssl_sock_close(ssock);
-                }
-            } else
-#endif /* PJ_WIN32 */
-            {
-                pj_ssl_sock_close(ssock);
-            }
-
-            return PJ_FALSE;
-        }
-        /* Notify application the newly accepted SSL socket */
-        if (ssock->param.cb.on_accept_complete)
-            ret = (*ssock->param.cb.on_accept_complete)
-                      (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
-                       pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
-
-    } else { /* Connecting */
-        /* On failure, reset SSL socket state first, as app may try to
-         * reconnect in the callback. */
-        if (status != PJ_SUCCESS) {
-            /* Server disconnected us, possibly due to negotiation failure */
-            tls_sock_reset(ssock);
-        }
-        if (ssock->param.cb.on_connect_complete) {
-
-            ret = (*ssock->param.cb.on_connect_complete)(ssock, status);
-        }
-    }
-
-    return ret;
-}
-
-static write_data_t *alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len)
-{
-    send_buf_t *send_buf = &ssock->send_buf;
-    pj_size_t avail_len, skipped_len = 0;
-    char *reg1, *reg2;
-    pj_size_t reg1_len, reg2_len;
-    write_data_t *p;
-
-    /* Check buffer availability */
-    avail_len = send_buf->max_len - send_buf->len;
-    if (avail_len < len)
-        return NULL;
-
-    /* If buffer empty, reset start pointer and return it */
-    if (send_buf->len == 0) {
-        send_buf->start = send_buf->buf;
-        send_buf->len   = len;
-        p = (write_data_t*)send_buf->start;
-        goto init_send_data;
-    }
-
-    /* Free space may be wrapped/splitted into two regions, so let's
-     * analyze them if any region can hold the write data. */
-    reg1 = send_buf->start + send_buf->len;
-    if (reg1 >= send_buf->buf + send_buf->max_len)
-        reg1 -= send_buf->max_len;
-        reg1_len = send_buf->max_len - send_buf->len;
-    if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) {
-        reg1_len = send_buf->buf + send_buf->max_len - reg1;
-        reg2 = send_buf->buf;
-        reg2_len = send_buf->start - send_buf->buf;
-    } else {
-        reg2 = NULL;
-        reg2_len = 0;
-    }
-
-    /* More buffer availability check, note that the write data must be in
-     * a contigue buffer. */
-    avail_len = PJ_MAX(reg1_len, reg2_len);
-    if (avail_len < len)
-    return NULL;
-
-    /* Get the data slot */
-    if (reg1_len >= len) {
-        p = (write_data_t*)reg1;
-    } else {
-        p = (write_data_t*)reg2;
-        skipped_len = reg1_len;
-    }
-
-    /* Update buffer length */
-    send_buf->len += len + skipped_len;
-
-init_send_data:
-    /* Init the new send data */
-    pj_bzero(p, sizeof(*p));
-    pj_list_init(p);
-    pj_list_push_back(&ssock->send_pending, p);
-
-    return p;
-}
-
-static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata)
-{
-    send_buf_t *buf = &ssock->send_buf;
-    write_data_t *spl = &ssock->send_pending;
-
-    pj_assert(!pj_list_empty(&ssock->send_pending));
-
-    /* Free slot from the buffer */
-    if (spl->next == wdata && spl->prev == wdata) {
-    /* This is the only data, reset the buffer */
-    buf->start = buf->buf;
-    buf->len = 0;
-    } else if (spl->next == wdata) {
-    /* This is the first data, shift start pointer of the buffer and
-     * adjust the buffer length.
-     */
-    buf->start = (char*)wdata->next;
-    if (wdata->next > wdata) {
-        buf->len -= ((char*)wdata->next - buf->start);
-    } else {
-        /* Overlapped */
-        pj_size_t right_len, left_len;
-        right_len = buf->buf + buf->max_len - (char*)wdata;
-        left_len  = (char*)wdata->next - buf->buf;
-        buf->len -= (right_len + left_len);
-    }
-    } else if (spl->prev == wdata) {
-    /* This is the last data, just adjust the buffer length */
-    if (wdata->prev < wdata) {
-        pj_size_t jump_len;
-        jump_len = (char*)wdata -
-               ((char*)wdata->prev + wdata->prev->record_len);
-        buf->len -= (wdata->record_len + jump_len);
-    } else {
-        /* Overlapped */
-        pj_size_t right_len, left_len;
-        right_len = buf->buf + buf->max_len -
-            ((char*)wdata->prev + wdata->prev->record_len);
-        left_len  = (char*)wdata + wdata->record_len - buf->buf;
-        buf->len -= (right_len + left_len);
-    }
-    }
-    /* For data in the middle buffer, just do nothing on the buffer. The slot
-     * will be freed later when freeing the first/last data. */
-
-    /* Remove the data from send pending list */
-    pj_list_erase(wdata);
-}
-
-#if 0
-/* Just for testing send buffer alloc/free */
-#include <pj/rand.h>
-pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool)
-{
-    enum { MAX_CHUNK_NUM = 20 };
-    unsigned chunk_size, chunk_cnt, i;
-    write_data_t *wdata[MAX_CHUNK_NUM] = {0};
-    pj_time_val now;
-    pj_ssl_sock_t *ssock = NULL;
-    pj_ssl_sock_param param;
-    pj_status_t status;
-
-    pj_gettimeofday(&now);
-    pj_srand((unsigned)now.sec);
-
-    pj_ssl_sock_param_default(&param);
-    status = pj_ssl_sock_create(pool, &param, &ssock);
-    if (status != PJ_SUCCESS) {
-        return status;
-    }
-
-    if (ssock->send_buf.max_len == 0) {
-        ssock->send_buf.buf = (char *)
-                              pj_pool_alloc(ssock->pool,
-                                            ssock->param.send_buffer_size);
-        ssock->send_buf.max_len = ssock->param.send_buffer_size;
-        ssock->send_buf.start = ssock->send_buf.buf;
-        ssock->send_buf.len = 0;
-    }
-
-    chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2;
-    chunk_cnt = 0;
-    for (i = 0; i < MAX_CHUNK_NUM; i++) {
-        wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321);
-        if (wdata[i])
-            chunk_cnt++;
-        else
-            break;
-    }
-
-    while (chunk_cnt) {
-        i = pj_rand() % MAX_CHUNK_NUM;
-        if (wdata[i]) {
-            free_send_data(ssock, wdata[i]);
-            wdata[i] = NULL;
-            chunk_cnt--;
-        }
-    }
-
-    if (ssock->send_buf.len != 0)
-        status = PJ_EBUG;
-
-    pj_ssl_sock_close(ssock);
-    return status;
-}
-#endif
-
-/* Flush write circular buffer to network socket. */
-static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock,
-                                         pj_ioqueue_op_key_t *send_key,
-                                         pj_size_t orig_len, unsigned flags)
-{
-    pj_ssize_t len;
-    write_data_t *wdata;
-    pj_size_t needed_len;
-    pj_status_t status;
-
-    pj_lock_acquire(ssock->circ_buf_output_mutex);
-
-    /* Check if there is data in the circular buffer, flush it if any */
-    if (circ_empty(&ssock->circ_buf_output)) {
-        pj_lock_release(ssock->circ_buf_output_mutex);
-
-        return PJ_SUCCESS;
-    }
-
-    len = circ_size(&ssock->circ_buf_output);
-
-    /* Calculate buffer size needed, and align it to 8 */
-    needed_len = len + sizeof(write_data_t);
-    needed_len = ((needed_len + 7) >> 3) << 3;
-
-    /* Allocate buffer for send data */
-    wdata = alloc_send_data(ssock, needed_len);
-    if (wdata == NULL) {
-        pj_lock_release(ssock->circ_buf_output_mutex);
-        return PJ_ENOMEM;
-    }
-
-    /* Copy the data and set its properties into the send data */
-    pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t));
-    wdata->key.user_data = wdata;
-    wdata->app_key = send_key;
-    wdata->record_len = needed_len;
-    wdata->data_len = len;
-    wdata->plain_data_len = orig_len;
-    wdata->flags = flags;
-    circ_read(&ssock->circ_buf_output, (pj_uint8_t *)&wdata->data, len);
-
-    /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */
-    pj_lock_release(ssock->circ_buf_output_mutex);
-
-    /* Send it */
-    if (ssock->param.sock_type == pj_SOCK_STREAM()) {
-        status = pj_activesock_send(ssock->asock, &wdata->key,
-                                    wdata->data.content, &len,
-                                    flags);
-    } else {
-        status = pj_activesock_sendto(ssock->asock, &wdata->key,
-                                      wdata->data.content, &len,
-                                      flags,
-                                      (pj_sockaddr_t*)&ssock->rem_addr,
-                                      ssock->addr_len);
-    }
-
-    if (status != PJ_EPENDING) {
-        /* When the sending is not pending, remove the wdata from send
-         * pending list. */
-        pj_lock_acquire(ssock->circ_buf_output_mutex);
-        free_send_data(ssock, wdata);
-        pj_lock_release(ssock->circ_buf_output_mutex);
-    }
-
-    return status;
-}
-
-static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data;
-    int timer_id = te->id;
-
-    te->id = TIMER_NONE;
-
-    PJ_UNUSED_ARG(th);
-
-    switch (timer_id) {
-    case TIMER_HANDSHAKE_TIMEOUT:
-        PJ_LOG(1, (ssock->pool->obj_name, "TLS timeout after %d.%ds",
-                   ssock->param.timeout.sec, ssock->param.timeout.msec));
-
-        on_handshake_complete(ssock, PJ_ETIMEDOUT);
-        break;
-    case TIMER_CLOSE:
-        pj_ssl_sock_close(ssock);
-        break;
-    default:
-        pj_assert(!"Unknown timer");
-        break;
-    }
-}
-
+                       "gnutls_server_name_set() failed: %s",
+                       gnutls_strerror(ret)));
+        }
+    }
+}
 
 /* Try to perform an asynchronous handshake */
-static pj_status_t tls_try_handshake(pj_ssl_sock_t *ssock)
-{
+static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
     int ret;
     pj_status_t status;
 
     /* Perform SSL handshake */
-    ret = gnutls_handshake(ssock->session);
+    ret = gnutls_handshake(gssock->session);
 
     status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0);
@@ -1673,5 +1130,5 @@
     if (ret == GNUTLS_E_SUCCESS) {
         /* System are GO */
-        ssock->connection_state = TLS_STATE_ESTABLISHED;
+        ssock->ssl_state = SSL_STATE_ESTABLISHED;
         status = PJ_SUCCESS;
     } else if (!gnutls_error_is_fatal(ret)) {
@@ -1688,866 +1145,31 @@
 }
 
-
-/*
- *******************************************************************
- * Active socket callbacks.
- *******************************************************************
- */
-
-/* PJ_TRUE asks the socket to read more data, PJ_FALSE takes it off the queue */
-static pj_bool_t asock_on_data_read(pj_activesock_t *asock, void *data,
-                                    pj_size_t size, pj_status_t status,
-                                    pj_size_t *remainder)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)
-                           pj_activesock_get_user_data(asock);
-
-    pj_size_t app_remainder = 0;
-
-    if (data && size > 0) {
-        /* Push data into input circular buffer (for GnuTLS) */
-        pj_lock_acquire(ssock->circ_buf_input_mutex);
-        circ_write(&ssock->circ_buf_input, data, size);
-        pj_lock_release(ssock->circ_buf_input_mutex);
-    }
-
-    /* Check if SSL handshake hasn't finished yet */
-    if (ssock->connection_state == TLS_STATE_HANDSHAKING) {
-        pj_bool_t ret = PJ_TRUE;
-
-        if (status == PJ_SUCCESS)
-            status = tls_try_handshake(ssock);
-
-        /* Not pending is either success or failed */
-        if (status != PJ_EPENDING)
-            ret = on_handshake_complete(ssock, status);
-
-        return ret;
-    }
-
-    /* See if there is any decrypted data for the application */
-    if (ssock->read_started) {
-        do {
-            /* Get read data structure at the end of the data */
-            read_data_t *app_read_data =
-            	*(OFFSET_OF_READ_DATA_PTR(ssock, data));
-            int app_data_size = (int)(ssock->read_size - app_read_data->len);
-
-            /* Decrypt received data using GnuTLS (will read our input
-             * circular buffer) */
-            int decrypted_size = gnutls_record_recv(ssock->session,
-                                	((read_data_t *)app_read_data->data) +
-                                         app_read_data->len,
-                                         app_data_size);
-
-            if (decrypted_size > 0 || status != PJ_SUCCESS) {
-                if (ssock->param.cb.on_data_read) {
-                    pj_bool_t ret;
-                    app_remainder = 0;
-
-                    if (decrypted_size > 0)
-                        app_read_data->len += decrypted_size;
-
-                    ret = (*ssock->param.cb.on_data_read)(ssock,
-                                                          app_read_data->data,
-                                                          app_read_data->len,
-                                                          status,
-                                                          &app_remainder);
-
-                    if (!ret) {
-                        /* We've been destroyed */
-                        return PJ_FALSE;
-                    }
-
-                    /* Application may have left some data to be consumed
-                     * later as remainder */
-                    app_read_data->len = app_remainder;
-                }
-
-                /* Active socket signalled connection closed/error, this has
-                 * been signalled to the application along with any remaining
-                 * buffer. So, let's just reset SSL socket now.  */
-                if (status != PJ_SUCCESS) {
-                    tls_sock_reset(ssock);
-                    return PJ_FALSE;
-                }
-            } else if (decrypted_size == 0) {
-                /* Nothing more to read */
-
-                return PJ_TRUE;
-            } else if (decrypted_size == GNUTLS_E_AGAIN ||
-                       decrypted_size == GNUTLS_E_INTERRUPTED) {
-                return PJ_TRUE;
-            } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) {
-                /* Seems like we are renegotiating */
-                pj_status_t try_handshake_status = tls_try_handshake(ssock);
-
-                /* Not pending is either success or failed */
-                if (try_handshake_status != PJ_EPENDING) {
-                    if (!on_handshake_complete(ssock, try_handshake_status)) {
-                        return PJ_FALSE;
-                    }
-                }
-
-                if (try_handshake_status != PJ_SUCCESS &&
-                    try_handshake_status != PJ_EPENDING) {
-                    return PJ_FALSE;
-                }
-            } else if (!gnutls_error_is_fatal(decrypted_size)) {
-                /* non-fatal error, let's just continue */
-            } else {
-                return PJ_FALSE;
-            }
-        } while (PJ_TRUE);
-    }
-
-    return PJ_TRUE;
-}
-
-
-/* Callback every time new data is available from the active socket */
-static pj_bool_t asock_on_data_sent(pj_activesock_t *asock,
-                                    pj_ioqueue_op_key_t *send_key,
-                                    pj_ssize_t sent)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t *)pj_activesock_get_user_data(asock);
-
-    PJ_UNUSED_ARG(send_key);
-    PJ_UNUSED_ARG(sent);
-
-    if (ssock->connection_state == TLS_STATE_HANDSHAKING) {
-        /* Initial handshaking */
-        pj_status_t status = tls_try_handshake(ssock);
-
-        /* Not pending is either success or failed */
-        if (status != PJ_EPENDING)
-            return on_handshake_complete(ssock, status);
-
-    } else if (send_key != &ssock->handshake_op_key) {
-        /* Some data has been sent, notify application */
-        write_data_t *wdata = (write_data_t*)send_key->user_data;
-        if (ssock->param.cb.on_data_sent) {
-            pj_bool_t ret;
-            pj_ssize_t sent_len;
-
-            sent_len = sent > 0 ? wdata->plain_data_len : sent;
-
-            ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key,
-                                                  sent_len);
-            if (!ret) {
-                /* We've been destroyed */
-                return PJ_FALSE;
-            }
-        }
-
-        /* Update write buffer state */
-        pj_lock_acquire(ssock->circ_buf_output_mutex);
-        free_send_data(ssock, wdata);
-        pj_lock_release(ssock->circ_buf_output_mutex);
+static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
+    int decrypted_size;
+
+    /* Decrypt received data using GnuTLS (will read our input
+     * circular buffer) */
+    decrypted_size = gnutls_record_recv(gssock->session, data, *size);
+    *size = 0;
+    if (decrypted_size > 0) {
+        *size = decrypted_size;
+        return PJ_SUCCESS;
+    } else if (decrypted_size == 0) {
+        /* Nothing more to read */
+        return PJ_SUCCESS;
+    } else if (decrypted_size == GNUTLS_E_REHANDSHAKE) {
+    	return PJ_EEOF;
+    } else if (decrypted_size == GNUTLS_E_AGAIN ||
+               decrypted_size == GNUTLS_E_INTERRUPTED ||
+               !gnutls_error_is_fatal(decrypted_size))
+    {
+    	/* non-fatal error, let's just continue */
+        return PJ_SUCCESS;
     } else {
-        /* SSL re-negotiation is on-progress, just do nothing */
-        /* FIXME: check if this is valid for GnuTLS too */
-    }
-
-    return PJ_TRUE;
-}
-
-
-/* Callback every time a new connection has been accepted (server) */
-static pj_bool_t asock_on_accept_complete(pj_activesock_t *asock,
-                                          pj_sock_t newsock,
-                                          const pj_sockaddr_t *src_addr,
-                                          int src_addr_len)
-{
-    pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t *)
-                                  pj_activesock_get_user_data(asock);
-
-    pj_ssl_sock_t *ssock;
-    pj_activesock_cb asock_cb;
-    pj_activesock_cfg asock_cfg;
-    unsigned int i;
-    pj_status_t status;
-
-    PJ_UNUSED_ARG(src_addr_len);
-
-    /* Create new SSL socket instance */
-    status = pj_ssl_sock_create(ssock_parent->pool,
-    				&ssock_parent->newsock_param,
-                                &ssock);
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Update new SSL socket attributes */
-    ssock->sock = newsock;
-    ssock->parent = ssock_parent;
-    ssock->is_server = PJ_TRUE;
-    if (ssock_parent->cert) {
-        status = pj_ssl_sock_set_certificate(ssock, ssock->pool,
-                                             ssock_parent->cert);
-        if (status != PJ_SUCCESS)
-            goto on_return;
-    }
-
-    /* Apply QoS, if specified */
-    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
-                                &ssock->param.qos_params, 1,
-                                ssock->pool->obj_name, NULL);
-    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
-        goto on_return;
-
-    /* Update local address */
-    ssock->addr_len = src_addr_len;
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
-                                 &ssock->addr_len);
-    if (status != PJ_SUCCESS) {
-        /* This fails on few envs, e.g: win IOCP, just tolerate this and
-         * use parent local address instead.
-         */
-        pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr);
-    }
-
-    /* Set remote address */
-    pj_sockaddr_cp(&ssock->rem_addr, src_addr);
-
-    /* Create SSL context */
-    status = tls_open(ssock);
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Prepare read buffer */
-    ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool,
-                                                ssock->param.async_cnt,
-                                                sizeof(void*));
-    if (!ssock->asock_rbuf)
-        return PJ_ENOMEM;
-
-    for (i = 0; i < ssock->param.async_cnt; ++i) {
-        ssock->asock_rbuf[i] = (void *)pj_pool_alloc(
-                                            ssock->pool,
-                                            ssock->param.read_buffer_size +
-                                            sizeof(read_data_t*));
-        if (!ssock->asock_rbuf[i])
-            return PJ_ENOMEM;
-    }
-
-    /* Create active socket */
-    pj_activesock_cfg_default(&asock_cfg);
-    asock_cfg.async_cnt = ssock->param.async_cnt;
-    asock_cfg.concurrency = ssock->param.concurrency;
-    asock_cfg.whole_data = PJ_TRUE;
-
-    pj_bzero(&asock_cb, sizeof(asock_cb));
-    asock_cb.on_data_read = asock_on_data_read;
-    asock_cb.on_data_sent = asock_on_data_sent;
-
-    status = pj_activesock_create(ssock->pool,
-                                  ssock->sock,
-                                  ssock->param.sock_type,
-                                  &asock_cfg,
-                                  ssock->param.ioqueue,
-                                  &asock_cb,
-                                  ssock,
-                                  &ssock->asock);
-
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Start reading */
-    status = pj_activesock_start_read2(ssock->asock, ssock->pool,
-                                       (unsigned)ssock->param.read_buffer_size,
-                                       ssock->asock_rbuf,
-                                       PJ_IOQUEUE_ALWAYS_ASYNC);
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Prepare write/send state */
-    pj_assert(ssock->send_buf.max_len == 0);
-    ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool,
-                                                ssock->param.send_buffer_size);
-    if (!ssock->send_buf.buf)
-        return PJ_ENOMEM;
-
-    ssock->send_buf.max_len = ssock->param.send_buffer_size;
-    ssock->send_buf.start = ssock->send_buf.buf;
-    ssock->send_buf.len = 0;
-
-    /* Start handshake timer */
-    if (ssock->param.timer_heap &&
-        (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0)) {
-        pj_assert(ssock->timer.id == TIMER_NONE);
-        ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
-        status = pj_timer_heap_schedule(ssock->param.timer_heap,
-                                        &ssock->timer,
-                                        &ssock->param.timeout);
-        if (status != PJ_SUCCESS)
-            ssock->timer.id = TIMER_NONE;
-    }
-
-    /* Start SSL handshake */
-    ssock->connection_state = TLS_STATE_HANDSHAKING;
-
-    status = tls_try_handshake(ssock);
-
-on_return:
-    if (ssock && status != PJ_EPENDING)
-        on_handshake_complete(ssock, status);
-
-    /* Must return PJ_TRUE whatever happened, as active socket must
-     * continue listening.
-     */
-    return PJ_TRUE;
-}
-
-
-/* Callback every time a new connection has been completed (client) */
-static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
-                                            pj_status_t status)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
-                           pj_activesock_get_user_data(asock);
-
-    unsigned int i;
-    int ret;
-
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Update local address */
-    ssock->addr_len = sizeof(pj_sockaddr);
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
-                                 &ssock->addr_len);
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Create SSL context */
-    status = tls_open(ssock);
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Prepare read buffer */
-    ssock->asock_rbuf = (void **)pj_pool_calloc(ssock->pool,
-                                                ssock->param.async_cnt,
-                                                sizeof(void *));
-    if (!ssock->asock_rbuf)
-        return PJ_ENOMEM;
-
-    for (i = 0; i < ssock->param.async_cnt; ++i) {
-        ssock->asock_rbuf[i] = (void *)pj_pool_alloc(
-                                            ssock->pool,
-                                            ssock->param.read_buffer_size +
-                                            sizeof(read_data_t *));
-        if (!ssock->asock_rbuf[i])
-            return PJ_ENOMEM;
-    }
-
-    /* Start read */
-    status = pj_activesock_start_read2(ssock->asock, ssock->pool,
-                                       (unsigned)ssock->param.read_buffer_size,
-                                       ssock->asock_rbuf,
-                                       PJ_IOQUEUE_ALWAYS_ASYNC);
-    if (status != PJ_SUCCESS)
-        goto on_return;
-
-    /* Prepare write/send state */
-    pj_assert(ssock->send_buf.max_len == 0);
-    ssock->send_buf.buf = (char *)pj_pool_alloc(ssock->pool,
-                                                ssock->param.send_buffer_size);
-    if (!ssock->send_buf.buf)
-        return PJ_ENOMEM;
-
-    ssock->send_buf.max_len = ssock->param.send_buffer_size;
-    ssock->send_buf.start = ssock->send_buf.buf;
-    ssock->send_buf.len = 0;
-
-    /* Set server name to connect */
-    if (ssock->param.server_name.slen) {
-        /* Server name is null terminated already */
-        ret = gnutls_server_name_set(ssock->session, GNUTLS_NAME_DNS,
-                                     ssock->param.server_name.ptr,
-                                     ssock->param.server_name.slen);
-        if (ret < 0) {
-            PJ_LOG(3, (ssock->pool->obj_name,
-                       "gnutls_server_name_set() failed: %s",
-                       gnutls_strerror(ret)));
-        }
-    }
-
-    /* Start handshake */
-    ssock->connection_state = TLS_STATE_HANDSHAKING;
-
-    status = tls_try_handshake(ssock);
-    if (status != PJ_EPENDING)
-        goto on_return;
-
-    return PJ_TRUE;
-
-on_return:
-    return on_handshake_complete(ssock, status);
-}
-
-static void tls_ciphers_fill(void)
-{
-     if (!tls_available_ciphers) {
-         tls_init();
-         tls_deinit();
-     }
-}
-
-/*
- *******************************************************************
- * API
- *******************************************************************
- */
-
-/* Load credentials from files. */
-PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
-                                                const pj_str_t *CA_file,
-                                                const pj_str_t *cert_file,
-                                                const pj_str_t *privkey_file,
-                                                const pj_str_t *privkey_pass,
-                                                pj_ssl_cert_t **p_cert)
-{
-    return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file,
-                    privkey_file, privkey_pass, p_cert);
-}
-
-/* Load credentials from files. */
-PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(
-                        pj_pool_t *pool,
-                        const pj_str_t *CA_file,
-                        const pj_str_t *CA_path,
-                        const pj_str_t *cert_file,
-                        const pj_str_t *privkey_file,
-                        const pj_str_t *privkey_pass,
-                        pj_ssl_cert_t **p_cert)
-{
-    pj_ssl_cert_t *cert;
-
-    PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file &&
-             privkey_file,
-             PJ_EINVAL);
-
-    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
-    if (CA_file) {
-        pj_strdup_with_null(pool, &cert->CA_file, CA_file);
-    }
-    if (CA_path) {
-        pj_strdup_with_null(pool, &cert->CA_path, CA_path);
-    }
-    pj_strdup_with_null(pool, &cert->cert_file, cert_file);
-    pj_strdup_with_null(pool, &cert->privkey_file, privkey_file);
-    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
-
-    *p_cert = cert;
-
-    return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
-					const pj_ssl_cert_buffer *CA_buf,
-					const pj_ssl_cert_buffer *cert_buf,
-					const pj_ssl_cert_buffer *privkey_buf,
-					const pj_str_t *privkey_pass,
-					pj_ssl_cert_t **p_cert)
-{
-    pj_ssl_cert_t *cert;
-
-    PJ_ASSERT_RETURN(pool && CA_buf && cert_buf && privkey_buf, PJ_EINVAL);
-
-    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
-    pj_strdup(pool, &cert->CA_buf, CA_buf);
-    pj_strdup(pool, &cert->cert_buf, cert_buf);
-    pj_strdup(pool, &cert->privkey_buf, privkey_buf);
-    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
-
-    *p_cert = cert;
-
-    return PJ_SUCCESS;
-}
-
-/* Store credentials. */
-PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate( pj_ssl_sock_t *ssock,
-                                                 pj_pool_t *pool,
-                                                 const pj_ssl_cert_t *cert)
-{
-    pj_ssl_cert_t *cert_;
-
-    PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL);
-
-    cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
-    pj_memcpy(cert_, cert, sizeof(cert));
-    pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file);
-    pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path);
-    pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file);
-    pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file);
-    pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass);
-
-    pj_strdup(pool, &cert_->CA_buf, &cert->CA_buf);
-    pj_strdup(pool, &cert_->cert_buf, &cert->cert_buf);
-    pj_strdup(pool, &cert_->privkey_buf, &cert->privkey_buf);
-
-    ssock->cert = cert_;
-
-    return PJ_SUCCESS;
-}
-
-
-/* Get available ciphers. */
-PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
-                                                 unsigned *cipher_num)
-{
-    unsigned int i;
-
-    PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
-
-    tls_ciphers_fill();
-
-    if (!tls_available_ciphers) {
-        *cipher_num = 0;
-        return PJ_ENOTFOUND;
-    }
-
-    *cipher_num = PJ_MIN(*cipher_num, tls_available_ciphers);
-
-    for (i = 0; i < *cipher_num; ++i)
-        ciphers[i] = tls_ciphers[i].id;
-
-    return PJ_SUCCESS;
-}
-
-
-/* Get cipher name string. */
-PJ_DEF(const char *)pj_ssl_cipher_name(pj_ssl_cipher cipher)
-{
-    unsigned int i;
-
-    tls_ciphers_fill();
-
-    for (i = 0; i < tls_available_ciphers; ++i) {
-        if (cipher == tls_ciphers[i].id)
-            return tls_ciphers[i].name;
-    }
-
-    return NULL;
-}
-
-
-/* Get cipher identifier. */
-PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name)
-{
-    unsigned int i;
-
-    tls_ciphers_fill();
-
-    for (i = 0; i < tls_available_ciphers; ++i) {
-        if (!pj_ansi_stricmp(tls_ciphers[i].name, cipher_name))
-            return tls_ciphers[i].id;
-    }
-
-    return PJ_TLS_UNKNOWN_CIPHER;
-}
-
-
-/* Check if the specified cipher is supported by the TLS backend. */
-PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher)
-{
-    unsigned int i;
-
-    tls_ciphers_fill();
-
-    for (i = 0; i < tls_available_ciphers; ++i) {
-        if (cipher == tls_ciphers[i].id)
-            return PJ_TRUE;
-    }
-
-    return PJ_FALSE;
-}
-
-/* Create SSL socket instance. */
-PJ_DEF(pj_status_t) pj_ssl_sock_create(pj_pool_t *pool,
-                                       const pj_ssl_sock_param *param,
-                                       pj_ssl_sock_t **p_ssock)
-{
-    pj_ssl_sock_t *ssock;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
-    PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP);
-
-    pool = pj_pool_create(pool->factory, "tls%p", 512, 512, NULL);
-
-    /* Create secure socket */
-    ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
-    ssock->pool = pool;
-    ssock->sock = PJ_INVALID_SOCKET;
-    ssock->connection_state = TLS_STATE_NULL;
-    pj_list_init(&ssock->write_pending);
-    pj_list_init(&ssock->write_pending_empty);
-    pj_list_init(&ssock->send_pending);
-    pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer);
-    pj_ioqueue_op_key_init(&ssock->handshake_op_key,
-                           sizeof(pj_ioqueue_op_key_t));
-
-    /* Create secure socket mutex */
-    status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
-                                            &ssock->circ_buf_output_mutex);
-    if (status != PJ_SUCCESS)
-        return status;
-
-    /* Create input circular buffer mutex */
-    status = pj_lock_create_simple_mutex(pool, pool->obj_name,
-                                         &ssock->circ_buf_input_mutex);
-    if (status != PJ_SUCCESS)
-        return status;
-
-    /* Create output circular buffer mutex */
-    status = pj_lock_create_simple_mutex(pool, pool->obj_name,
-                                         &ssock->circ_buf_output_mutex);
-    if (status != PJ_SUCCESS)
-        return status;
-
-    /* Init secure socket param */
-    ssock->param = *param;
-    ssock->param.read_buffer_size = ((ssock->param.read_buffer_size + 7) >> 3)
-    				     << 3;
-
-    if (param->ciphers_num > 0) {
-        unsigned int i;
-        ssock->param.ciphers = (pj_ssl_cipher *)
-                               pj_pool_calloc(pool, param->ciphers_num,
-                                              sizeof(pj_ssl_cipher));
-        if (!ssock->param.ciphers)
-            return PJ_ENOMEM;
-
-        for (i = 0; i < param->ciphers_num; ++i)
-            ssock->param.ciphers[i] = param->ciphers[i];
-    }
-
-    /* Server name must be null-terminated */
-    pj_strdup_with_null(pool, &ssock->param.server_name, &param->server_name);
-
-    /* Finally */
-    *p_ssock = ssock;
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Close the secure socket. This will unregister the socket from the
- * ioqueue and ultimately close the socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
-{
-    pj_pool_t *pool;
-
-    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
-
-    if (!ssock->pool)
-        return PJ_SUCCESS;
-
-    if (ssock->timer.id != TIMER_NONE) {
-        pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
-        ssock->timer.id = TIMER_NONE;
-    }
-
-    tls_sock_reset(ssock);
-
-    pj_lock_destroy(ssock->circ_buf_output_mutex);
-    pj_lock_destroy(ssock->circ_buf_input_mutex);
-
-    pool = ssock->pool;
-    ssock->pool = NULL;
-    if (pool)
-        pj_pool_release(pool);
-
-    return PJ_SUCCESS;
-}
-
-
-/* Associate arbitrary data with the secure socket. */
-PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
-                                              void *user_data)
-{
-    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
-
-    ssock->param.user_data = user_data;
-    return PJ_SUCCESS;
-}
-
-
-/* Retrieve the user data previously associated with this secure socket. */
-PJ_DEF(void *)pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
-{
-    PJ_ASSERT_RETURN(ssock, NULL);
-
-    return ssock->param.user_data;
-}
-
-
-/* Retrieve the local address and port used by specified SSL socket. */
-PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
-                                          pj_ssl_sock_info *info)
-{
-    pj_bzero(info, sizeof(*info));
-
-    /* Established flag */
-    info->established = (ssock->connection_state == TLS_STATE_ESTABLISHED);
-
-    /* Protocol */
-    info->proto = ssock->param.proto;
-
-    /* Local address */
-    pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
-
-    if (info->established) {
-        int i;
-        gnutls_cipher_algorithm_t lookup;
-        gnutls_cipher_algorithm_t cipher;
-
-        /* Current cipher */
-        cipher = gnutls_cipher_get(ssock->session);
-        for (i = 0; ; i++) {
-            unsigned char id[2];
-            const char *suite = gnutls_cipher_suite_info(i,(unsigned char *)id,
-                                                         NULL, &lookup, NULL,
-                                                         NULL);
-            if (suite) {
-                if (lookup == cipher) {
-                    info->cipher = (pj_uint32_t) ((id[0] << 8) | id[1]);
-                    break;
-                }
-            } else
-                break;
-        }
-
-        /* Remote address */
-        pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
-
-        /* Certificates info */
-        info->local_cert_info = &ssock->local_cert_info;
-        info->remote_cert_info = &ssock->remote_cert_info;
-
-        /* Verification status */
-        info->verify_status = ssock->verify_status;
-    }
-
-    /* Last known GnuTLS error code */
-    info->last_native_err = ssock->last_err;
-
-    return PJ_SUCCESS;
-}
-
-
-/* Starts read operation on this secure socket. */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_read(pj_ssl_sock_t *ssock,
-                                           pj_pool_t *pool,
-                                           unsigned buff_size,
-                                           pj_uint32_t flags)
-{
-    void **readbuf;
-    unsigned int i;
-
-    PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
-    PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
-                     PJ_EINVALIDOP);
-
-    readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt,
-                                      sizeof(void *));
-    if (!readbuf)
-        return PJ_ENOMEM;
-
-    for (i = 0; i < ssock->param.async_cnt; ++i) {
-        readbuf[i] = pj_pool_alloc(pool, buff_size);
-        if (!readbuf[i])
-            return PJ_ENOMEM;
-    }
-
-    return pj_ssl_sock_start_read2(ssock, pool, buff_size, readbuf, flags);
-}
-
-
-/*
- * Same as #pj_ssl_sock_start_read(), except that the application
- * supplies the buffers for the read operation so that the acive socket
- * does not have to allocate the buffers.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
-                                             pj_pool_t *pool,
-                                             unsigned buff_size,
-                                             void *readbuf[],
-                                             pj_uint32_t flags)
-{
-    unsigned int i;
-
-    PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL);
-    PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
-                     PJ_EINVALIDOP);
-
-    /* Create SSL socket read buffer */
-    ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool,
-                                                     ssock->param.async_cnt,
-                                                     sizeof(read_data_t));
-    if (!ssock->ssock_rbuf)
-        return PJ_ENOMEM;
-
-    /* Store SSL socket read buffer pointer in the activesock read buffer */
-    for (i = 0; i < ssock->param.async_cnt; ++i) {
-        read_data_t **p_ssock_rbuf =
-                        OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]);
-
-        ssock->ssock_rbuf[i].data = readbuf[i];
-        ssock->ssock_rbuf[i].len = 0;
-
-        *p_ssock_rbuf = &ssock->ssock_rbuf[i];
-    }
-
-    ssock->read_size = buff_size;
-    ssock->read_started = PJ_TRUE;
-    ssock->read_flags = flags;
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Same as pj_ssl_sock_start_read(), except that this function is used
- * only for datagram sockets, and it will trigger \a on_data_recvfrom()
- * callback instead.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
-                                                pj_pool_t *pool,
-                                                unsigned buff_size,
-                                                pj_uint32_t flags)
-{
-    PJ_UNUSED_ARG(ssock);
-    PJ_UNUSED_ARG(pool);
-    PJ_UNUSED_ARG(buff_size);
-    PJ_UNUSED_ARG(flags);
-
-    return PJ_ENOTSUP;
-}
-
-
-/*
- * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom()
- * operation takes the buffer from the argument rather than creating
- * new ones.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
-                                                 pj_pool_t *pool,
-                                                 unsigned buff_size,
-                                                 void *readbuf[],
-                                                 pj_uint32_t flags)
-{
-    PJ_UNUSED_ARG(ssock);
-    PJ_UNUSED_ARG(pool);
-    PJ_UNUSED_ARG(buff_size);
-    PJ_UNUSED_ARG(readbuf);
-    PJ_UNUSED_ARG(flags);
-
-    return PJ_ENOTSUP;
-}
-
+        return PJ_ECANCELLED;
+    }
+}
 
 /*
@@ -2556,10 +1178,9 @@
  * sending data should be delayed until re-negotiation is completed.
  */
-static pj_status_t tls_write(pj_ssl_sock_t *ssock,
-                             pj_ioqueue_op_key_t *send_key,
-                             const void *data, pj_ssize_t size, unsigned flags)
-{
-    pj_status_t status;
-    int nwritten;
+static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data,
+			     pj_ssize_t size, int *nwritten)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
+    int nwritten_;
     pj_ssize_t total_written = 0;
 
@@ -2571,11 +1192,11 @@
     while (total_written < size) {
         /* Try encrypting using GnuTLS */
-        nwritten = gnutls_record_send(ssock->session,
+        nwritten_ = gnutls_record_send(gssock->session,
         			      ((read_data_t *)data) + total_written,
-                                      size);
-
-        if (nwritten > 0) {
+                                      size - total_written);
+
+        if (nwritten_ > 0) {
             /* Good, some data was encrypted and written */
-            total_written += nwritten;
+            total_written += nwritten_;
         } else {
             /* Normally we would have to retry record_send but our internal
@@ -2583,5 +1204,6 @@
              * We will just try again later, although this should never happen.
              */
-            return tls_status_from_err(ssock, nwritten);
+            *nwritten = nwritten_;
+            return tls_status_from_err(ssock, nwritten_);
         }
     }
@@ -2589,385 +1211,15 @@
     /* All encrypted data is written to the output circular buffer;
      * now send it on the socket (or notify problem). */
-    if (total_written == size)
-        status = flush_circ_buf_output(ssock, send_key, size, flags);
-    else
-        status = PJ_ENOMEM;
-
-    return status;
-}
-
-
-/* Flush delayed data sending in the write pending list. */
-static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
-{
-    /* Check for another ongoing flush */
-    if (ssock->flushing_write_pend) {
-        return PJ_EBUSY;
-    }
-
-    pj_lock_acquire(ssock->circ_buf_output_mutex);
-
-    /* Again, check for another ongoing flush */
-    if (ssock->flushing_write_pend) {
-        pj_lock_release(ssock->circ_buf_output_mutex);
-        return PJ_EBUSY;
-    }
-
-    /* Set ongoing flush flag */
-    ssock->flushing_write_pend = PJ_TRUE;
-
-    while (!pj_list_empty(&ssock->write_pending)) {
-        write_data_t *wp;
-        pj_status_t status;
-
-        wp = ssock->write_pending.next;
-
-        /* Ticket #1573: Don't hold mutex while calling socket send. */
-        pj_lock_release(ssock->circ_buf_output_mutex);
-
-        status = tls_write(ssock, &wp->key, wp->data.ptr,
-                           wp->plain_data_len, wp->flags);
-        if (status != PJ_SUCCESS) {
-            /* Reset ongoing flush flag first. */
-            ssock->flushing_write_pend = PJ_FALSE;
-            return status;
-        }
-
-        pj_lock_acquire(ssock->circ_buf_output_mutex);
-        pj_list_erase(wp);
-        pj_list_push_back(&ssock->write_pending_empty, wp);
-    }
-
-    /* Reset ongoing flush flag */
-    ssock->flushing_write_pend = PJ_FALSE;
-
-    pj_lock_release(ssock->circ_buf_output_mutex);
-
+    *nwritten = total_written;
     return PJ_SUCCESS;
 }
 
-
-/* Sending is delayed, push back the sending data into pending list. */
-static pj_status_t delay_send(pj_ssl_sock_t *ssock,
-                              pj_ioqueue_op_key_t *send_key,
-                              const void *data, pj_ssize_t size,
-                              unsigned flags)
-{
-    write_data_t *wp;
-
-    pj_lock_acquire(ssock->circ_buf_output_mutex);
-
-    /* Init write pending instance */
-    if (!pj_list_empty(&ssock->write_pending_empty)) {
-        wp = ssock->write_pending_empty.next;
-        pj_list_erase(wp);
-    } else {
-        wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t);
-    }
-
-    wp->app_key = send_key;
-    wp->plain_data_len = size;
-    wp->data.ptr = data;
-    wp->flags = flags;
-
-    pj_list_push_back(&ssock->write_pending, wp);
-
-    pj_lock_release(ssock->circ_buf_output_mutex);
-
-    /* Must return PJ_EPENDING */
-    return PJ_EPENDING;
-}
-
-
-/**
- * Send data using the socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_send(pj_ssl_sock_t *ssock,
-                                     pj_ioqueue_op_key_t *send_key,
-                                     const void *data, pj_ssize_t *size,
-                                     unsigned flags)
-{
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ssock && data && size && (*size > 0), PJ_EINVAL);
-    PJ_ASSERT_RETURN(ssock->connection_state==TLS_STATE_ESTABLISHED,
-                     PJ_EINVALIDOP);
-
-    /* Flush delayed send first. Sending data might be delayed when
-     * re-negotiation is on-progress. */
-    status = flush_delayed_send(ssock);
-    if (status == PJ_EBUSY) {
-        /* Re-negotiation or flushing is on progress, delay sending */
-        status = delay_send(ssock, send_key, data, *size, flags);
-        goto on_return;
-    } else if (status != PJ_SUCCESS) {
-        goto on_return;
-    }
-
-    /* Write data to SSL */
-    status = tls_write(ssock, send_key, data, *size, flags);
-    if (status == PJ_EBUSY) {
-        /* Re-negotiation is on progress, delay sending */
-        status = delay_send(ssock, send_key, data, *size, flags);
-    }
-
-on_return:
-    return status;
-}
-
-
-/**
- * Send datagram using the socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
-                                        pj_ioqueue_op_key_t *send_key,
-                                        const void *data, pj_ssize_t *size,
-                                        unsigned flags,
-                                        const pj_sockaddr_t *addr, int addr_len)
-{
-    PJ_UNUSED_ARG(ssock);
-    PJ_UNUSED_ARG(send_key);
-    PJ_UNUSED_ARG(data);
-    PJ_UNUSED_ARG(size);
-    PJ_UNUSED_ARG(flags);
-    PJ_UNUSED_ARG(addr);
-    PJ_UNUSED_ARG(addr_len);
-
-    return PJ_ENOTSUP;
-}
-
-/**
- * Starts asynchronous socket accept() operations on this secure socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
-                          pj_pool_t *pool,
-                          const pj_sockaddr_t *localaddr,
-                          int addr_len)
-{
-    return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len,
-                         &ssock->param);
-}
-
-/**
- * Starts asynchronous socket accept() operations on this secure socket.
- */
-PJ_DEF(pj_status_t)
-pj_ssl_sock_start_accept2 (pj_ssl_sock_t *ssock,
-                           pj_pool_t *pool,
-                           const pj_sockaddr_t *localaddr,
-                           int addr_len,
-                           const pj_ssl_sock_param *newsock_param)
-{
-    pj_activesock_cb asock_cb;
-    pj_activesock_cfg asock_cfg;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
-
-    /* Verify new socket parameters */
-    if (newsock_param->grp_lock != ssock->param.grp_lock ||
-        newsock_param->sock_af != ssock->param.sock_af ||
-        newsock_param->sock_type != ssock->param.sock_type)
-    {
-        return PJ_EINVAL;
-    }
-
-    /* Create socket */
-    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
-                            &ssock->sock);
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Apply SO_REUSEADDR */
-    if (ssock->param.reuse_addr) {
-        int enabled = 1;
-        status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(),
-                                    pj_SO_REUSEADDR(),
-                                    &enabled, sizeof(enabled));
-        if (status != PJ_SUCCESS) {
-            PJ_PERROR(4,(ssock->pool->obj_name, status,
-                         "Warning: error applying SO_REUSEADDR"));
-        }
-    }
-
-    /* Apply QoS, if specified */
-    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
-                                &ssock->param.qos_params, 2,
-                                ssock->pool->obj_name, NULL);
-    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
-        goto on_error;
-
-    /* Bind socket */
-    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Start listening to the address */
-    status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN);
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Create active socket */
-    pj_activesock_cfg_default(&asock_cfg);
-    asock_cfg.async_cnt = ssock->param.async_cnt;
-    asock_cfg.concurrency = ssock->param.concurrency;
-    asock_cfg.whole_data = PJ_TRUE;
-
-    pj_bzero(&asock_cb, sizeof(asock_cb));
-    asock_cb.on_accept_complete = asock_on_accept_complete;
-
-    status = pj_activesock_create(pool,
-                                  ssock->sock,
-                                  ssock->param.sock_type,
-                                  &asock_cfg,
-                                  ssock->param.ioqueue,
-                                  &asock_cb,
-                                  ssock,
-                                  &ssock->asock);
-
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Start accepting */
-    pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param);
-    status = pj_activesock_start_accept(ssock->asock, pool);
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Update local address */
-    ssock->addr_len = addr_len;
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
-                                 &ssock->addr_len);
-    if (status != PJ_SUCCESS)
-        pj_sockaddr_cp(&ssock->local_addr, localaddr);
-
-    ssock->is_server = PJ_TRUE;
-
-    return PJ_SUCCESS;
-
-on_error:
-    tls_sock_reset(ssock);
-    return status;
-}
-
-
-/**
- * Starts asynchronous socket connect() operation.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_connect( pj_ssl_sock_t *ssock,
-                                               pj_pool_t *pool,
-                                               const pj_sockaddr_t *localaddr,
-                                               const pj_sockaddr_t *remaddr,
-                                               int addr_len)
-{
-    pj_activesock_cb asock_cb;
-    pj_activesock_cfg asock_cfg;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
-                     PJ_EINVAL);
-
-    /* Create socket */
-    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0,
-                            &ssock->sock);
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Apply QoS, if specified */
-    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
-                                &ssock->param.qos_params, 2,
-                                ssock->pool->obj_name, NULL);
-    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
-        goto on_error;
-
-    /* Bind socket */
-    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Create active socket */
-    pj_activesock_cfg_default(&asock_cfg);
-    asock_cfg.async_cnt = ssock->param.async_cnt;
-    asock_cfg.concurrency = ssock->param.concurrency;
-    asock_cfg.whole_data = PJ_TRUE;
-
-    pj_bzero(&asock_cb, sizeof(asock_cb));
-    asock_cb.on_connect_complete = asock_on_connect_complete;
-    asock_cb.on_data_read = asock_on_data_read;
-    asock_cb.on_data_sent = asock_on_data_sent;
-
-    status = pj_activesock_create(pool,
-                                  ssock->sock,
-                                  ssock->param.sock_type,
-                                  &asock_cfg,
-                                  ssock->param.ioqueue,
-                                  &asock_cb,
-                                  ssock,
-                                  &ssock->asock);
-
-    if (status != PJ_SUCCESS)
-        goto on_error;
-
-    /* Save remote address */
-    pj_sockaddr_cp(&ssock->rem_addr, remaddr);
-
-    /* Start timer */
-    if (ssock->param.timer_heap &&
-        (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0))
-    {
-        pj_assert(ssock->timer.id == TIMER_NONE);
-        ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
-        status = pj_timer_heap_schedule(ssock->param.timer_heap,
-                                        &ssock->timer,
-                                        &ssock->param.timeout);
-        if (status != PJ_SUCCESS)
-            ssock->timer.id = TIMER_NONE;
-    }
-
-    status = pj_activesock_start_connect(ssock->asock, pool, remaddr,
-                                         addr_len);
-
-    if (status == PJ_SUCCESS)
-        asock_on_connect_complete(ssock->asock, PJ_SUCCESS);
-    else if (status != PJ_EPENDING)
-        goto on_error;
-
-    /* Update local address */
-    ssock->addr_len = addr_len;
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
-                                 &ssock->addr_len);
-    /* Note that we may not get an IP address here. This can
-     * happen for example on Windows, where getsockname()
-     * would return 0.0.0.0 if socket has just started the
-     * async connect. In this case, just leave the local
-     * address with 0.0.0.0 for now; it will be updated
-     * once the socket is established.
-     */
-
-    /* Update socket state */
-    ssock->is_server = PJ_FALSE;
-
-    return PJ_EPENDING;
-
-on_error:
-    tls_sock_reset(ssock);
-    return status;
-}
-
-
-PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
-{
+static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock)
+{
+    gnutls_sock_t *gssock = (gnutls_sock_t *)ssock;
     int status;
 
-    /* Nothing established yet */
-    PJ_ASSERT_RETURN(ssock->connection_state == TLS_STATE_ESTABLISHED,
-                     PJ_EINVALIDOP);
-
-    /* Cannot renegotiate; we're a client */
-    /* FIXME: in fact maybe that's not true */
-    PJ_ASSERT_RETURN(!ssock->is_server, PJ_EINVALIDOP);
-
     /* First call gnutls_rehandshake() to see if this is even possible */
-    status = gnutls_rehandshake(ssock->session);
+    status = gnutls_rehandshake(gssock->session);
 
     if (status == GNUTLS_E_SUCCESS) {
@@ -2980,8 +1232,5 @@
          *                                      renegotiate
          */
-        ssock->connection_state = TLS_STATE_HANDSHAKING;
-        status = tls_try_handshake(ssock);
-
-        return status;
+        return PJ_SUCCESS;
     } else {
         return tls_status_from_err(ssock, status);
Index: /pjproject/trunk/pjlib/src/pj/ssl_sock_imp_common.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/ssl_sock_imp_common.c	(revision 5938)
+++ /pjproject/trunk/pjlib/src/pj/ssl_sock_imp_common.c	(revision 5938)
@@ -0,0 +1,2069 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2019-2019 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pj/ssl_sock.h>
+#include <pj/assert.h>
+#include <pj/errno.h>
+#include <pj/log.h>
+#include <pj/math.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+#include "ssl_sock_imp_common.h"
+
+/* Workaround for ticket #985 and #1930 */
+#ifndef PJ_SSL_SOCK_DELAYED_CLOSE_TIMEOUT
+#   define PJ_SSL_SOCK_DELAYED_CLOSE_TIMEOUT	500
+#endif
+
+#ifdef SSL_SOCK_IMP_USE_CIRC_BUF
+/*
+ *******************************************************************
+ * Circular buffer functions.
+ *******************************************************************
+ */
+
+static pj_status_t circ_init(pj_pool_factory *factory,
+                             circ_buf_t *cb, pj_size_t cap)
+{
+    cb->cap    = cap;
+    cb->readp  = 0;
+    cb->writep = 0;
+    cb->size   = 0;
+
+    /* Initial pool holding the buffer elements */
+    cb->pool = pj_pool_create(factory, "tls-circ%p", cap, cap, NULL);
+    if (!cb->pool)
+        return PJ_ENOMEM;
+
+    /* Allocate circular buffer */
+    cb->buf = pj_pool_alloc(cb->pool, cap);
+    if (!cb->buf) {
+        pj_pool_release(cb->pool);
+        return PJ_ENOMEM;
+    }
+
+    return PJ_SUCCESS;
+}
+
+static void circ_deinit(circ_buf_t *cb)
+{
+    if (cb->pool) {
+        pj_pool_release(cb->pool);
+        cb->pool = NULL;
+    }
+}
+
+static pj_bool_t circ_empty(const circ_buf_t *cb)
+{
+    return cb->size == 0;
+}
+
+static pj_size_t circ_size(const circ_buf_t *cb)
+{
+    return cb->size;
+}
+
+static pj_size_t circ_avail(const circ_buf_t *cb)
+{
+    return cb->cap - cb->size;
+}
+
+static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len)
+{
+    pj_size_t size_after = cb->cap - cb->readp;
+    pj_size_t tbc = PJ_MIN(size_after, len);
+    pj_size_t rem = len - tbc;
+
+    pj_memcpy(dst, cb->buf + cb->readp, tbc);
+    pj_memcpy(dst + tbc, cb->buf, rem);
+
+    cb->readp += len;
+    cb->readp &= (cb->cap - 1);
+
+    cb->size -= len;
+}
+
+static pj_status_t circ_write(circ_buf_t *cb,
+                              const pj_uint8_t *src, pj_size_t len)
+{
+    /* Overflow condition: resize */
+    if (len > circ_avail(cb)) {
+        /* Minimum required capacity */
+        pj_size_t min_cap = len + cb->size;
+
+        /* Next 32-bit power of two */
+        min_cap--;
+        min_cap |= min_cap >> 1;
+        min_cap |= min_cap >> 2;
+        min_cap |= min_cap >> 4;
+        min_cap |= min_cap >> 8;
+        min_cap |= min_cap >> 16;
+        min_cap++;
+
+        /* Create a new pool to hold a bigger buffer, using the same factory */
+        pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p",
+                                         min_cap, min_cap, NULL);
+        if (!pool)
+            return PJ_ENOMEM;
+
+        /* Allocate our new buffer */
+        pj_uint8_t *buf = pj_pool_alloc(pool, min_cap);
+        if (!buf) {
+            pj_pool_release(pool);
+            return PJ_ENOMEM;
+        }
+
+        /* Save old size, which we shall restore after the next read */
+        pj_size_t old_size = cb->size;
+
+        /* Copy old data into beginning of new buffer */
+        circ_read(cb, buf, cb->size);
+
+        /* Restore old size now */
+        cb->size = old_size;
+
+        /* Release the previous pool */
+        pj_pool_release(cb->pool);
+
+        /* Update circular buffer members */
+        cb->pool = pool;
+        cb->buf = buf;
+        cb->readp = 0;
+        cb->writep = cb->size;
+        cb->cap = min_cap;
+    }
+
+    pj_size_t size_after = cb->cap - cb->writep;
+    pj_size_t tbc = PJ_MIN(size_after, len);
+    pj_size_t rem = len - tbc;
+
+    pj_memcpy(cb->buf + cb->writep, src, tbc);
+    pj_memcpy(cb->buf, src + tbc, rem);
+
+    cb->writep += len;
+    cb->writep &= (cb->cap - 1);
+
+    cb->size += len;
+
+    return PJ_SUCCESS;
+}
+#endif
+
+/*
+ *******************************************************************
+ * Helper functions.
+ *******************************************************************
+ */
+
+/* Close sockets */
+static void ssl_close_sockets(pj_ssl_sock_t *ssock)
+{
+    pj_activesock_t *asock;
+    pj_sock_t sock;
+
+    /* This can happen when pj_ssl_sock_create() fails. */
+    if (!ssock->write_mutex)
+    	return;
+
+    pj_lock_acquire(ssock->write_mutex);
+    asock = ssock->asock;
+    if (asock) {
+        // Don't set ssock->asock to NULL, as it may trigger assertion in
+        // send operation. This should be safe as active socket will simply
+        // return PJ_EINVALIDOP on any operation if it is already closed.
+        //ssock->asock = NULL;
+        ssock->sock = PJ_INVALID_SOCKET;
+    }
+    sock = ssock->sock;
+    if (sock != PJ_INVALID_SOCKET)
+        ssock->sock = PJ_INVALID_SOCKET;
+    pj_lock_release(ssock->write_mutex);
+
+    if (asock)
+        pj_activesock_close(asock);
+
+    if (sock != PJ_INVALID_SOCKET)
+        pj_sock_close(sock);
+}
+
+/* When handshake completed:
+ * - notify application
+ * - if handshake failed, reset SSL state
+ * - return PJ_FALSE when SSL socket instance is destroyed by application.
+ */
+static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, 
+				       pj_status_t status)
+{
+    /* Cancel handshake timer */
+    if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) {
+	pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
+	ssock->timer.id = TIMER_NONE;
+    }
+
+    /* Update certificates info on successful handshake */
+    if (status == PJ_SUCCESS)
+	ssl_update_certs_info(ssock);
+
+    /* Accepting */
+    if (ssock->is_server) {
+	if (status != PJ_SUCCESS) {
+	    /* Handshake failed in accepting, destroy our self silently. */
+
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    char buf[PJ_INET6_ADDRSTRLEN+10];
+
+	    pj_strerror(status, errmsg, sizeof(errmsg));
+	    PJ_LOG(3,(ssock->pool->obj_name, "Handshake failed in accepting "
+		      "%s: %s",
+		      pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3),
+		      errmsg));
+
+	    if (ssock->param.cb.on_accept_complete2) {
+		(*ssock->param.cb.on_accept_complete2) 
+		      (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, 
+		      pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr), 
+		      status);
+	    }
+
+	    /* Originally, this is a workaround for ticket #985. However,
+	     * a race condition may occur in multiple worker threads
+	     * environment when we are destroying SSL objects while other
+	     * threads are still accessing them.
+	     * Please see ticket #1930 for more info.
+	     */
+#if 1 //(defined(PJ_WIN32) && PJ_WIN32!=0)||(defined(PJ_WIN64) && PJ_WIN64!=0)
+	    if (ssock->param.timer_heap) {
+		pj_time_val interval = {0, PJ_SSL_SOCK_DELAYED_CLOSE_TIMEOUT};
+
+		ssock->ssl_state = SSL_STATE_NULL;
+		ssl_close_sockets(ssock);
+
+		if (ssock->timer.id != TIMER_NONE) {
+		    pj_timer_heap_cancel(ssock->param.timer_heap,
+					 &ssock->timer);
+		}
+		ssock->timer.id = TIMER_CLOSE;
+		pj_time_val_normalize(&interval);
+		if (pj_timer_heap_schedule(ssock->param.timer_heap, 
+					   &ssock->timer, &interval) != 0)
+		{
+	    	    PJ_LOG(3,(ssock->pool->obj_name, "Failed to schedule "
+		      	      "a delayed close. Race condition may occur."));
+		    ssock->timer.id = TIMER_NONE;
+		    pj_ssl_sock_close(ssock);
+		}
+	    } else {
+		pj_ssl_sock_close(ssock);
+	    }
+#else
+	    {
+		pj_ssl_sock_close(ssock);
+	    }
+#endif
+
+	    return PJ_FALSE;
+	}
+	/* Notify application the newly accepted SSL socket */
+	if (ssock->param.cb.on_accept_complete2) {
+	    pj_bool_t ret;
+	    ret = (*ssock->param.cb.on_accept_complete2) 
+		    (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, 
+		    pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr), 
+		    status);
+	    if (ret == PJ_FALSE)
+		return PJ_FALSE;	
+	} else if (ssock->param.cb.on_accept_complete) {
+	    pj_bool_t ret;
+	    ret = (*ssock->param.cb.on_accept_complete)
+		      (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
+		       pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
+	    if (ret == PJ_FALSE)
+		return PJ_FALSE;
+	}
+    }
+
+    /* Connecting */
+    else {
+	/* On failure, reset SSL socket state first, as app may try to 
+	 * reconnect in the callback.
+	 */
+	if (status != PJ_SUCCESS) {
+	    /* Server disconnected us, possibly due to SSL nego failure */
+	    ssl_reset_sock_state(ssock);
+	}
+	if (ssock->param.cb.on_connect_complete) {
+	    pj_bool_t ret;
+	    ret = (*ssock->param.cb.on_connect_complete)(ssock, status);
+	    if (ret == PJ_FALSE)
+		return PJ_FALSE;
+	}
+    }
+
+    return PJ_TRUE;
+}
+
+static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len)
+{
+    send_buf_t *send_buf = &ssock->send_buf;
+    pj_size_t avail_len, skipped_len = 0;
+    char *reg1, *reg2;
+    pj_size_t reg1_len, reg2_len;
+    write_data_t *p;
+
+    /* Check buffer availability */
+    avail_len = send_buf->max_len - send_buf->len;
+    if (avail_len < len)
+	return NULL;
+
+    /* If buffer empty, reset start pointer and return it */
+    if (send_buf->len == 0) {
+	send_buf->start = send_buf->buf;
+	send_buf->len   = len;
+	p = (write_data_t*)send_buf->start;
+	goto init_send_data;
+    }
+
+    /* Free space may be wrapped/splitted into two regions, so let's
+     * analyze them if any region can hold the write data.
+     */
+    reg1 = send_buf->start + send_buf->len;
+    if (reg1 >= send_buf->buf + send_buf->max_len)
+	reg1 -= send_buf->max_len;
+    reg1_len = send_buf->max_len - send_buf->len;
+    if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) {
+	reg1_len = send_buf->buf + send_buf->max_len - reg1;
+	reg2 = send_buf->buf;
+	reg2_len = send_buf->start - send_buf->buf;
+    } else {
+	reg2 = NULL;
+	reg2_len = 0;
+    }
+
+    /* More buffer availability check, note that the write data must be in
+     * a contigue buffer.
+     */
+    avail_len = PJ_MAX(reg1_len, reg2_len);
+    if (avail_len < len)
+	return NULL;
+
+    /* Get the data slot */
+    if (reg1_len >= len) {
+	p = (write_data_t*)reg1;
+    } else {
+	p = (write_data_t*)reg2;
+	skipped_len = reg1_len;
+    }
+
+    /* Update buffer length */
+    send_buf->len += len + skipped_len;
+
+init_send_data:
+    /* Init the new send data */
+    pj_bzero(p, sizeof(*p));
+    pj_list_init(p);
+    pj_list_push_back(&ssock->send_pending, p);
+
+    return p;
+}
+
+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata)
+{
+    send_buf_t *buf = &ssock->send_buf;
+    write_data_t *spl = &ssock->send_pending;
+
+    pj_assert(!pj_list_empty(&ssock->send_pending));
+    
+    /* Free slot from the buffer */
+    if (spl->next == wdata && spl->prev == wdata) {
+	/* This is the only data, reset the buffer */
+	buf->start = buf->buf;
+	buf->len = 0;
+    } else if (spl->next == wdata) {
+	/* This is the first data, shift start pointer of the buffer and
+	 * adjust the buffer length.
+	 */
+	buf->start = (char*)wdata->next;
+	if (wdata->next > wdata) {
+	    buf->len -= ((char*)wdata->next - buf->start);
+	} else {
+	    /* Overlapped */
+	    pj_size_t right_len, left_len;
+	    right_len = buf->buf + buf->max_len - (char*)wdata;
+	    left_len  = (char*)wdata->next - buf->buf;
+	    buf->len -= (right_len + left_len);
+	}
+    } else if (spl->prev == wdata) {
+	/* This is the last data, just adjust the buffer length */
+	if (wdata->prev < wdata) {
+	    pj_size_t jump_len;
+	    jump_len = (char*)wdata -
+		       ((char*)wdata->prev + wdata->prev->record_len);
+	    buf->len -= (wdata->record_len + jump_len);
+	} else {
+	    /* Overlapped */
+	    pj_size_t right_len, left_len;
+	    right_len = buf->buf + buf->max_len -
+			((char*)wdata->prev + wdata->prev->record_len);
+	    left_len  = (char*)wdata + wdata->record_len - buf->buf;
+	    buf->len -= (right_len + left_len);
+	}
+    }
+    /* For data in the middle buffer, just do nothing on the buffer. The slot
+     * will be freed later when freeing the first/last data.
+     */
+    
+    /* Remove the data from send pending list */
+    pj_list_erase(wdata);
+}
+
+/* Flush write circular buffer to network socket. */
+static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock,
+                                         pj_ioqueue_op_key_t *send_key,
+                                         pj_size_t orig_len, unsigned flags)
+{
+    pj_ssize_t len;
+    write_data_t *wdata;
+    pj_size_t needed_len;
+    pj_status_t status;
+
+    pj_lock_acquire(ssock->write_mutex);
+
+    /* Check if there is data in the circular buffer, flush it if any */
+    if (io_empty(ssock, &ssock->circ_buf_output)) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_SUCCESS;
+    }
+
+    /* Get data and its length */
+    len = io_size(ssock, &ssock->circ_buf_output);
+    if (len == 0) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_SUCCESS;
+    }
+
+    /* Calculate buffer size needed, and align it to 8 */
+    needed_len = len + sizeof(write_data_t);
+    needed_len = ((needed_len + 7) >> 3) << 3;
+
+    /* Allocate buffer for send data */
+    wdata = alloc_send_data(ssock, needed_len);
+    if (wdata == NULL) {
+	/* Oops, the send buffer is full, let's just
+	 * queue it for sending and return PJ_EPENDING.
+	 */
+	ssock->send_buf_pending.data_len = needed_len;
+	ssock->send_buf_pending.app_key = send_key;
+	ssock->send_buf_pending.flags = flags;
+	ssock->send_buf_pending.plain_data_len = orig_len;
+	pj_lock_release(ssock->write_mutex);
+	return PJ_EPENDING;
+    }
+
+    /* Copy the data and set its properties into the send data */
+    pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t));
+    wdata->key.user_data = wdata;
+    wdata->app_key = send_key;
+    wdata->record_len = needed_len;
+    wdata->data_len = len;
+    wdata->plain_data_len = orig_len;
+    wdata->flags = flags;
+    io_read(ssock, &ssock->circ_buf_output, (pj_uint8_t *)&wdata->data, len);
+
+    /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */
+    pj_lock_release(ssock->write_mutex);
+
+    /* Send it */
+    if (ssock->param.sock_type == pj_SOCK_STREAM()) {
+	status = pj_activesock_send(ssock->asock, &wdata->key, 
+				    wdata->data.content, &len,
+				    flags);
+    } else {
+	status = pj_activesock_sendto(ssock->asock, &wdata->key, 
+				      wdata->data.content, &len,
+				      flags,
+				      (pj_sockaddr_t*)&ssock->rem_addr,
+				      ssock->addr_len);
+    }
+
+    if (status != PJ_EPENDING) {
+	/* When the sending is not pending, remove the wdata from send
+	 * pending list.
+	 */
+	pj_lock_acquire(ssock->write_mutex);
+	free_send_data(ssock, wdata);
+	pj_lock_release(ssock->write_mutex);
+    }
+
+    return status;
+}
+
+#if 0
+/* Just for testing send buffer alloc/free */
+#include <pj/rand.h>
+pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool)
+{
+    enum { MAX_CHUNK_NUM = 20 };
+    unsigned chunk_size, chunk_cnt, i;
+    write_data_t *wdata[MAX_CHUNK_NUM] = {0};
+    pj_time_val now;
+    pj_ssl_sock_t *ssock = NULL;
+    pj_ssl_sock_param param;
+    pj_status_t status;
+
+    pj_gettimeofday(&now);
+    pj_srand((unsigned)now.sec);
+
+    pj_ssl_sock_param_default(&param);
+    status = pj_ssl_sock_create(pool, &param, &ssock);
+    if (status != PJ_SUCCESS) {
+	return status;
+    }
+
+    if (ssock->send_buf.max_len == 0) {
+	ssock->send_buf.buf = (char*)
+			      pj_pool_alloc(ssock->pool, 
+					    ssock->param.send_buffer_size);
+	ssock->send_buf.max_len = ssock->param.send_buffer_size;
+	ssock->send_buf.start = ssock->send_buf.buf;
+	ssock->send_buf.len = 0;
+    }
+
+    chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2;
+    chunk_cnt = 0;
+    for (i = 0; i < MAX_CHUNK_NUM; i++) {
+	wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321);
+	if (wdata[i])
+	    chunk_cnt++;
+	else
+	    break;
+    }
+
+    while (chunk_cnt) {
+	i = pj_rand() % MAX_CHUNK_NUM;
+	if (wdata[i]) {
+	    free_send_data(ssock, wdata[i]);
+	    wdata[i] = NULL;
+	    chunk_cnt--;
+	}
+    }
+
+    if (ssock->send_buf.len != 0)
+	status = PJ_EBUG;
+
+    pj_ssl_sock_close(ssock);
+    return status;
+}
+#endif
+
+static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data;
+    int timer_id = te->id;
+
+    te->id = TIMER_NONE;
+
+    PJ_UNUSED_ARG(th);
+
+    switch (timer_id) {
+    case TIMER_HANDSHAKE_TIMEOUT:
+	PJ_LOG(1,(ssock->pool->obj_name, "SSL timeout after %d.%ds",
+		  ssock->param.timeout.sec, ssock->param.timeout.msec));
+
+	on_handshake_complete(ssock, PJ_ETIMEDOUT);
+	break;
+    case TIMER_CLOSE:
+	pj_ssl_sock_close(ssock);
+	break;
+    default:
+	pj_assert(!"Unknown timer");
+	break;
+    }
+}
+
+static void ssl_on_destroy(void *arg)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)arg;
+
+    ssl_destroy(ssock);
+
+    if (ssock->circ_buf_input_mutex) {
+        pj_lock_destroy(ssock->circ_buf_input_mutex);
+	ssock->circ_buf_input_mutex = NULL;
+    }
+
+    if (ssock->circ_buf_output_mutex) {
+        pj_lock_destroy(ssock->circ_buf_output_mutex);
+	ssock->circ_buf_output_mutex = NULL;
+	ssock->write_mutex = NULL;
+    }
+
+    pj_pool_safe_release(&ssock->pool);
+}
+
+
+/*
+ *******************************************************************
+ * Active socket callbacks.
+ *******************************************************************
+ */
+
+/*
+ * Get the offset of pointer to read-buffer of SSL socket from read-buffer
+ * of active socket. Note that both SSL socket and active socket employ 
+ * different but correlated read-buffers (as much as async_cnt for each),
+ * and to make it easier/faster to find corresponding SSL socket's read-buffer
+ * from known active socket's read-buffer, the pointer of corresponding 
+ * SSL socket's read-buffer is stored right after the end of active socket's
+ * read-buffer.
+ */
+#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \
+					(read_data_t**) \
+					((pj_int8_t*)(asock_rbuf) + \
+					ssock->param.read_buffer_size)
+
+static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
+				     void *data,
+				     pj_size_t size,
+				     pj_status_t status,
+				     pj_size_t *remainder)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+			   pj_activesock_get_user_data(asock);
+
+    /* Socket error or closed */
+    if (data && size > 0) {
+    	pj_status_t status_;
+
+	/* Consume the whole data */
+        if (ssock->circ_buf_input_mutex)
+	    pj_lock_acquire(ssock->circ_buf_input_mutex);
+        status_ = io_write(ssock,&ssock->circ_buf_input, data, size);
+        if (ssock->circ_buf_input_mutex)
+            pj_lock_release(ssock->circ_buf_input_mutex);
+        if (status_ != PJ_SUCCESS) {
+            status = status_;
+	    goto on_error;
+	}
+    }
+
+    /* Check if SSL handshake hasn't finished yet */
+    if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
+	pj_bool_t ret = PJ_TRUE;
+
+	if (status == PJ_SUCCESS)
+	    status = ssl_do_handshake(ssock);
+
+	/* Not pending is either success or failed */
+	if (status != PJ_EPENDING)
+	    ret = on_handshake_complete(ssock, status);
+
+	return ret;
+    }
+
+    /* See if there is any decrypted data for the application */
+    if (ssock->read_started) {
+	do {
+	    read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
+	    void *data_ = (pj_int8_t*)buf->data + buf->len;
+	    int size_ = (int)(ssock->read_size - buf->len);
+	    pj_status_t status_;
+
+	    status_ = ssl_read(ssock, data_, &size_);
+
+	    if (size_ > 0 || status != PJ_SUCCESS) {
+		if (ssock->param.cb.on_data_read) {
+		    pj_bool_t ret;
+		    pj_size_t remainder_ = 0;
+
+		    if (size_ > 0)
+			buf->len += size_;
+    		
+                    if (status != PJ_SUCCESS) {
+                        ssock->ssl_state = SSL_STATE_ERROR;
+                    }
+
+		    ret = (*ssock->param.cb.on_data_read)(ssock, buf->data,
+							  buf->len, status,
+							  &remainder_);
+		    if (!ret) {
+			/* We've been destroyed */
+			return PJ_FALSE;
+		    }
+
+		    /* Application may have left some data to be consumed 
+		     * later.
+		     */
+		    buf->len = remainder_;
+		}
+
+		/* Active socket signalled connection closed/error, this has
+		 * been signalled to the application along with any remaining
+		 * buffer. So, let's just reset SSL socket now.
+		 */
+		if (status != PJ_SUCCESS) {
+		    ssl_reset_sock_state(ssock);
+		    return PJ_FALSE;
+		}
+
+	    } else if (status_ == PJ_SUCCESS) {
+	    	break;
+	    } else if (status_ == PJ_EEOF) {
+		status = ssl_do_handshake(ssock);
+		if (status == PJ_SUCCESS) {
+		    /* Renegotiation completed */
+
+		    /* Update certificates */
+		    ssl_update_certs_info(ssock);
+
+		    // Ticket #1573: Don't hold mutex while calling
+		    //               PJLIB socket send(). 
+		    //pj_lock_acquire(ssock->write_mutex);
+		    status = flush_delayed_send(ssock);
+		    //pj_lock_release(ssock->write_mutex);
+
+		    /* If flushing is ongoing, treat it as success */
+		    if (status == PJ_EBUSY)
+			status = PJ_SUCCESS;
+
+		    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+			PJ_PERROR(1,(ssock->pool->obj_name, status, 
+				     "Failed to flush delayed send"));
+			goto on_error;
+		    }
+		} else if (status != PJ_EPENDING) {
+		    PJ_PERROR(1,(ssock->pool->obj_name, status, 
+			         "Renegotiation failed"));
+		    goto on_error;
+		}
+
+		break;
+	    } else {
+	    	/* Error */
+	    	status = status_;
+	    	goto on_error;
+	    }
+
+	} while (1);
+    }
+
+    return PJ_TRUE;
+
+on_error:
+    if (ssock->ssl_state == SSL_STATE_HANDSHAKING)
+	return on_handshake_complete(ssock, status);
+
+    if (ssock->read_started && ssock->param.cb.on_data_read) {
+	pj_bool_t ret;
+	ret = (*ssock->param.cb.on_data_read)(ssock, NULL, 0, status,
+					      remainder);
+	if (!ret) {
+	    /* We've been destroyed */
+	    return PJ_FALSE;
+	}
+    }
+
+    ssl_reset_sock_state(ssock);
+    return PJ_FALSE;
+}
+
+static pj_bool_t asock_on_data_sent (pj_activesock_t *asock,
+				     pj_ioqueue_op_key_t *send_key,
+				     pj_ssize_t sent)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+			   pj_activesock_get_user_data(asock);
+    write_data_t *wdata = (write_data_t*)send_key->user_data;
+    pj_ioqueue_op_key_t *app_key = wdata->app_key;
+    pj_ssize_t sent_len;
+
+    sent_len = (sent > 0)? wdata->plain_data_len : sent;
+    
+    /* Update write buffer state */
+    pj_lock_acquire(ssock->write_mutex);
+    free_send_data(ssock, wdata);
+    pj_lock_release(ssock->write_mutex);
+    wdata = NULL;
+
+    if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
+	/* Initial handshaking */
+	pj_status_t status;
+	
+	status = ssl_do_handshake(ssock);
+	/* Not pending is either success or failed */
+	if (status != PJ_EPENDING)
+	    return on_handshake_complete(ssock, status);
+
+    } else if (send_key != &ssock->handshake_op_key) {
+	/* Some data has been sent, notify application */
+	if (ssock->param.cb.on_data_sent) {
+	    pj_bool_t ret;
+	    ret = (*ssock->param.cb.on_data_sent)(ssock, app_key, 
+						  sent_len);
+	    if (!ret) {
+		/* We've been destroyed */
+		return PJ_FALSE;
+	    }
+	}
+    } else {
+	/* SSL re-negotiation is on-progress, just do nothing */
+    }
+
+    /* Send buffer has been updated, let's try to send any pending data */
+    if (ssock->send_buf_pending.data_len) {
+	pj_status_t status;
+	status = flush_circ_buf_output(ssock, ssock->send_buf_pending.app_key,
+				 ssock->send_buf_pending.plain_data_len,
+				 ssock->send_buf_pending.flags);
+	if (status == PJ_SUCCESS || status == PJ_EPENDING) {
+	    ssock->send_buf_pending.data_len = 0;
+	}
+    }
+
+    return PJ_TRUE;
+}
+
+static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
+					   pj_sock_t newsock,
+					   const pj_sockaddr_t *src_addr,
+					   int src_addr_len)
+{
+    pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t*)
+				  pj_activesock_get_user_data(asock);
+    pj_ssl_sock_t *ssock;
+    pj_activesock_cb asock_cb;
+    pj_activesock_cfg asock_cfg;
+    unsigned i;
+    pj_status_t status;
+
+    /* Create new SSL socket instance */
+    status = pj_ssl_sock_create(ssock_parent->pool,
+				&ssock_parent->newsock_param, &ssock);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Update new SSL socket attributes */
+    ssock->sock = newsock;
+    ssock->parent = ssock_parent;
+    ssock->is_server = PJ_TRUE;
+    if (ssock_parent->cert) {
+	status = pj_ssl_sock_set_certificate(ssock, ssock->pool, 
+					     ssock_parent->cert);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+    }
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+				&ssock->param.qos_params, 1, 
+				ssock->pool->obj_name, NULL);
+    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+	goto on_return;
+
+    /* Apply socket options, if specified */
+    if (ssock->param.sockopt_params.cnt) {
+	status = pj_sock_setsockopt_params(ssock->sock, 
+					   &ssock->param.sockopt_params);
+	if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error)
+	    goto on_return;
+    }
+
+    /* Update local address */
+    ssock->addr_len = src_addr_len;
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
+				 &ssock->addr_len);
+    if (status != PJ_SUCCESS) {
+	/* This fails on few envs, e.g: win IOCP, just tolerate this and
+	 * use parent local address instead.
+	 */
+	pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr);
+    }
+
+    /* Set remote address */
+    pj_sockaddr_cp(&ssock->rem_addr, src_addr);
+
+    /* Create SSL context */
+    status = ssl_create(ssock);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare read buffer */
+    ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, 
+					       ssock->param.async_cnt,
+					       sizeof(void*));
+    if (!ssock->asock_rbuf)
+        return PJ_ENOMEM;
+
+    for (i = 0; i<ssock->param.async_cnt; ++i) {
+	ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
+					    ssock->pool, 
+					    ssock->param.read_buffer_size + 
+					    sizeof(read_data_t*));
+        if (!ssock->asock_rbuf[i])
+            return PJ_ENOMEM;
+    }
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    asock_cfg.async_cnt = ssock->param.async_cnt;
+    asock_cfg.concurrency = ssock->param.concurrency;
+    asock_cfg.whole_data = PJ_TRUE;
+    
+    /* If listener socket has group lock, automatically create group lock
+     * for the new socket.
+     */
+    if (ssock_parent->param.grp_lock) {
+	pj_grp_lock_t *glock;
+
+	status = pj_grp_lock_create(ssock->pool, NULL, &glock);
+	if (status != PJ_SUCCESS)
+	    goto on_return;
+
+	pj_grp_lock_add_ref(glock);
+	asock_cfg.grp_lock = ssock->param.grp_lock = glock;
+	pj_grp_lock_add_handler(ssock->param.grp_lock, ssock->pool, ssock,
+				ssl_on_destroy);
+    }
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    asock_cb.on_data_read = asock_on_data_read;
+    asock_cb.on_data_sent = asock_on_data_sent;
+
+    status = pj_activesock_create(ssock->pool,
+				  ssock->sock, 
+				  ssock->param.sock_type,
+				  &asock_cfg,
+				  ssock->param.ioqueue, 
+				  &asock_cb,
+				  ssock,
+				  &ssock->asock);
+
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Start read */
+    status = pj_activesock_start_read2(ssock->asock, ssock->pool, 
+				       (unsigned)ssock->param.read_buffer_size,
+				       ssock->asock_rbuf,
+				       PJ_IOQUEUE_ALWAYS_ASYNC);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare write/send state */
+    pj_assert(ssock->send_buf.max_len == 0);
+    ssock->send_buf.buf = (char*)
+			  pj_pool_alloc(ssock->pool, 
+					ssock->param.send_buffer_size);
+    if (!ssock->send_buf.buf)
+        return PJ_ENOMEM;
+
+    ssock->send_buf.max_len = ssock->param.send_buffer_size;
+    ssock->send_buf.start = ssock->send_buf.buf;
+    ssock->send_buf.len = 0;
+
+    /* Start handshake timer */
+    if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 ||
+	ssock->param.timeout.msec != 0))
+    {
+	pj_assert(ssock->timer.id == TIMER_NONE);
+	ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
+	status = pj_timer_heap_schedule(ssock->param.timer_heap, 
+				        &ssock->timer,
+					&ssock->param.timeout);
+	if (status != PJ_SUCCESS) {
+	    ssock->timer.id = TIMER_NONE;
+	    status = PJ_SUCCESS;
+	}
+    }
+
+    /* Start SSL handshake */
+    ssock->ssl_state = SSL_STATE_HANDSHAKING;
+    ssl_set_state(ssock, PJ_TRUE);
+    status = ssl_do_handshake(ssock);
+
+on_return:
+    if (ssock && status != PJ_EPENDING) {
+	on_handshake_complete(ssock, status);
+    }
+
+    /* Must return PJ_TRUE whatever happened, as active socket must 
+     * continue listening.
+     */
+    return PJ_TRUE;
+}
+
+static pj_bool_t asock_on_accept_complete2(pj_activesock_t *asock,
+					   pj_sock_t newsock,
+					   const pj_sockaddr_t *src_addr,
+					   int src_addr_len,
+					   pj_status_t status)
+{
+    pj_bool_t ret = PJ_TRUE;
+    if (status != PJ_SUCCESS) {
+	pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+			pj_activesock_get_user_data(asock);
+
+	if (ssock->param.cb.on_accept_complete2) {
+	    (*ssock->param.cb.on_accept_complete2) (ssock, NULL, 
+						    src_addr, src_addr_len, 
+						    status);
+	}
+    } else {
+	ret = asock_on_accept_complete(asock, newsock, src_addr, src_addr_len);
+    }
+    return ret;
+}
+
+static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
+					    pj_status_t status)
+{
+    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
+			   pj_activesock_get_user_data(asock);
+    unsigned i;
+
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Update local address */
+    ssock->addr_len = sizeof(pj_sockaddr);
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
+				 &ssock->addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Create SSL context */
+    status = ssl_create(ssock);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare read buffer */
+    ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, 
+					       ssock->param.async_cnt,
+					       sizeof(void*));
+    if (!ssock->asock_rbuf)
+        return PJ_ENOMEM;
+
+    for (i = 0; i<ssock->param.async_cnt; ++i) {
+	ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
+					    ssock->pool, 
+					    ssock->param.read_buffer_size + 
+					    sizeof(read_data_t*));
+        if (!ssock->asock_rbuf[i])
+            return PJ_ENOMEM;
+    }
+
+    /* Start read */
+    status = pj_activesock_start_read2(ssock->asock, ssock->pool, 
+				       (unsigned)ssock->param.read_buffer_size,
+				       ssock->asock_rbuf,
+				       PJ_IOQUEUE_ALWAYS_ASYNC);
+    if (status != PJ_SUCCESS)
+	goto on_return;
+
+    /* Prepare write/send state */
+    pj_assert(ssock->send_buf.max_len == 0);
+    ssock->send_buf.buf = (char*)
+			     pj_pool_alloc(ssock->pool, 
+					   ssock->param.send_buffer_size);
+    if (!ssock->send_buf.buf)
+        return PJ_ENOMEM;
+
+    ssock->send_buf.max_len = ssock->param.send_buffer_size;
+    ssock->send_buf.start = ssock->send_buf.buf;
+    ssock->send_buf.len = 0;
+
+    /* Set peer name */
+    ssl_set_peer_name(ssock);
+
+    /* Start SSL handshake */
+    ssock->ssl_state = SSL_STATE_HANDSHAKING;
+    ssl_set_state(ssock, PJ_FALSE);
+
+    status = ssl_do_handshake(ssock);
+    if (status != PJ_EPENDING)
+	goto on_return;
+
+    return PJ_TRUE;
+
+on_return:
+    return on_handshake_complete(ssock, status);
+}
+
+
+/*
+ *******************************************************************
+ * API
+ *******************************************************************
+ */
+
+/* Get available ciphers. */
+PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
+					         unsigned *cipher_num)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
+
+    ssl_ciphers_populate();
+
+    if (ssl_cipher_num == 0) {
+	*cipher_num = 0;
+	return PJ_ENOTFOUND;
+    }
+
+    *cipher_num = PJ_MIN(*cipher_num, ssl_cipher_num);
+
+    for (i = 0; i < *cipher_num; ++i)
+	ciphers[i] = ssl_ciphers[i].id;
+
+    return PJ_SUCCESS;
+}
+
+/* Get cipher name string */
+PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
+{
+    unsigned i;
+
+    ssl_ciphers_populate();
+
+    for (i = 0; i < ssl_cipher_num; ++i) {
+	if (cipher == ssl_ciphers[i].id)
+	    return ssl_ciphers[i].name;
+    }
+
+    return NULL;
+}
+
+/* Get cipher identifier */
+PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name)
+{
+    unsigned i;
+
+    ssl_ciphers_populate();
+
+    for (i = 0; i < ssl_cipher_num; ++i) {
+        if (!pj_ansi_stricmp(ssl_ciphers[i].name, cipher_name))
+            return ssl_ciphers[i].id;
+    }
+
+    return PJ_TLS_UNKNOWN_CIPHER;
+}
+
+/* Check if the specified cipher is supported by SSL/TLS backend. */
+PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher)
+{
+    unsigned i;
+
+    ssl_ciphers_populate();
+
+    for (i = 0; i < ssl_cipher_num; ++i) {
+	if (cipher == ssl_ciphers[i].id)
+	    return PJ_TRUE;
+    }
+
+    return PJ_FALSE;
+}
+
+/* Get available curves. */
+PJ_DEF(pj_status_t) pj_ssl_curve_get_availables(pj_ssl_curve curves[],
+						unsigned *curve_num)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(curves && curve_num, PJ_EINVAL);
+
+    ssl_ciphers_populate();
+
+    if (ssl_curves_num == 0) {
+	*curve_num = 0;
+	return PJ_ENOTFOUND;
+    }
+
+    *curve_num = PJ_MIN(*curve_num, ssl_curves_num);
+
+    for (i = 0; i < *curve_num; ++i)
+	curves[i] = ssl_curves[i].id;
+
+    return PJ_SUCCESS;
+}
+
+/* Get curve name string. */
+PJ_DEF(const char*) pj_ssl_curve_name(pj_ssl_curve curve)
+{
+    unsigned i;
+
+    ssl_ciphers_populate();
+
+    for (i = 0; i < ssl_curves_num; ++i) {
+	if (curve == ssl_curves[i].id)
+	    return ssl_curves[i].name;
+    }
+
+    return NULL;
+}
+
+/* Get curve ID from curve name string. */
+PJ_DEF(pj_ssl_curve) pj_ssl_curve_id(const char *curve_name)
+{
+    unsigned i;
+
+    ssl_ciphers_populate();
+
+    for (i = 0; i < ssl_curves_num; ++i) {
+        if (ssl_curves[i].name &&
+        	!pj_ansi_stricmp(ssl_curves[i].name, curve_name))
+        {
+            return ssl_curves[i].id;
+        }
+    }
+
+    return PJ_TLS_UNKNOWN_CURVE;
+}
+
+/* Check if the specified curve is supported by SSL/TLS backend. */
+PJ_DEF(pj_bool_t) pj_ssl_curve_is_supported(pj_ssl_curve curve)
+{
+    unsigned i;
+
+    ssl_ciphers_populate();
+
+    for (i = 0; i < ssl_curves_num; ++i) {
+	if (curve == ssl_curves[i].id)
+	    return PJ_TRUE;
+    }
+
+    return PJ_FALSE;
+}
+
+/*
+ * Create SSL socket instance. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
+					const pj_ssl_sock_param *param,
+					pj_ssl_sock_t **p_ssock)
+{
+    pj_ssl_sock_t *ssock;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP);
+
+    pool = pj_pool_create(pool->factory, "ssl%p", 512, 512, NULL);
+
+    /* Create secure socket */
+    ssock = ssl_alloc(pool);
+    ssock->pool = pool;
+    ssock->sock = PJ_INVALID_SOCKET;
+    ssock->ssl_state = SSL_STATE_NULL;
+    ssock->circ_buf_input.owner = ssock;
+    ssock->circ_buf_output.owner = ssock;
+    pj_list_init(&ssock->write_pending);
+    pj_list_init(&ssock->write_pending_empty);
+    pj_list_init(&ssock->send_pending);
+    pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer);
+    pj_ioqueue_op_key_init(&ssock->handshake_op_key,
+			   sizeof(pj_ioqueue_op_key_t));
+
+    /* Create secure socket mutex */
+    status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
+                                            &ssock->circ_buf_output_mutex);
+    ssock->write_mutex = ssock->circ_buf_output_mutex;
+    if (status != PJ_SUCCESS)
+        return status;
+
+    /* Create input circular buffer mutex */
+    status = pj_lock_create_simple_mutex(pool, pool->obj_name,
+                                         &ssock->circ_buf_input_mutex);
+    if (status != PJ_SUCCESS)
+        return status;
+
+    /* Init secure socket param */
+    pj_ssl_sock_param_copy(pool, &ssock->param, param);
+
+    if (ssock->param.grp_lock) {
+	pj_grp_lock_add_ref(ssock->param.grp_lock);
+	pj_grp_lock_add_handler(ssock->param.grp_lock, pool, ssock,
+				ssl_on_destroy);
+    }
+
+    ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3;
+    if (!ssock->param.timer_heap) {
+	PJ_LOG(3,(ssock->pool->obj_name, "Warning: timer heap is not "
+		  "available. It is recommended to supply one to avoid "
+	          "a race condition if more than one worker threads "
+	          "are used."));
+    }
+
+    /* Finally */
+    *p_ssock = ssock;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Close the secure socket. This will unregister the socket from the
+ * ioqueue and ultimately close the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
+{
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+    if (!ssock->pool)
+	return PJ_SUCCESS;
+
+    if (ssock->timer.id != TIMER_NONE) {
+	pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
+	ssock->timer.id = TIMER_NONE;
+    }
+
+    ssl_reset_sock_state(ssock);
+    if (ssock->param.grp_lock) {
+	pj_grp_lock_dec_ref(ssock->param.grp_lock);
+    } else {
+	ssl_on_destroy(ssock);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Associate arbitrary data with the secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
+					      void *user_data)
+{
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+    ssock->param.user_data = user_data;
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Retrieve the user data previously associated with this secure
+ * socket.
+ */
+PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
+{
+    PJ_ASSERT_RETURN(ssock, NULL);
+
+    return ssock->param.user_data;
+}
+
+/*
+ * Retrieve the local address and port used by specified SSL socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
+					  pj_ssl_sock_info *info)
+{
+    pj_bzero(info, sizeof(*info));
+
+    /* Established flag */
+    info->established = (ssock->ssl_state == SSL_STATE_ESTABLISHED);
+
+    /* Protocol */
+    info->proto = ssock->param.proto;
+
+    /* Local address */
+    pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
+    
+    if (info->established) {
+	info->cipher = ssl_get_cipher(ssock);
+
+	/* Remote address */
+	pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
+
+	/* Certificates info */
+	info->local_cert_info = &ssock->local_cert_info;
+	info->remote_cert_info = &ssock->remote_cert_info;
+
+	/* Verification status */
+	info->verify_status = ssock->verify_status;
+    }
+
+    /* Last known SSL error code */
+    info->last_native_err = ssock->last_err;
+
+    /* Group lock */
+    info->grp_lock = ssock->param.grp_lock;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Starts read operation on this secure socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    unsigned buff_size,
+					    pj_uint32_t flags)
+{
+    void **readbuf;
+    unsigned i;
+
+    PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
+
+    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
+	return PJ_EINVALIDOP;
+
+    readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, 
+				      sizeof(void*));
+    if (!readbuf)
+        return PJ_ENOMEM;
+
+    for (i=0; i<ssock->param.async_cnt; ++i) {
+	readbuf[i] = pj_pool_alloc(pool, buff_size);
+        if (!readbuf[i])
+            return PJ_ENOMEM;
+    }
+
+    return pj_ssl_sock_start_read2(ssock, pool, buff_size, 
+				   readbuf, flags);
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_read(), except that the application
+ * supplies the buffers for the read operation so that the acive socket
+ * does not have to allocate the buffers.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
+					     pj_pool_t *pool,
+					     unsigned buff_size,
+					     void *readbuf[],
+					     pj_uint32_t flags)
+{
+    unsigned i;
+
+    PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL);
+
+    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
+	return PJ_EINVALIDOP;
+
+    /* Create SSL socket read buffer */
+    ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, 
+					       ssock->param.async_cnt,
+					       sizeof(read_data_t));
+    if (!ssock->ssock_rbuf)
+        return PJ_ENOMEM;
+
+    /* Store SSL socket read buffer pointer in the activesock read buffer */
+    for (i=0; i<ssock->param.async_cnt; ++i) {
+	read_data_t **p_ssock_rbuf = 
+			OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]);
+
+	ssock->ssock_rbuf[i].data = readbuf[i];
+	ssock->ssock_rbuf[i].len = 0;
+
+	*p_ssock_rbuf = &ssock->ssock_rbuf[i];
+    }
+
+    ssock->read_size = buff_size;
+    ssock->read_started = PJ_TRUE;
+    ssock->read_flags = flags;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Same as pj_ssl_sock_start_read(), except that this function is used
+ * only for datagram sockets, and it will trigger \a on_data_recvfrom()
+ * callback instead.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
+						pj_pool_t *pool,
+						unsigned buff_size,
+						pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(flags);
+
+    return PJ_ENOTSUP;
+}
+
+
+/*
+ * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() 
+ * operation takes the buffer from the argument rather than creating
+ * new ones.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
+						 pj_pool_t *pool,
+						 unsigned buff_size,
+						 void *readbuf[],
+						 pj_uint32_t flags)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(pool);
+    PJ_UNUSED_ARG(buff_size);
+    PJ_UNUSED_ARG(readbuf);
+    PJ_UNUSED_ARG(flags);
+
+    return PJ_ENOTSUP;
+}
+
+
+/* Write plain data to SSL and flush the buffer. */
+static pj_status_t ssl_send (pj_ssl_sock_t *ssock, 
+			     pj_ioqueue_op_key_t *send_key,
+			     const void *data,
+			     pj_ssize_t size,
+			     unsigned flags)
+{
+    pj_status_t status;
+    int nwritten;
+
+    /* Write the plain data to SSL, after SSL encrypts it, the buffer will
+     * contain the secured data to be sent via socket. Note that re-
+     * negotitation may be on progress, so sending data should be delayed
+     * until re-negotiation is completed.
+     */
+    pj_lock_acquire(ssock->write_mutex);
+    /* Don't write to SSL if send buffer is full and some data is in
+     * write buffer already, just return PJ_ENOMEM.
+     */
+    if (ssock->send_buf_pending.data_len) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_ENOMEM;
+    }
+    status = ssl_write(ssock, data, size, &nwritten);
+    pj_lock_release(ssock->write_mutex);
+    
+    if (status == PJ_SUCCESS && nwritten == size) {
+	/* All data written, flush write buffer to network socket */
+	status = flush_circ_buf_output(ssock, send_key, size, flags);
+    } else if (status == PJ_EEOF) {
+        /* Re-negotiation is on progress, flush re-negotiation data */
+	status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0);
+	if (status == PJ_SUCCESS || status == PJ_EPENDING) {
+	    /* Just return PJ_EBUSY when re-negotiation is on progress */
+	    status = PJ_EBUSY;
+	}
+    }
+
+    return status;
+}
+
+/* Flush delayed data sending in the write pending list. */
+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
+{
+    /* Check for another ongoing flush */
+    if (ssock->flushing_write_pend)
+	return PJ_EBUSY;
+
+    pj_lock_acquire(ssock->write_mutex);
+
+    /* Again, check for another ongoing flush */
+    if (ssock->flushing_write_pend) {
+	pj_lock_release(ssock->write_mutex);
+	return PJ_EBUSY;
+    }
+
+    /* Set ongoing flush flag */
+    ssock->flushing_write_pend = PJ_TRUE;
+
+    while (!pj_list_empty(&ssock->write_pending)) {
+        write_data_t *wp;
+	pj_status_t status;
+
+	wp = ssock->write_pending.next;
+
+	/* Ticket #1573: Don't hold mutex while calling socket send. */
+	pj_lock_release(ssock->write_mutex);
+
+	status = ssl_send (ssock, &wp->key, wp->data.ptr, 
+			   wp->plain_data_len, wp->flags);
+	if (status != PJ_SUCCESS) {
+	    /* Reset ongoing flush flag first. */
+	    ssock->flushing_write_pend = PJ_FALSE;
+	    return status;
+	}
+
+	pj_lock_acquire(ssock->write_mutex);
+	pj_list_erase(wp);
+	pj_list_push_back(&ssock->write_pending_empty, wp);
+    }
+
+    /* Reset ongoing flush flag */
+    ssock->flushing_write_pend = PJ_FALSE;
+
+    pj_lock_release(ssock->write_mutex);
+
+    return PJ_SUCCESS;
+}
+
+/* Sending is delayed, push back the sending data into pending list. */
+static pj_status_t delay_send (pj_ssl_sock_t *ssock,
+			       pj_ioqueue_op_key_t *send_key,
+			       const void *data,
+			       pj_ssize_t size,
+			       unsigned flags)
+{
+    write_data_t *wp;
+
+    pj_lock_acquire(ssock->write_mutex);
+
+    /* Init write pending instance */
+    if (!pj_list_empty(&ssock->write_pending_empty)) {
+	wp = ssock->write_pending_empty.next;
+	pj_list_erase(wp);
+    } else {
+	wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t);
+    }
+
+    wp->app_key = send_key;
+    wp->plain_data_len = size;
+    wp->data.ptr = data;
+    wp->flags = flags;
+
+    pj_list_push_back(&ssock->write_pending, wp);
+    
+    pj_lock_release(ssock->write_mutex);
+
+    /* Must return PJ_EPENDING */
+    return PJ_EPENDING;
+}
+
+
+/**
+ * Send data using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
+				      pj_ioqueue_op_key_t *send_key,
+				      const void *data,
+				      pj_ssize_t *size,
+				      unsigned flags)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL);
+
+    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
+	return PJ_EINVALIDOP;
+
+    // Ticket #1573: Don't hold mutex while calling PJLIB socket send().
+    //pj_lock_acquire(ssock->write_mutex);
+
+    /* Flush delayed send first. Sending data might be delayed when 
+     * re-negotiation is on-progress.
+     */
+    status = flush_delayed_send(ssock);
+    if (status == PJ_EBUSY) {
+	/* Re-negotiation or flushing is on progress, delay sending */
+	status = delay_send(ssock, send_key, data, *size, flags);
+	goto on_return;
+    } else if (status != PJ_SUCCESS) {
+	goto on_return;
+    }
+
+    /* Write data to SSL */
+    status = ssl_send(ssock, send_key, data, *size, flags);
+    if (status == PJ_EBUSY) {
+	/* Re-negotiation is on progress, delay sending */
+	status = delay_send(ssock, send_key, data, *size, flags);
+    }
+
+on_return:
+    //pj_lock_release(ssock->write_mutex);
+    return status;
+}
+
+
+/**
+ * Send datagram using the socket.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
+					pj_ioqueue_op_key_t *send_key,
+					const void *data,
+					pj_ssize_t *size,
+					unsigned flags,
+					const pj_sockaddr_t *addr,
+					int addr_len)
+{
+    PJ_UNUSED_ARG(ssock);
+    PJ_UNUSED_ARG(send_key);
+    PJ_UNUSED_ARG(data);
+    PJ_UNUSED_ARG(size);
+    PJ_UNUSED_ARG(flags);
+    PJ_UNUSED_ARG(addr);
+    PJ_UNUSED_ARG(addr_len);
+
+    return PJ_ENOTSUP;
+}
+
+
+/**
+ * Starts asynchronous socket accept() operations on this secure socket. 
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
+					      pj_pool_t *pool,
+					      const pj_sockaddr_t *localaddr,
+					      int addr_len)
+{
+    return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len,
+    				     &ssock->param);
+}
+
+
+/**
+ * Same as #pj_ssl_sock_start_accept(), but application provides parameter
+ * for new accepted secure sockets.
+ */
+PJ_DEF(pj_status_t)
+pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock,
+			  pj_pool_t *pool,
+			  const pj_sockaddr_t *localaddr,
+			  int addr_len,
+			  const pj_ssl_sock_param *newsock_param)
+{
+    pj_activesock_cb asock_cb;
+    pj_activesock_cfg asock_cfg;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
+
+    /* Verify new socket parameters */
+    if (newsock_param->grp_lock != ssock->param.grp_lock ||
+        newsock_param->sock_af != ssock->param.sock_af ||
+        newsock_param->sock_type != ssock->param.sock_type)
+    {
+        return PJ_EINVAL;
+    }
+
+    /* Create socket */
+    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, 
+			    &ssock->sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Apply SO_REUSEADDR */
+    if (ssock->param.reuse_addr) {
+	int enabled = 1;
+	status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(),
+				    pj_SO_REUSEADDR(),
+				    &enabled, sizeof(enabled));
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(ssock->pool->obj_name, status,
+		         "Warning: error applying SO_REUSEADDR"));
+	}
+    }
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+				&ssock->param.qos_params, 2, 
+				ssock->pool->obj_name, NULL);
+    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+	goto on_error;
+
+    /* Apply socket options, if specified */
+    if (ssock->param.sockopt_params.cnt) {
+	status = pj_sock_setsockopt_params(ssock->sock, 
+					   &ssock->param.sockopt_params);
+
+	if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error)
+	    goto on_error;
+    }
+
+    /* Bind socket */
+    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Start listening to the address */
+    status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    asock_cfg.async_cnt = ssock->param.async_cnt;
+    asock_cfg.concurrency = ssock->param.concurrency;
+    asock_cfg.whole_data = PJ_TRUE;
+    asock_cfg.grp_lock = ssock->param.grp_lock;
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    //asock_cb.on_accept_complete = asock_on_accept_complete;
+    asock_cb.on_accept_complete2 = asock_on_accept_complete2;
+
+    status = pj_activesock_create(pool,
+				  ssock->sock, 
+				  ssock->param.sock_type,
+				  &asock_cfg,
+				  ssock->param.ioqueue, 
+				  &asock_cb,
+				  ssock,
+				  &ssock->asock);
+
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Start accepting */
+    pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param);
+    ssock->newsock_param.grp_lock = NULL;
+    status = pj_activesock_start_accept(ssock->asock, pool);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Update local address */
+    ssock->addr_len = addr_len;
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
+				 &ssock->addr_len);
+    if (status != PJ_SUCCESS)
+	pj_sockaddr_cp(&ssock->local_addr, localaddr);
+
+    ssock->is_server = PJ_TRUE;
+
+    return PJ_SUCCESS;
+
+on_error:
+    ssl_reset_sock_state(ssock);
+    return status;
+}
+
+
+/**
+ * Starts asynchronous socket connect() operation.
+ */
+PJ_DEF(pj_status_t) pj_ssl_sock_start_connect( pj_ssl_sock_t *ssock,
+					       pj_pool_t *pool,
+					       const pj_sockaddr_t *localaddr,
+					       const pj_sockaddr_t *remaddr,
+					       int addr_len)
+{
+    pj_activesock_cb asock_cb;
+    pj_activesock_cfg asock_cfg;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
+		     PJ_EINVAL);
+
+    /* Create socket */
+    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, 
+			    &ssock->sock);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Apply QoS, if specified */
+    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
+				&ssock->param.qos_params, 2, 
+				ssock->pool->obj_name, NULL);
+    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
+	goto on_error;
+
+    /* Apply socket options, if specified */
+    if (ssock->param.sockopt_params.cnt) {
+	status = pj_sock_setsockopt_params(ssock->sock, 
+					   &ssock->param.sockopt_params);
+
+	if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error)
+	    goto on_error;
+    }
+
+    /* Bind socket */
+    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Create active socket */
+    pj_activesock_cfg_default(&asock_cfg);
+    asock_cfg.async_cnt = ssock->param.async_cnt;
+    asock_cfg.concurrency = ssock->param.concurrency;
+    asock_cfg.whole_data = PJ_TRUE;
+    asock_cfg.grp_lock = ssock->param.grp_lock;
+
+    pj_bzero(&asock_cb, sizeof(asock_cb));
+    asock_cb.on_connect_complete = asock_on_connect_complete;
+    asock_cb.on_data_read = asock_on_data_read;
+    asock_cb.on_data_sent = asock_on_data_sent;
+
+    status = pj_activesock_create(pool,
+				  ssock->sock, 
+				  ssock->param.sock_type,
+				  &asock_cfg,
+				  ssock->param.ioqueue, 
+				  &asock_cb,
+				  ssock,
+				  &ssock->asock);
+
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    /* Save remote address */
+    pj_sockaddr_cp(&ssock->rem_addr, remaddr);
+
+    /* Start timer */
+    if (ssock->param.timer_heap &&
+        (ssock->param.timeout.sec != 0 || ssock->param.timeout.msec != 0))
+    {
+	pj_assert(ssock->timer.id == TIMER_NONE);
+	ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
+	status = pj_timer_heap_schedule(ssock->param.timer_heap,
+					&ssock->timer,
+				        &ssock->param.timeout);
+	if (status != PJ_SUCCESS) {
+	    ssock->timer.id = TIMER_NONE;
+	    status = PJ_SUCCESS;
+	}
+    }
+
+    status = pj_activesock_start_connect(ssock->asock, pool, remaddr,
+					 addr_len);
+
+    if (status == PJ_SUCCESS)
+	asock_on_connect_complete(ssock->asock, PJ_SUCCESS);
+    else if (status != PJ_EPENDING)
+	goto on_error;
+
+    /* Update local address */
+    ssock->addr_len = addr_len;
+    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
+				 &ssock->addr_len);
+    /* Note that we may not get an IP address here. This can
+     * happen for example on Windows, where getsockname()
+     * would return 0.0.0.0 if socket has just started the
+     * async connect. In this case, just leave the local
+     * address with 0.0.0.0 for now; it will be updated
+     * once the socket is established.
+     */
+
+    /* Update SSL state */
+    ssock->is_server = PJ_FALSE;
+
+    return PJ_EPENDING;
+
+on_error:
+    ssl_reset_sock_state(ssock);
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
+{
+    return ssl_renegotiate(ssock);
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
+
+    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
+	return PJ_EINVALIDOP;
+
+    status = ssl_renegotiate(ssock);
+    if (status == PJ_SUCCESS) {
+	status = ssl_do_handshake(ssock);
+    }
+
+    return status;
+}
+
+/* Load credentials from files. */
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files (pj_pool_t *pool,
+						 const pj_str_t *CA_file,
+						 const pj_str_t *cert_file,
+						 const pj_str_t *privkey_file,
+						 const pj_str_t *privkey_pass,
+						 pj_ssl_cert_t **p_cert)
+{
+    return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file,
+					privkey_file, privkey_pass, p_cert);
+}
+
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool,
+						 const pj_str_t *CA_file,
+						 const pj_str_t *CA_path,
+						 const pj_str_t *cert_file,
+						 const pj_str_t *privkey_file,
+						 const pj_str_t *privkey_pass,
+						 pj_ssl_cert_t **p_cert)
+{
+    pj_ssl_cert_t *cert;
+
+    PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file &&
+		     privkey_file,
+		     PJ_EINVAL);
+
+    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+    if (CA_file) {
+    	pj_strdup_with_null(pool, &cert->CA_file, CA_file);
+    }
+    if (CA_path) {
+    	pj_strdup_with_null(pool, &cert->CA_path, CA_path);
+    }
+    pj_strdup_with_null(pool, &cert->cert_file, cert_file);
+    pj_strdup_with_null(pool, &cert->privkey_file, privkey_file);
+    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
+
+    *p_cert = cert;
+
+    return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
+					const pj_ssl_cert_buffer *CA_buf,
+					const pj_ssl_cert_buffer *cert_buf,
+					const pj_ssl_cert_buffer *privkey_buf,
+					const pj_str_t *privkey_pass,
+					pj_ssl_cert_t **p_cert)
+{
+    pj_ssl_cert_t *cert;
+
+    PJ_ASSERT_RETURN(pool && CA_buf && cert_buf && privkey_buf, PJ_EINVAL);
+
+    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+    pj_strdup(pool, &cert->CA_buf, CA_buf);
+    pj_strdup(pool, &cert->cert_buf, cert_buf);
+    pj_strdup(pool, &cert->privkey_buf, privkey_buf);
+    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
+
+    *p_cert = cert;
+
+    return PJ_SUCCESS;
+}
+
+/* Set SSL socket credentials. */
+PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
+					    pj_ssl_sock_t *ssock,
+					    pj_pool_t *pool,
+					    const pj_ssl_cert_t *cert)
+{
+    pj_ssl_cert_t *cert_;
+
+    PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL);
+
+    cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
+    pj_memcpy(cert_, cert, sizeof(pj_ssl_cert_t));
+    pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file);
+    pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path);
+    pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file);
+    pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file);
+    pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass);
+
+    pj_strdup(pool, &cert_->CA_buf, &cert->CA_buf);
+    pj_strdup(pool, &cert_->cert_buf, &cert->cert_buf);
+    pj_strdup(pool, &cert_->privkey_buf, &cert->privkey_buf);
+
+    ssock->cert = cert_;
+
+    return PJ_SUCCESS;
+}
+
Index: /pjproject/trunk/pjlib/src/pj/ssl_sock_imp_common.h
===================================================================
--- /pjproject/trunk/pjlib/src/pj/ssl_sock_imp_common.h	(revision 5938)
+++ /pjproject/trunk/pjlib/src/pj/ssl_sock_imp_common.h	(revision 5938)
@@ -0,0 +1,263 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2019-2019 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+
+#ifndef __SSL_SOCK_IMP_COMMON_H__
+#define __SSL_SOCK_IMP_COMMON_H__
+
+#include <pj/activesock.h>
+#include <pj/timer.h>
+
+/*
+ * SSL/TLS state enumeration.
+ */
+enum ssl_state {
+    SSL_STATE_NULL,
+    SSL_STATE_HANDSHAKING,
+    SSL_STATE_ESTABLISHED,
+    SSL_STATE_ERROR
+};
+
+/*
+ * Internal timer types.
+ */
+enum timer_id
+{
+    TIMER_NONE,
+    TIMER_HANDSHAKE_TIMEOUT,
+    TIMER_CLOSE
+};
+
+/*
+ * Structure of SSL socket read buffer.
+ */
+typedef struct read_data_t
+{
+    void		 *data;
+    pj_size_t		  len;
+} read_data_t;
+
+/*
+ * Structure of SSL socket write data.
+ */
+typedef struct write_data_t {
+    PJ_DECL_LIST_MEMBER(struct write_data_t);
+    pj_ioqueue_op_key_t	 key;
+    pj_size_t 	 	 record_len;
+    pj_ioqueue_op_key_t	*app_key;
+    pj_size_t 	 	 plain_data_len;
+    pj_size_t 	 	 data_len;
+    unsigned		 flags;
+    union {
+	char		 content[1];
+	const char	*ptr;
+    } data;
+} write_data_t;
+
+/*
+ * Structure of SSL socket write buffer (circular buffer).
+ */
+typedef struct send_buf_t {
+    char		*buf;
+    pj_size_t		 max_len;    
+    char		*start;
+    pj_size_t		 len;
+} send_buf_t;
+
+/* Circular buffer object */
+typedef struct circ_buf_t {
+    pj_ssl_sock_t *owner;  /* owner of the circular buffer */
+    pj_size_t      cap;    /* maximum number of elements (must be power of 2) */
+    pj_size_t      readp;  /* index of oldest element */
+    pj_size_t      writep; /* index at which to write new element  */
+    pj_size_t      size;   /* number of elements */
+    pj_uint8_t    *buf;    /* data buffer */
+    pj_pool_t     *pool;   /* where new allocations will take place */
+} circ_buf_t;
+
+/*
+ * Secure socket structure definition.
+ */
+typedef struct pj_ssl_sock_t
+{
+    pj_pool_t		 *pool;
+    pj_ssl_sock_t	 *parent;
+    pj_ssl_sock_param	  param;
+    pj_ssl_sock_param	  newsock_param;
+    pj_ssl_cert_t	 *cert;
+    
+    pj_ssl_cert_info	  local_cert_info;
+    pj_ssl_cert_info	  remote_cert_info;
+
+    pj_bool_t		  is_server;
+    enum ssl_state	  ssl_state;
+    pj_ioqueue_op_key_t	  handshake_op_key;
+    pj_timer_entry	  timer;
+    pj_status_t		  verify_status;
+
+    unsigned long	  last_err;
+
+    pj_sock_t		  sock;
+    pj_activesock_t	 *asock;
+
+    pj_sockaddr		  local_addr;
+    pj_sockaddr		  rem_addr;
+    int			  addr_len;
+    
+    pj_bool_t		  read_started;
+    pj_size_t		  read_size;
+    pj_uint32_t		  read_flags;
+    void		**asock_rbuf;
+    read_data_t		 *ssock_rbuf;
+
+    write_data_t	  write_pending;/* list of pending write to ssl */
+    write_data_t	  write_pending_empty; /* cache for write_pending   */
+    pj_bool_t		  flushing_write_pend; /* flag of flushing is ongoing*/
+    send_buf_t		  send_buf;
+    write_data_t 	  send_buf_pending; /* send buffer is full but some
+					     * data is queuing in wbio.     */
+    write_data_t	  send_pending;	/* list of pending write to network */
+    pj_lock_t		 *write_mutex;	/* protect write BIO and send_buf   */
+
+    circ_buf_t            circ_buf_input;
+    pj_lock_t            *circ_buf_input_mutex;
+
+    circ_buf_t            circ_buf_output;
+    pj_lock_t            *circ_buf_output_mutex;
+} pj_ssl_sock_t;
+
+
+/*
+ * Certificate/credential structure definition.
+ */
+struct pj_ssl_cert_t
+{
+    pj_str_t CA_file;
+    pj_str_t CA_path;
+    pj_str_t cert_file;
+    pj_str_t privkey_file;
+    pj_str_t privkey_pass;
+
+    /* Certificate buffer. */
+    pj_ssl_cert_buffer CA_buf;
+    pj_ssl_cert_buffer cert_buf;
+    pj_ssl_cert_buffer privkey_buf;
+};
+
+/* ssl available ciphers */
+static unsigned ssl_cipher_num;
+static struct ssl_ciphers_t {
+    pj_ssl_cipher    id;
+    const char	    *name;
+} ssl_ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
+
+/* ssl available curves */
+static unsigned ssl_curves_num;
+static struct ssl_curves_t {
+    pj_ssl_curve    id;
+    const char	    *name;
+} ssl_curves[PJ_SSL_SOCK_MAX_CURVES];
+
+/*
+ *******************************************************************
+ * I/O functions.
+ *******************************************************************
+ */
+
+static pj_bool_t io_empty(pj_ssl_sock_t *ssock, circ_buf_t *cb);
+static pj_size_t io_size(pj_ssl_sock_t *ssock, circ_buf_t *cb);
+static void io_read(pj_ssl_sock_t *ssock, circ_buf_t *cb,
+		    pj_uint8_t *dst, pj_size_t len);
+static pj_status_t io_write(pj_ssl_sock_t *ssock, circ_buf_t *cb,
+                            const pj_uint8_t *src, pj_size_t len);
+
+static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len);
+static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata);
+static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
+
+#ifdef SSL_SOCK_IMP_USE_CIRC_BUF
+/*
+ *******************************************************************
+ * Circular buffer functions.
+ *******************************************************************
+ */
+
+static pj_status_t circ_init(pj_pool_factory *factory,
+                             circ_buf_t *cb, pj_size_t cap);
+static void circ_deinit(circ_buf_t *cb);
+static pj_bool_t circ_empty(const circ_buf_t *cb);
+static pj_size_t circ_size(const circ_buf_t *cb);
+static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len);
+static pj_status_t circ_write(circ_buf_t *cb,
+                              const pj_uint8_t *src, pj_size_t len);
+
+inline static pj_bool_t io_empty(pj_ssl_sock_t *ssock, circ_buf_t *cb)
+{
+    return circ_empty(cb);
+}
+inline static pj_size_t io_size(pj_ssl_sock_t *ssock, circ_buf_t *cb)
+{
+    return circ_size(cb);
+}
+inline static void io_reset(pj_ssl_sock_t *ssock, circ_buf_t *cb) {}
+inline static void io_read(pj_ssl_sock_t *ssock, circ_buf_t *cb,
+		    	   pj_uint8_t *dst, pj_size_t len)
+{
+    return circ_read(cb, dst, len);
+}
+inline static pj_status_t io_write(pj_ssl_sock_t *ssock, circ_buf_t *cb,
+                            const pj_uint8_t *src, pj_size_t len)
+{
+    return circ_write(cb, src, len);
+}
+
+#endif
+
+
+/*
+ *******************************************************************
+ * The below functions must be implemented by SSL backend.
+ *******************************************************************
+ */
+
+/* Allocate SSL backend struct */
+static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool);
+/* Create and initialize new SSL context and instance */
+static pj_status_t ssl_create(pj_ssl_sock_t *ssock);
+/* Destroy SSL context and instance */
+static void ssl_destroy(pj_ssl_sock_t *ssock);
+/* Reset SSL socket state */
+static void ssl_reset_sock_state(pj_ssl_sock_t *ssock);
+
+/* Ciphers and certs */
+static void ssl_ciphers_populate();
+static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock);
+static void ssl_update_certs_info(pj_ssl_sock_t *ssock);
+
+/* SSL session functions */
+static void ssl_set_state(pj_ssl_sock_t *ssock, pj_bool_t is_server);
+static void ssl_set_peer_name(pj_ssl_sock_t *ssock);
+
+static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock);
+static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock);
+static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size);
+static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data,
+			     pj_ssize_t size, int *nwritten);
+
+#endif /* __SSL_SOCK_IMP_COMMON_H__ */
+       
Index: /pjproject/trunk/pjlib/src/pj/ssl_sock_ossl.c
===================================================================
--- /pjproject/trunk/pjlib/src/pj/ssl_sock_ossl.c	(revision 5937)
+++ /pjproject/trunk/pjlib/src/pj/ssl_sock_ossl.c	(revision 5938)
@@ -32,5 +32,4 @@
 #include <pj/timer.h>
 
-
 /* Only build when PJ_HAS_SSL_SOCK is enabled and when the backend is
  * OpenSSL.
@@ -39,10 +38,8 @@
     (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_OPENSSL)
 
+#include "ssl_sock_imp_common.h"
+#include "ssl_sock_imp_common.c"
+
 #define THIS_FILE		"ssl_sock_ossl.c"
-
-/* Workaround for ticket #985 and #1930 */
-#ifndef PJ_SSL_SOCK_DELAYED_CLOSE_TIMEOUT
-#   define PJ_SSL_SOCK_DELAYED_CLOSE_TIMEOUT	500
-#endif
 
 /* 
@@ -164,116 +161,9 @@
 
 /*
- * SSL/TLS state enumeration.
- */
-enum ssl_state {
-    SSL_STATE_NULL,
-    SSL_STATE_HANDSHAKING,
-    SSL_STATE_ESTABLISHED,
-    SSL_STATE_ERROR
-};
-
-/*
- * Internal timer types.
- */
-enum timer_id
-{
-    TIMER_NONE,
-    TIMER_HANDSHAKE_TIMEOUT,
-    TIMER_CLOSE
-};
-
-/*
- * Structure of SSL socket read buffer.
- */
-typedef struct read_data_t
-{
-    void		 *data;
-    pj_size_t		  len;
-} read_data_t;
-
-/*
- * Get the offset of pointer to read-buffer of SSL socket from read-buffer
- * of active socket. Note that both SSL socket and active socket employ 
- * different but correlated read-buffers (as much as async_cnt for each),
- * and to make it easier/faster to find corresponding SSL socket's read-buffer
- * from known active socket's read-buffer, the pointer of corresponding 
- * SSL socket's read-buffer is stored right after the end of active socket's
- * read-buffer.
- */
-#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \
-					(read_data_t**) \
-					((pj_int8_t*)(asock_rbuf) + \
-					ssock->param.read_buffer_size)
-
-/*
- * Structure of SSL socket write data.
- */
-typedef struct write_data_t {
-    PJ_DECL_LIST_MEMBER(struct write_data_t);
-    pj_ioqueue_op_key_t	 key;
-    pj_size_t 	 	 record_len;
-    pj_ioqueue_op_key_t	*app_key;
-    pj_size_t 	 	 plain_data_len;
-    pj_size_t 	 	 data_len;
-    unsigned		 flags;
-    union {
-	char		 content[1];
-	const char	*ptr;
-    } data;
-} write_data_t;
-
-/*
- * Structure of SSL socket write buffer (circular buffer).
- */
-typedef struct send_buf_t {
-    char		*buf;
-    pj_size_t		 max_len;    
-    char		*start;
-    pj_size_t		 len;
-} send_buf_t;
-
-/*
  * Secure socket structure definition.
  */
-struct pj_ssl_sock_t
-{
-    pj_pool_t		 *pool;
-    pj_ssl_sock_t	 *parent;
-    pj_ssl_sock_param	  param;
-    pj_ssl_sock_param	  newsock_param;
-    pj_ssl_cert_t	 *cert;
-    
-    pj_ssl_cert_info	  local_cert_info;
-    pj_ssl_cert_info	  remote_cert_info;
-
-    pj_bool_t		  is_server;
-    enum ssl_state	  ssl_state;
-    pj_ioqueue_op_key_t	  handshake_op_key;
-    pj_timer_entry	  timer;
-    pj_status_t		  verify_status;
-
-    unsigned long	  last_err;
-
-    pj_sock_t		  sock;
-    pj_activesock_t	 *asock;
-
-    pj_sockaddr		  local_addr;
-    pj_sockaddr		  rem_addr;
-    int			  addr_len;
-    
-    pj_bool_t		  read_started;
-    pj_size_t		  read_size;
-    pj_uint32_t		  read_flags;
-    void		**asock_rbuf;
-    read_data_t		 *ssock_rbuf;
-
-    write_data_t	  write_pending;/* list of pending write to OpenSSL */
-    write_data_t	  write_pending_empty; /* cache for write_pending   */
-    pj_bool_t		  flushing_write_pend; /* flag of flushing is ongoing*/
-    send_buf_t		  send_buf;
-    write_data_t 	  send_buf_pending; /* send buffer is full but some
-					     * data is queuing in wbio.     */
-    write_data_t	  send_pending;	/* list of pending write to network */
-    pj_lock_t		 *write_mutex;	/* protect write BIO and send_buf   */
+typedef struct ossl_sock_t
+{
+    pj_ssl_sock_t  	  base;
 
     SSL_CTX		 *ossl_ctx;
@@ -281,34 +171,5 @@
     BIO			 *ossl_rbio;
     BIO			 *ossl_wbio;
-};
-
-
-/*
- * Certificate/credential structure definition.
- */
-struct pj_ssl_cert_t
-{
-    pj_str_t CA_file;
-    pj_str_t CA_path;
-    pj_str_t cert_file;
-    pj_str_t privkey_file;
-    pj_str_t privkey_pass;
-
-    /* Certificate buffer. */
-    pj_ssl_cert_buffer CA_buf;
-    pj_ssl_cert_buffer cert_buf;
-    pj_ssl_cert_buffer privkey_buf;
-};
-
-
-static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len);
-static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata);
-static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
-
-/*
- *******************************************************************
- * Static/internal functions.
- *******************************************************************
- */
+} ossl_sock_t;
 
 /**
@@ -520,20 +381,54 @@
 
 
+/*
+ *******************************************************************
+ * I/O functions.
+ *******************************************************************
+ */
+
+static pj_bool_t io_empty(pj_ssl_sock_t *ssock, circ_buf_t *cb)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+
+    return !BIO_pending(ossock->ossl_wbio);
+}
+
+static pj_size_t io_size(pj_ssl_sock_t *ssock, circ_buf_t *cb)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    char *data;
+
+    return BIO_get_mem_data(ossock->ossl_wbio, &data);
+}
+
+static void io_read(pj_ssl_sock_t *ssock, circ_buf_t *cb,
+		    pj_uint8_t *dst, pj_size_t len)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    char *data;
+
+    BIO_get_mem_data(ossock->ossl_wbio, &data);
+    pj_memcpy(dst, data, len);
+
+    /* Reset write BIO */
+    (void)BIO_reset(ossock->ossl_wbio);
+}
+
+static pj_status_t io_write(pj_ssl_sock_t *ssock, circ_buf_t *cb,
+                            const pj_uint8_t *src, pj_size_t len)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    int nwritten;
+
+    nwritten = BIO_write(ossock->ossl_rbio, src, (int)len);
+    return (nwritten < (int)len)? GET_SSL_STATUS(cb->owner): PJ_SUCCESS;
+}
+
+/*
+ *******************************************************************
+ */
+
 /* OpenSSL library initialization counter */
 static int openssl_init_count;
-
-/* OpenSSL available ciphers */
-static unsigned openssl_cipher_num;
-static struct openssl_ciphers_t {
-    pj_ssl_cipher    id;
-    const char	    *name;
-} openssl_ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
-
-/* OpenSSL available curves */
-static unsigned openssl_curves_num;
-static struct openssl_curves_t {
-    pj_ssl_curve    id;
-    const char	    *name;
-} openssl_curves[PJ_SSL_SOCK_MAX_CURVES];
 
 /* OpenSSL application data index */
@@ -570,5 +465,5 @@
 
     /* Init available ciphers */
-    if (openssl_cipher_num == 0 || openssl_curves_num == 0) {
+    if (ssl_cipher_num == 0 || ssl_curves_num == 0) {
 	SSL_METHOD *meth = NULL;
 	SSL_CTX *ctx;
@@ -609,16 +504,16 @@
 
 	n = sk_SSL_CIPHER_num(sk_cipher);
-	if (n > PJ_ARRAY_SIZE(openssl_ciphers))
-	    n = PJ_ARRAY_SIZE(openssl_ciphers);
+	if (n > PJ_ARRAY_SIZE(ssl_ciphers))
+	    n = PJ_ARRAY_SIZE(ssl_ciphers);
 
 	for (i = 0; i < n; ++i) {
 	    const SSL_CIPHER *c;
 	    c = sk_SSL_CIPHER_value(sk_cipher,i);
-	    openssl_ciphers[i].id = (pj_ssl_cipher)
+	    ssl_ciphers[i].id = (pj_ssl_cipher)
 				    (pj_uint32_t)SSL_CIPHER_get_id(c) &
 				    0x00FFFFFF;
-	    openssl_ciphers[i].name = SSL_CIPHER_get_name(c);
-	}
-	openssl_cipher_num = n;
+	    ssl_ciphers[i].name = SSL_CIPHER_get_name(c);
+	}
+	ssl_cipher_num = n;
 
 	SSL_set_session(ssl, SSL_SESSION_new());
@@ -626,9 +521,9 @@
 #if !USING_LIBRESSL && !defined(OPENSSL_NO_EC) \
     && OPENSSL_VERSION_NUMBER >= 0x1000200fL
-	openssl_curves_num = SSL_get_shared_curve(ssl,-1);
-	if (openssl_curves_num > PJ_ARRAY_SIZE(openssl_curves))
-	    openssl_curves_num = PJ_ARRAY_SIZE(openssl_curves);
-
-	for (i = 0; i < openssl_curves_num; i++) {
+	ssl_curves_num = SSL_get_shared_curve(ssl,-1);
+	if (ssl_curves_num > PJ_ARRAY_SIZE(ssl_curves))
+	    ssl_curves_num = PJ_ARRAY_SIZE(ssl_curves);
+
+	for (i = 0; i < ssl_curves_num; i++) {
 	    nid = SSL_get_shared_curve(ssl, i);
 
@@ -642,11 +537,11 @@
 	    }
 
-	    openssl_curves[i].id   = get_cid_from_nid(nid);
-	    openssl_curves[i].name = cname;
+	    ssl_curves[i].id   = get_cid_from_nid(nid);
+	    ssl_curves[i].name = cname;
 	}
 #else
 	PJ_UNUSED_ARG(nid);
 	PJ_UNUSED_ARG(cname);
-	openssl_curves_num = 0;
+	ssl_curves_num = 0;
 #endif
 
@@ -788,7 +683,15 @@
 static void set_entropy(pj_ssl_sock_t *ssock);
 
+
+static pj_ssl_sock_t *ssl_alloc(pj_pool_t *pool)
+{
+    return (pj_ssl_sock_t *)PJ_POOL_ZALLOC_T(pool, ossl_sock_t);
+}
+
+
 /* Create and initialize new SSL context and instance */
-static pj_status_t create_ssl(pj_ssl_sock_t *ssock)
-{
+static pj_status_t ssl_create(pj_ssl_sock_t *ssock)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
 #if !defined(OPENSSL_NO_DH)
     BIO *bio;
@@ -1142,12 +1045,12 @@
 
     /* Create SSL instance */
-    ssock->ossl_ctx = ctx;
-    ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
-    if (ssock->ossl_ssl == NULL) {
+    ossock->ossl_ctx = ctx;
+    ossock->ossl_ssl = SSL_new(ossock->ossl_ctx);
+    if (ossock->ossl_ssl == NULL) {
 	return GET_SSL_STATUS(ssock);
     }
 
     /* Set SSL sock as application data of SSL instance */
-    SSL_set_ex_data(ssock->ossl_ssl, sslsock_idx, ssock);
+    SSL_set_ex_data(ossock->ossl_ssl, sslsock_idx, ssock);
 
     /* SSL verification options */
@@ -1156,5 +1059,5 @@
 	mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
 
-    SSL_set_verify(ssock->ossl_ssl, mode, &verify_cb);
+    SSL_set_verify(ossock->ossl_ssl, mode, &verify_cb);
 
     /* Set cipher list */
@@ -1174,9 +1077,9 @@
 
     /* Setup SSL BIOs */
-    ssock->ossl_rbio = BIO_new(BIO_s_mem());
-    ssock->ossl_wbio = BIO_new(BIO_s_mem());
-    (void)BIO_set_close(ssock->ossl_rbio, BIO_CLOSE);
-    (void)BIO_set_close(ssock->ossl_wbio, BIO_CLOSE);
-    SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio);
+    ossock->ossl_rbio = BIO_new(BIO_s_mem());
+    ossock->ossl_wbio = BIO_new(BIO_s_mem());
+    (void)BIO_set_close(ossock->ossl_rbio, BIO_CLOSE);
+    (void)BIO_set_close(ossock->ossl_wbio, BIO_CLOSE);
+    SSL_set_bio(ossock->ossl_ssl, ossock->ossl_rbio, ossock->ossl_wbio);
 
     return PJ_SUCCESS;
@@ -1185,8 +1088,10 @@
 
 /* Destroy SSL context and instance */
-static void destroy_ssl(pj_ssl_sock_t *ssock)
-{
+static void ssl_destroy(pj_ssl_sock_t *ssock)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+
     /* Destroy SSL instance */
-    if (ssock->ossl_ssl) {
+    if (ossock->ossl_ssl) {
 	/**
 	 * Avoid calling SSL_shutdown() if handshake wasn't completed.
@@ -1194,15 +1099,15 @@
 	 * SSL handshake, while previous versions always return 0.	 
 	 */
-	if (SSL_in_init(ssock->ossl_ssl) == 0) {
-	    SSL_shutdown(ssock->ossl_ssl);
+	if (SSL_in_init(ossock->ossl_ssl) == 0) {
+	    SSL_shutdown(ossock->ossl_ssl);
 	}   	
-	SSL_free(ssock->ossl_ssl); /* this will also close BIOs */
-	ssock->ossl_ssl = NULL;
+	SSL_free(ossock->ossl_ssl); /* this will also close BIOs */
+	ossock->ossl_ssl = NULL;
     }
 
     /* Destroy SSL context */
-    if (ssock->ossl_ctx) {
-	SSL_CTX_free(ssock->ossl_ctx);
-	ssock->ossl_ctx = NULL;
+    if (ossock->ossl_ctx) {
+	SSL_CTX_free(ossock->ossl_ctx);
+	ossock->ossl_ctx = NULL;
     }
 
@@ -1214,38 +1119,6 @@
 
 
-/* Close sockets */
-static void close_sockets(pj_ssl_sock_t *ssock)
-{
-    pj_activesock_t *asock;
-    pj_sock_t sock;
-
-    /* This can happen when pj_ssl_sock_create() fails. */
-    if (!ssock->write_mutex)
-    	return;
-
-    pj_lock_acquire(ssock->write_mutex);
-    asock = ssock->asock;
-    if (asock) {
-        // Don't set ssock->asock to NULL, as it may trigger assertion in
-        // send operation. This should be safe as active socket will simply
-        // return PJ_EINVALIDOP on any operation if it is already closed.
-        //ssock->asock = NULL;
-        ssock->sock = PJ_INVALID_SOCKET;
-    }
-    sock = ssock->sock;
-    if (sock != PJ_INVALID_SOCKET)
-        ssock->sock = PJ_INVALID_SOCKET;
-    pj_lock_release(ssock->write_mutex);
-
-    if (asock)
-        pj_activesock_close(asock);
-
-    if (sock != PJ_INVALID_SOCKET)
-        pj_sock_close(sock);
-}
-
-
 /* Reset SSL socket state */
-static void reset_ssl_sock_state(pj_ssl_sock_t *ssock)
+static void ssl_reset_sock_state(pj_ssl_sock_t *ssock)
 {
     pj_lock_acquire(ssock->write_mutex);
@@ -1253,5 +1126,5 @@
     pj_lock_release(ssock->write_mutex);
 
-    close_sockets(ssock);
+    ssl_close_sockets(ssock);
 
     /* Upon error, OpenSSL may leave any error description in the thread 
@@ -1265,7 +1138,30 @@
 
 
+static void ssl_ciphers_populate()
+{
+    if (ssl_cipher_num == 0 || ssl_curves_num == 0) {
+	init_openssl();
+	shutdown_openssl();
+    }
+}
+
+static pj_ssl_cipher ssl_get_cipher(pj_ssl_sock_t *ssock)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    const SSL_CIPHER *cipher;
+
+    /* Current cipher */
+    cipher = SSL_get_current_cipher(ossock->ossl_ssl);
+    if (cipher) {
+	return (SSL_CIPHER_get_id(cipher) & 0x00FFFFFF);
+    } else {
+	return PJ_TLS_UNKNOWN_CIPHER;
+    }
+}
+
 /* Generate cipher list with user preference order in OpenSSL format */
 static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock)
 {
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
     pj_pool_t *tmp_pool = NULL;
     char *buf = NULL;
@@ -1277,5 +1173,5 @@
 
     if (ssock->param.ciphers_num == 0) {
-	ret = SSL_set_cipher_list(ssock->ossl_ssl, PJ_SSL_SOCK_OSSL_CIPHERS);
+	ret = SSL_set_cipher_list(ossock->ossl_ssl, PJ_SSL_SOCK_OSSL_CIPHERS);
     	if (ret < 1) {
 	    return GET_SSL_STATUS(ssock);
@@ -1296,8 +1192,8 @@
 
     /* Set SSL with ALL available ciphers */
-    SSL_set_cipher_list(ssock->ossl_ssl, "ALL:COMPLEMENTOFALL");
+    SSL_set_cipher_list(ossock->ossl_ssl, "ALL:COMPLEMENTOFALL");
 
     /* Generate user specified cipher list in OpenSSL format */
-    sk_cipher = SSL_get_ciphers(ssock->ossl_ssl);
+    sk_cipher = SSL_get_ciphers(ossock->ossl_ssl);
     for (i = 0; i < ssock->param.ciphers_num; ++i) {
 	for (j = 0; j < sk_SSL_CIPHER_num(sk_cipher); ++j) {
@@ -1335,5 +1231,5 @@
 
     /* Finally, set chosen cipher list */
-    ret = SSL_set_cipher_list(ssock->ossl_ssl, buf);
+    ret = SSL_set_cipher_list(ossock->ossl_ssl, buf);
     if (ret < 1) {
 	pj_pool_release(tmp_pool);
@@ -1349,4 +1245,5 @@
 #if !USING_LIBRESSL && !defined(OPENSSL_NO_EC) \
     && OPENSSL_VERSION_NUMBER >= 0x1000200fL
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
     int ret;
     int curves[PJ_SSL_SOCK_MAX_CURVES];
@@ -1360,11 +1257,11 @@
     }
 
-    if( SSL_is_server(ssock->ossl_ssl) ) {
-	ret = SSL_set1_curves(ssock->ossl_ssl, curves,
+    if( SSL_is_server(ossock->ossl_ssl) ) {
+	ret = SSL_set1_curves(ossock->ossl_ssl, curves,
 			      ssock->param.curves_num);
 	if (ret < 1)
 	    return GET_SSL_STATUS(ssock);
     } else {
-	ret = SSL_CTX_set1_curves(ssock->ossl_ctx, curves,
+	ret = SSL_CTX_set1_curves(ossock->ossl_ctx, curves,
 				  ssock->param.curves_num);
 	if (ret < 1)
@@ -1380,12 +1277,13 @@
 {
 #if !USING_LIBRESSL && OPENSSL_VERSION_NUMBER >= 0x1000200fL
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
     int ret;
 
     if (ssock->param.sigalgs.ptr && ssock->param.sigalgs.slen) {
 	if (ssock->is_server) {
-	    ret = SSL_set1_client_sigalgs_list(ssock->ossl_ssl,
+	    ret = SSL_set1_client_sigalgs_list(ossock->ossl_ssl,
 	    				       ssock->param.sigalgs.ptr);
 	} else {
-	    ret = SSL_set1_sigalgs_list(ssock->ossl_ssl,
+	    ret = SSL_set1_sigalgs_list(ossock->ossl_ssl,
 	    				ssock->param.sigalgs.ptr);
 	}
@@ -1654,6 +1552,7 @@
  * called after handshake or renegotiation successfully completed.
  */
-static void update_certs_info(pj_ssl_sock_t *ssock)
-{
+static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
     X509 *x;
 
@@ -1661,5 +1560,5 @@
 
     /* Active local certificate */
-    x = SSL_get_certificate(ssock->ossl_ssl);
+    x = SSL_get_certificate(ossock->ossl_ssl);
     if (x) {
 	get_cert_info(ssock->pool, &ssock->local_cert_info, x, PJ_FALSE);
@@ -1670,5 +1569,5 @@
 
     /* Active remote certificate */
-    x = SSL_get_peer_certificate(ssock->ossl_ssl);
+    x = SSL_get_peer_certificate(ossock->ossl_ssl);
     if (x) {
 	get_cert_info(ssock->pool, &ssock->remote_cert_info, x, PJ_TRUE);
@@ -1681,299 +1580,4 @@
 
 
-/* When handshake completed:
- * - notify application
- * - if handshake failed, reset SSL state
- * - return PJ_FALSE when SSL socket instance is destroyed by application.
- */
-static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, 
-				       pj_status_t status)
-{
-    /* Cancel handshake timer */
-    if (ssock->timer.id == TIMER_HANDSHAKE_TIMEOUT) {
-	pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
-	ssock->timer.id = TIMER_NONE;
-    }
-
-    /* Update certificates info on successful handshake */
-    if (status == PJ_SUCCESS)
-	update_certs_info(ssock);
-
-    /* Accepting */
-    if (ssock->is_server) {
-	if (status != PJ_SUCCESS) {
-	    /* Handshake failed in accepting, destroy our self silently. */
-
-	    char errmsg[PJ_ERR_MSG_SIZE];
-	    char buf[PJ_INET6_ADDRSTRLEN+10];
-
-	    pj_strerror(status, errmsg, sizeof(errmsg));
-	    PJ_LOG(3,(ssock->pool->obj_name, "Handshake failed in accepting "
-		      "%s: %s",
-		      pj_sockaddr_print(&ssock->rem_addr, buf, sizeof(buf), 3),
-		      errmsg));
-
-	    if (ssock->param.cb.on_accept_complete2) {
-		(*ssock->param.cb.on_accept_complete2) 
-		      (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, 
-		      pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr), 
-		      status);
-	    }
-
-	    /* Originally, this is a workaround for ticket #985. However,
-	     * a race condition may occur in multiple worker threads
-	     * environment when we are destroying SSL objects while other
-	     * threads are still accessing them.
-	     * Please see ticket #1930 for more info.
-	     */
-#if 1 //(defined(PJ_WIN32) && PJ_WIN32!=0)||(defined(PJ_WIN64) && PJ_WIN64!=0)
-	    if (ssock->param.timer_heap) {
-		pj_time_val interval = {0, PJ_SSL_SOCK_DELAYED_CLOSE_TIMEOUT};
-
-		ssock->ssl_state = SSL_STATE_NULL;
-		close_sockets(ssock);
-
-		if (ssock->timer.id != TIMER_NONE) {
-		    pj_timer_heap_cancel(ssock->param.timer_heap,
-					 &ssock->timer);
-		}
-		ssock->timer.id = TIMER_CLOSE;
-		pj_time_val_normalize(&interval);
-		if (pj_timer_heap_schedule(ssock->param.timer_heap, 
-					   &ssock->timer, &interval) != 0)
-		{
-	    	    PJ_LOG(3,(ssock->pool->obj_name, "Failed to schedule "
-		      	      "a delayed close. Race condition may occur."));
-		    ssock->timer.id = TIMER_NONE;
-		    pj_ssl_sock_close(ssock);
-		}
-	    } else {
-		pj_ssl_sock_close(ssock);
-	    }
-#else
-	    {
-		pj_ssl_sock_close(ssock);
-	    }
-#endif
-
-	    return PJ_FALSE;
-	}
-	/* Notify application the newly accepted SSL socket */
-	if (ssock->param.cb.on_accept_complete2) {
-	    pj_bool_t ret;
-	    ret = (*ssock->param.cb.on_accept_complete2) 
-		    (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, 
-		    pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr), 
-		    status);
-	    if (ret == PJ_FALSE)
-		return PJ_FALSE;	
-	} else if (ssock->param.cb.on_accept_complete) {
-	    pj_bool_t ret;
-	    ret = (*ssock->param.cb.on_accept_complete)
-		      (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr,
-		       pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr));
-	    if (ret == PJ_FALSE)
-		return PJ_FALSE;
-	}
-    }
-
-    /* Connecting */
-    else {
-	/* On failure, reset SSL socket state first, as app may try to 
-	 * reconnect in the callback.
-	 */
-	if (status != PJ_SUCCESS) {
-	    /* Server disconnected us, possibly due to SSL nego failure */
-	    if (status == PJ_EEOF) {
-		unsigned long err;
-		err = ERR_get_error();
-		if (err != SSL_ERROR_NONE)
-		    status = STATUS_FROM_SSL_ERR("connecting", ssock, err);
-	    }
-	    reset_ssl_sock_state(ssock);
-	}
-	if (ssock->param.cb.on_connect_complete) {
-	    pj_bool_t ret;
-	    ret = (*ssock->param.cb.on_connect_complete)(ssock, status);
-	    if (ret == PJ_FALSE)
-		return PJ_FALSE;
-	}
-    }
-
-    return PJ_TRUE;
-}
-
-static write_data_t* alloc_send_data(pj_ssl_sock_t *ssock, pj_size_t len)
-{
-    send_buf_t *send_buf = &ssock->send_buf;
-    pj_size_t avail_len, skipped_len = 0;
-    char *reg1, *reg2;
-    pj_size_t reg1_len, reg2_len;
-    write_data_t *p;
-
-    /* Check buffer availability */
-    avail_len = send_buf->max_len - send_buf->len;
-    if (avail_len < len)
-	return NULL;
-
-    /* If buffer empty, reset start pointer and return it */
-    if (send_buf->len == 0) {
-	send_buf->start = send_buf->buf;
-	send_buf->len   = len;
-	p = (write_data_t*)send_buf->start;
-	goto init_send_data;
-    }
-
-    /* Free space may be wrapped/splitted into two regions, so let's
-     * analyze them if any region can hold the write data.
-     */
-    reg1 = send_buf->start + send_buf->len;
-    if (reg1 >= send_buf->buf + send_buf->max_len)
-	reg1 -= send_buf->max_len;
-    reg1_len = send_buf->max_len - send_buf->len;
-    if (reg1 + reg1_len > send_buf->buf + send_buf->max_len) {
-	reg1_len = send_buf->buf + send_buf->max_len - reg1;
-	reg2 = send_buf->buf;
-	reg2_len = send_buf->start - send_buf->buf;
-    } else {
-	reg2 = NULL;
-	reg2_len = 0;
-    }
-
-    /* More buffer availability check, note that the write data must be in
-     * a contigue buffer.
-     */
-    avail_len = PJ_MAX(reg1_len, reg2_len);
-    if (avail_len < len)
-	return NULL;
-
-    /* Get the data slot */
-    if (reg1_len >= len) {
-	p = (write_data_t*)reg1;
-    } else {
-	p = (write_data_t*)reg2;
-	skipped_len = reg1_len;
-    }
-
-    /* Update buffer length */
-    send_buf->len += len + skipped_len;
-
-init_send_data:
-    /* Init the new send data */
-    pj_bzero(p, sizeof(*p));
-    pj_list_init(p);
-    pj_list_push_back(&ssock->send_pending, p);
-
-    return p;
-}
-
-static void free_send_data(pj_ssl_sock_t *ssock, write_data_t *wdata)
-{
-    send_buf_t *buf = &ssock->send_buf;
-    write_data_t *spl = &ssock->send_pending;
-
-    pj_assert(!pj_list_empty(&ssock->send_pending));
-    
-    /* Free slot from the buffer */
-    if (spl->next == wdata && spl->prev == wdata) {
-	/* This is the only data, reset the buffer */
-	buf->start = buf->buf;
-	buf->len = 0;
-    } else if (spl->next == wdata) {
-	/* This is the first data, shift start pointer of the buffer and
-	 * adjust the buffer length.
-	 */
-	buf->start = (char*)wdata->next;
-	if (wdata->next > wdata) {
-	    buf->len -= ((char*)wdata->next - buf->start);
-	} else {
-	    /* Overlapped */
-	    pj_size_t right_len, left_len;
-	    right_len = buf->buf + buf->max_len - (char*)wdata;
-	    left_len  = (char*)wdata->next - buf->buf;
-	    buf->len -= (right_len + left_len);
-	}
-    } else if (spl->prev == wdata) {
-	/* This is the last data, just adjust the buffer length */
-	if (wdata->prev < wdata) {
-	    pj_size_t jump_len;
-	    jump_len = (char*)wdata -
-		       ((char*)wdata->prev + wdata->prev->record_len);
-	    buf->len -= (wdata->record_len + jump_len);
-	} else {
-	    /* Overlapped */
-	    pj_size_t right_len, left_len;
-	    right_len = buf->buf + buf->max_len -
-			((char*)wdata->prev + wdata->prev->record_len);
-	    left_len  = (char*)wdata + wdata->record_len - buf->buf;
-	    buf->len -= (right_len + left_len);
-	}
-    }
-    /* For data in the middle buffer, just do nothing on the buffer. The slot
-     * will be freed later when freeing the first/last data.
-     */
-    
-    /* Remove the data from send pending list */
-    pj_list_erase(wdata);
-}
-
-#if 0
-/* Just for testing send buffer alloc/free */
-#include <pj/rand.h>
-pj_status_t pj_ssl_sock_ossl_test_send_buf(pj_pool_t *pool)
-{
-    enum { MAX_CHUNK_NUM = 20 };
-    unsigned chunk_size, chunk_cnt, i;
-    write_data_t *wdata[MAX_CHUNK_NUM] = {0};
-    pj_time_val now;
-    pj_ssl_sock_t *ssock = NULL;
-    pj_ssl_sock_param param;
-    pj_status_t status;
-
-    pj_gettimeofday(&now);
-    pj_srand((unsigned)now.sec);
-
-    pj_ssl_sock_param_default(&param);
-    status = pj_ssl_sock_create(pool, &param, &ssock);
-    if (status != PJ_SUCCESS) {
-	return status;
-    }
-
-    if (ssock->send_buf.max_len == 0) {
-	ssock->send_buf.buf = (char*)
-			      pj_pool_alloc(ssock->pool, 
-					    ssock->param.send_buffer_size);
-	ssock->send_buf.max_len = ssock->param.send_buffer_size;
-	ssock->send_buf.start = ssock->send_buf.buf;
-	ssock->send_buf.len = 0;
-    }
-
-    chunk_size = ssock->param.send_buffer_size / MAX_CHUNK_NUM / 2;
-    chunk_cnt = 0;
-    for (i = 0; i < MAX_CHUNK_NUM; i++) {
-	wdata[i] = alloc_send_data(ssock, pj_rand() % chunk_size + 321);
-	if (wdata[i])
-	    chunk_cnt++;
-	else
-	    break;
-    }
-
-    while (chunk_cnt) {
-	i = pj_rand() % MAX_CHUNK_NUM;
-	if (wdata[i]) {
-	    free_send_data(ssock, wdata[i]);
-	    wdata[i] = NULL;
-	    chunk_cnt--;
-	}
-    }
-
-    if (ssock->send_buf.len != 0)
-	status = PJ_EBUG;
-
-    pj_ssl_sock_close(ssock);
-    return status;
-}
-#endif
-
-
 /* Flush write BIO to network socket. Note that any access to write BIO
  * MUST be serialized, so mutex protection must cover any call to OpenSSL
@@ -1982,119 +1586,46 @@
  * OpenSSL API call).
  */
-static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock, 
-				   pj_ioqueue_op_key_t *send_key,
-				   pj_size_t orig_len,
-				   unsigned flags)
-{
-    char *data;
-    pj_ssize_t len;
-    write_data_t *wdata;
-    pj_size_t needed_len;
-    pj_status_t status;
-
-    pj_lock_acquire(ssock->write_mutex);
-
-    /* Check if there is data in write BIO, flush it if any */
-    if (!BIO_pending(ssock->ossl_wbio)) {
-	pj_lock_release(ssock->write_mutex);
-	return PJ_SUCCESS;
-    }
-
-    /* Get data and its length */
-    len = BIO_get_mem_data(ssock->ossl_wbio, &data);
-    if (len == 0) {
-	pj_lock_release(ssock->write_mutex);
-	return PJ_SUCCESS;
-    }
-
-    /* Calculate buffer size needed, and align it to 8 */
-    needed_len = len + sizeof(write_data_t);
-    needed_len = ((needed_len + 7) >> 3) << 3;
-
-    /* Allocate buffer for send data */
-    wdata = alloc_send_data(ssock, needed_len);
-    if (wdata == NULL) {
-	/* Oops, write BIO is ready but the send buffer is full, let's just
-	 * queue it for sending and return PJ_EPENDING.
-	 */
-	ssock->send_buf_pending.data_len = needed_len;
-	ssock->send_buf_pending.app_key = send_key;
-	ssock->send_buf_pending.flags = flags;
-	ssock->send_buf_pending.plain_data_len = orig_len;
-	pj_lock_release(ssock->write_mutex);
-	return PJ_EPENDING;
-    }
-
-    /* Copy the data and set its properties into the send data */
-    pj_ioqueue_op_key_init(&wdata->key, sizeof(pj_ioqueue_op_key_t));
-    wdata->key.user_data = wdata;
-    wdata->app_key = send_key;
-    wdata->record_len = needed_len;
-    wdata->data_len = len;
-    wdata->plain_data_len = orig_len;
-    wdata->flags = flags;
-    pj_memcpy(&wdata->data, data, len);
-
-    /* Reset write BIO */
-    (void)BIO_reset(ssock->ossl_wbio);
-
-    /* Ticket #1573: Don't hold mutex while calling PJLIB socket send(). */
-    pj_lock_release(ssock->write_mutex);
-
-    /* Send it */
-    if (ssock->param.sock_type == pj_SOCK_STREAM()) {
-	status = pj_activesock_send(ssock->asock, &wdata->key, 
-				    wdata->data.content, &len,
-				    flags);
+static pj_status_t flush_circ_buf_output(pj_ssl_sock_t *ssock,
+                                         pj_ioqueue_op_key_t *send_key,
+                                         pj_size_t orig_len, unsigned flags);
+
+
+static void ssl_set_state(pj_ssl_sock_t *ssock, pj_bool_t is_server)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    if (is_server) {
+        SSL_set_accept_state(ossock->ossl_ssl);
     } else {
-	status = pj_activesock_sendto(ssock->asock, &wdata->key, 
-				      wdata->data.content, &len,
-				      flags,
-				      (pj_sockaddr_t*)&ssock->rem_addr,
-				      ssock->addr_len);
-    }
-
-    if (status != PJ_EPENDING) {
-	/* When the sending is not pending, remove the wdata from send
-	 * pending list.
-	 */
-	pj_lock_acquire(ssock->write_mutex);
-	free_send_data(ssock, wdata);
-	pj_lock_release(ssock->write_mutex);
-    }
-
-    return status;
-}
-
-
-static void on_timer(pj_timer_heap_t *th, struct pj_timer_entry *te)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)te->user_data;
-    int timer_id = te->id;
-
-    te->id = TIMER_NONE;
-
-    PJ_UNUSED_ARG(th);
-
-    switch (timer_id) {
-    case TIMER_HANDSHAKE_TIMEOUT:
-	PJ_LOG(1,(ssock->pool->obj_name, "SSL timeout after %d.%ds",
-		  ssock->param.timeout.sec, ssock->param.timeout.msec));
-
-	on_handshake_complete(ssock, PJ_ETIMEDOUT);
-	break;
-    case TIMER_CLOSE:
-	pj_ssl_sock_close(ssock);
-	break;
-    default:
-	pj_assert(!"Unknown timer");
-	break;
-    }
+	SSL_set_connect_state(ossock->ossl_ssl);
+    }
+}
+
+
+static void ssl_set_peer_name(pj_ssl_sock_t *ssock)
+{
+#ifdef SSL_set_tlsext_host_name
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+
+    /* Set server name to connect */
+    if (ssock->param.server_name.slen) {
+	/* Server name is null terminated already */
+	if (!SSL_set_tlsext_host_name(ossock->ossl_ssl, 
+				      ssock->param.server_name.ptr))
+	{
+	    char err_str[PJ_ERR_MSG_SIZE];
+
+	    ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str));
+	    PJ_LOG(3,(ssock->pool->obj_name, "SSL_set_tlsext_host_name() "
+		"failed: %s", err_str));
+	}
+    }
+#endif
 }
 
 
 /* Asynchronouse handshake */
-static pj_status_t do_handshake(pj_ssl_sock_t *ssock)
-{
+static pj_status_t ssl_do_handshake(pj_ssl_sock_t *ssock)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
     pj_status_t status;
     int err;
@@ -2102,5 +1633,5 @@
     /* Perform SSL handshake */
     pj_lock_acquire(ssock->write_mutex);
-    err = SSL_do_handshake(ssock->ossl_ssl);
+    err = SSL_do_handshake(ossock->ossl_ssl);
     pj_lock_release(ssock->write_mutex);
 
@@ -2108,5 +1639,5 @@
      * flush it if any.
      */
-    status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
+    status = flush_circ_buf_output(ssock, &ssock->handshake_op_key, 0, 0);
     if (status != PJ_SUCCESS && status != PJ_EPENDING) {
 	return status;
@@ -2114,5 +1645,5 @@
 
     if (err < 0) {
-	int err2 = SSL_get_error(ssock->ossl_ssl, err);
+	int err2 = SSL_get_error(ossock->ossl_ssl, err);
 	if (err2 != SSL_ERROR_NONE && err2 != SSL_ERROR_WANT_READ)
 	{
@@ -2124,5 +1655,5 @@
 
     /* Check if handshake has been completed */
-    if (SSL_is_init_finished(ssock->ossl_ssl)) {
+    if (SSL_is_init_finished(ossock->ossl_ssl)) {
 	ssock->ssl_state = SSL_STATE_ESTABLISHED;
 	return PJ_SUCCESS;
@@ -2132,1093 +1663,75 @@
 }
 
-static void ssl_on_destroy(void *arg)
-{
-    pj_pool_t *pool = NULL;
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)arg;
-
-    destroy_ssl(ssock);
-
-    pj_lock_destroy(ssock->write_mutex);
-
-    pool = ssock->pool;
-    ssock->pool = NULL;
-    if (pool)
-	pj_pool_release(pool);
-}
-
-
-/*
- *******************************************************************
- * Active socket callbacks.
- *******************************************************************
- */
-
-static pj_bool_t asock_on_data_read (pj_activesock_t *asock,
-				     void *data,
-				     pj_size_t size,
-				     pj_status_t status,
-				     pj_size_t *remainder)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
-			   pj_activesock_get_user_data(asock);
-    pj_size_t nwritten;
-
-    /* Socket error or closed */
-    if (data && size > 0) {
-	/* Consume the whole data */
-	nwritten = BIO_write(ssock->ossl_rbio, data, (int)size);
-	if (nwritten < size) {
-	    status = GET_SSL_STATUS(ssock);
-	    goto on_error;
-	}
-    }
-
-    /* Check if SSL handshake hasn't finished yet */
-    if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
-	pj_bool_t ret = PJ_TRUE;
-
-	if (status == PJ_SUCCESS)
-	    status = do_handshake(ssock);
-
-	/* Not pending is either success or failed */
-	if (status != PJ_EPENDING)
-	    ret = on_handshake_complete(ssock, status);
-
-	return ret;
-    }
-
-    /* See if there is any decrypted data for the application */
-    if (ssock->read_started) {
-	do {
-	    read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data));
-	    void *data_ = (pj_int8_t*)buf->data + buf->len;
-	    int size_ = (int)(ssock->read_size - buf->len);
-	    int len = size_;
-
-	    /* SSL_read() may write some data to BIO write when re-negotiation
-	     * is on progress, so let's protect it with write mutex.
-	     */
-	    pj_lock_acquire(ssock->write_mutex);
-	    size_ = SSL_read(ssock->ossl_ssl, data_, size_);
-	    pj_lock_release(ssock->write_mutex);
-
-	    if (size_ > 0 || status != PJ_SUCCESS) {
-		if (ssock->param.cb.on_data_read) {
-		    pj_bool_t ret;
-		    pj_size_t remainder_ = 0;
-
-		    if (size_ > 0)
-			buf->len += size_;
-    		
-                    if (status != PJ_SUCCESS) {
-                        ssock->ssl_state = SSL_STATE_ERROR;
-                    }
-
-		    ret = (*ssock->param.cb.on_data_read)(ssock, buf->data,
-							  buf->len, status,
-							  &remainder_);
-		    if (!ret) {
-			/* We've been destroyed */
-			return PJ_FALSE;
-		    }
-
-		    /* Application may have left some data to be consumed 
-		     * later.
-		     */
-		    buf->len = remainder_;
-		}
-
-		/* Active socket signalled connection closed/error, this has
-		 * been signalled to the application along with any remaining
-		 * buffer. So, let's just reset SSL socket now.
-		 */
-		if (status != PJ_SUCCESS) {
-		    reset_ssl_sock_state(ssock);
-		    return PJ_FALSE;
-		}
-
-	    } else {
-
-		int err = SSL_get_error(ssock->ossl_ssl, size_);
-		
-		/* SSL might just return SSL_ERROR_WANT_READ in 
-		 * re-negotiation.
-		 */
-		if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ)
-		{
-		    if (err == SSL_ERROR_SYSCALL && size_ == -1 &&
-			ERR_peek_error() == 0 && errno == 0)
-		    {
-			status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
-						      err, len);
-			PJ_LOG(4,("SSL", "SSL_read() = -1, with "
-				  	 "SSL_ERROR_SYSCALL, no SSL error, "
-				  	 "and errno = 0 - skip BIO error"));
-		        /* Ignore these errors */
-		    } else {
-		        /* Reset SSL socket state, then return PJ_FALSE */
-		        status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
-		        			      err, len);
-		        reset_ssl_sock_state(ssock);
-		        goto on_error;
-		    }
-		}
-
-		status = do_handshake(ssock);
-		if (status == PJ_SUCCESS) {
-		    /* Renegotiation completed */
-
-		    /* Update certificates */
-		    update_certs_info(ssock);
-
-		    // Ticket #1573: Don't hold mutex while calling
-		    //               PJLIB socket send(). 
-		    //pj_lock_acquire(ssock->write_mutex);
-		    status = flush_delayed_send(ssock);
-		    //pj_lock_release(ssock->write_mutex);
-
-		    /* If flushing is ongoing, treat it as success */
-		    if (status == PJ_EBUSY)
-			status = PJ_SUCCESS;
-
-		    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
-			PJ_PERROR(1,(ssock->pool->obj_name, status, 
-				     "Failed to flush delayed send"));
-			goto on_error;
-		    }
-		} else if (status != PJ_EPENDING) {
-		    PJ_PERROR(1,(ssock->pool->obj_name, status, 
-			         "Renegotiation failed"));
-		    goto on_error;
-		}
-
-		break;
-	    }
-	} while (1);
-    }
-
-    return PJ_TRUE;
-
-on_error:
-    if (ssock->ssl_state == SSL_STATE_HANDSHAKING)
-	return on_handshake_complete(ssock, status);
-
-    if (ssock->read_started && ssock->param.cb.on_data_read) {
-	pj_bool_t ret;
-	ret = (*ssock->param.cb.on_data_read)(ssock, NULL, 0, status,
-					      remainder);
-	if (!ret) {
-	    /* We've been destroyed */
-	    return PJ_FALSE;
-	}
-    }
-
-    reset_ssl_sock_state(ssock);
-    return PJ_FALSE;
-}
-
-
-static pj_bool_t asock_on_data_sent (pj_activesock_t *asock,
-				     pj_ioqueue_op_key_t *send_key,
-				     pj_ssize_t sent)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
-			   pj_activesock_get_user_data(asock);
-    write_data_t *wdata = (write_data_t*)send_key->user_data;
-    pj_ioqueue_op_key_t *app_key = wdata->app_key;
-    pj_ssize_t sent_len;
-
-    sent_len = (sent > 0)? wdata->plain_data_len : sent;
-    
-    /* Update write buffer state */
-    pj_lock_acquire(ssock->write_mutex);
-    free_send_data(ssock, wdata);
-    pj_lock_release(ssock->write_mutex);
-    wdata = NULL;
-
-    if (ssock->ssl_state == SSL_STATE_HANDSHAKING) {
-	/* Initial handshaking */
-	pj_status_t status;
-	
-	status = do_handshake(ssock);
-	/* Not pending is either success or failed */
-	if (status != PJ_EPENDING)
-	    return on_handshake_complete(ssock, status);
-
-    } else if (send_key != &ssock->handshake_op_key) {
-	/* Some data has been sent, notify application */
-	if (ssock->param.cb.on_data_sent) {
-	    pj_bool_t ret;
-	    ret = (*ssock->param.cb.on_data_sent)(ssock, app_key, 
-						  sent_len);
-	    if (!ret) {
-		/* We've been destroyed */
-		return PJ_FALSE;
-	    }
-	}
-    } else {
-	/* SSL re-negotiation is on-progress, just do nothing */
-    }
-
-    /* Send buffer has been updated, let's try to send any pending data */
-    if (ssock->send_buf_pending.data_len) {
-	pj_status_t status;
-	status = flush_write_bio(ssock, ssock->send_buf_pending.app_key,
-				 ssock->send_buf_pending.plain_data_len,
-				 ssock->send_buf_pending.flags);
-	if (status == PJ_SUCCESS || status == PJ_EPENDING) {
-	    ssock->send_buf_pending.data_len = 0;
-	}
-    }
-
-    return PJ_TRUE;
-}
-
-
-static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
-					   pj_sock_t newsock,
-					   const pj_sockaddr_t *src_addr,
-					   int src_addr_len)
-{
-    pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t*)
-				  pj_activesock_get_user_data(asock);
-    pj_ssl_sock_t *ssock;
-    pj_activesock_cb asock_cb;
-    pj_activesock_cfg asock_cfg;
-    unsigned i;
-    pj_status_t status;
-
-    /* Create new SSL socket instance */
-    status = pj_ssl_sock_create(ssock_parent->pool,
-				&ssock_parent->newsock_param, &ssock);
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Update new SSL socket attributes */
-    ssock->sock = newsock;
-    ssock->parent = ssock_parent;
-    ssock->is_server = PJ_TRUE;
-    if (ssock_parent->cert) {
-	status = pj_ssl_sock_set_certificate(ssock, ssock->pool, 
-					     ssock_parent->cert);
-	if (status != PJ_SUCCESS)
-	    goto on_return;
-    }
-
-    /* Apply QoS, if specified */
-    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
-				&ssock->param.qos_params, 1, 
-				ssock->pool->obj_name, NULL);
-    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
-	goto on_return;
-
-    /* Apply socket options, if specified */
-    if (ssock->param.sockopt_params.cnt) {
-	status = pj_sock_setsockopt_params(ssock->sock, 
-					   &ssock->param.sockopt_params);
-	if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error)
-	    goto on_return;
-    }
-
-    /* Update local address */
-    ssock->addr_len = src_addr_len;
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
-				 &ssock->addr_len);
-    if (status != PJ_SUCCESS) {
-	/* This fails on few envs, e.g: win IOCP, just tolerate this and
-	 * use parent local address instead.
-	 */
-	pj_sockaddr_cp(&ssock->local_addr, &ssock_parent->local_addr);
-    }
-
-    /* Set remote address */
-    pj_sockaddr_cp(&ssock->rem_addr, src_addr);
-
-    /* Create SSL context */
-    status = create_ssl(ssock);
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Prepare read buffer */
-    ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, 
-					       ssock->param.async_cnt,
-					       sizeof(void*));
-    for (i = 0; i<ssock->param.async_cnt; ++i) {
-	ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
-					    ssock->pool, 
-					    ssock->param.read_buffer_size + 
-					    sizeof(read_data_t*));
-    }
-
-    /* Create active socket */
-    pj_activesock_cfg_default(&asock_cfg);
-    asock_cfg.async_cnt = ssock->param.async_cnt;
-    asock_cfg.concurrency = ssock->param.concurrency;
-    asock_cfg.whole_data = PJ_TRUE;
-    
-    /* If listener socket has group lock, automatically create group lock
-     * for the new socket.
-     */
-    if (ssock_parent->param.grp_lock) {
-	pj_grp_lock_t *glock;
-
-	status = pj_grp_lock_create(ssock->pool, NULL, &glock);
-	if (status != PJ_SUCCESS)
-	    goto on_return;
-
-	pj_grp_lock_add_ref(glock);
-	asock_cfg.grp_lock = ssock->param.grp_lock = glock;
-	pj_grp_lock_add_handler(ssock->param.grp_lock, ssock->pool, ssock,
-				ssl_on_destroy);
-    }
-
-    pj_bzero(&asock_cb, sizeof(asock_cb));
-    asock_cb.on_data_read = asock_on_data_read;
-    asock_cb.on_data_sent = asock_on_data_sent;
-
-    status = pj_activesock_create(ssock->pool,
-				  ssock->sock, 
-				  ssock->param.sock_type,
-				  &asock_cfg,
-				  ssock->param.ioqueue, 
-				  &asock_cb,
-				  ssock,
-				  &ssock->asock);
-
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Start read */
-    status = pj_activesock_start_read2(ssock->asock, ssock->pool, 
-				       (unsigned)ssock->param.read_buffer_size,
-				       ssock->asock_rbuf,
-				       PJ_IOQUEUE_ALWAYS_ASYNC);
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Prepare write/send state */
-    pj_assert(ssock->send_buf.max_len == 0);
-    ssock->send_buf.buf = (char*)
-			  pj_pool_alloc(ssock->pool, 
-					ssock->param.send_buffer_size);
-    ssock->send_buf.max_len = ssock->param.send_buffer_size;
-    ssock->send_buf.start = ssock->send_buf.buf;
-    ssock->send_buf.len = 0;
-
-    /* Start handshake timer */
-    if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 ||
-	ssock->param.timeout.msec != 0))
-    {
-	pj_assert(ssock->timer.id == TIMER_NONE);
-	ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
-	status = pj_timer_heap_schedule(ssock->param.timer_heap, 
-				        &ssock->timer,
-					&ssock->param.timeout);
-	if (status != PJ_SUCCESS) {
-	    ssock->timer.id = TIMER_NONE;
-	    status = PJ_SUCCESS;
-	}
-    }
-
-    /* Start SSL handshake */
-    ssock->ssl_state = SSL_STATE_HANDSHAKING;
-    SSL_set_accept_state(ssock->ossl_ssl);
-    status = do_handshake(ssock);
-
-on_return:
-    if (ssock && status != PJ_EPENDING) {
-	on_handshake_complete(ssock, status);
-    }
-
-    /* Must return PJ_TRUE whatever happened, as active socket must 
-     * continue listening.
-     */
-    return PJ_TRUE;
-}
-
-
-static pj_bool_t asock_on_accept_complete2(pj_activesock_t *asock,
-					   pj_sock_t newsock,
-					   const pj_sockaddr_t *src_addr,
-					   int src_addr_len,
-					   pj_status_t status)
-{
-    pj_bool_t ret = PJ_TRUE;
-    if (status != PJ_SUCCESS) {
-	pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
-			pj_activesock_get_user_data(asock);
-
-	if (ssock->param.cb.on_accept_complete2) {
-	    (*ssock->param.cb.on_accept_complete2) (ssock, NULL, 
-						    src_addr, src_addr_len, 
-						    status);
-	}
-    } else {
-	ret = asock_on_accept_complete(asock, newsock, src_addr, src_addr_len);
-    }
-    return ret;
-}
-
-
-static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
-					    pj_status_t status)
-{
-    pj_ssl_sock_t *ssock = (pj_ssl_sock_t*)
-			   pj_activesock_get_user_data(asock);
-    unsigned i;
-
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Update local address */
-    ssock->addr_len = sizeof(pj_sockaddr);
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
-				 &ssock->addr_len);
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Create SSL context */
-    status = create_ssl(ssock);
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Prepare read buffer */
-    ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, 
-					       ssock->param.async_cnt,
-					       sizeof(void*));
-    for (i = 0; i<ssock->param.async_cnt; ++i) {
-	ssock->asock_rbuf[i] = (void*) pj_pool_alloc(
-					    ssock->pool, 
-					    ssock->param.read_buffer_size + 
-					    sizeof(read_data_t*));
-    }
-
-    /* Start read */
-    status = pj_activesock_start_read2(ssock->asock, ssock->pool, 
-				       (unsigned)ssock->param.read_buffer_size,
-				       ssock->asock_rbuf,
-				       PJ_IOQUEUE_ALWAYS_ASYNC);
-    if (status != PJ_SUCCESS)
-	goto on_return;
-
-    /* Prepare write/send state */
-    pj_assert(ssock->send_buf.max_len == 0);
-    ssock->send_buf.buf = (char*)
-			     pj_pool_alloc(ssock->pool, 
-					   ssock->param.send_buffer_size);
-    ssock->send_buf.max_len = ssock->param.send_buffer_size;
-    ssock->send_buf.start = ssock->send_buf.buf;
-    ssock->send_buf.len = 0;
-
-#ifdef SSL_set_tlsext_host_name
-    /* Set server name to connect */
-    if (ssock->param.server_name.slen) {
-	/* Server name is null terminated already */
-	if (!SSL_set_tlsext_host_name(ssock->ossl_ssl, 
-				      ssock->param.server_name.ptr))
-	{
-	    char err_str[PJ_ERR_MSG_SIZE];
-
-	    ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str));
-	    PJ_LOG(3,(ssock->pool->obj_name, "SSL_set_tlsext_host_name() "
-		"failed: %s", err_str));
-	}
-    }
-#endif
-
-    /* Start SSL handshake */
-    ssock->ssl_state = SSL_STATE_HANDSHAKING;
-    SSL_set_connect_state(ssock->ossl_ssl);
-
-    status = do_handshake(ssock);
-    if (status != PJ_EPENDING)
-	goto on_return;
-
-    return PJ_TRUE;
-
-on_return:
-    return on_handshake_complete(ssock, status);
-}
-
-
-
-/*
- *******************************************************************
- * API
- *******************************************************************
- */
-
-/* Load credentials from files. */
-PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files (pj_pool_t *pool,
-						 const pj_str_t *CA_file,
-						 const pj_str_t *cert_file,
-						 const pj_str_t *privkey_file,
-						 const pj_str_t *privkey_pass,
-						 pj_ssl_cert_t **p_cert)
-{
-    return pj_ssl_cert_load_from_files2(pool, CA_file, NULL, cert_file,
-					privkey_file, privkey_pass, p_cert);
-}
-
-PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool,
-						 const pj_str_t *CA_file,
-						 const pj_str_t *CA_path,
-						 const pj_str_t *cert_file,
-						 const pj_str_t *privkey_file,
-						 const pj_str_t *privkey_pass,
-						 pj_ssl_cert_t **p_cert)
-{
-    pj_ssl_cert_t *cert;
-
-    PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file &&
-		     privkey_file,
-		     PJ_EINVAL);
-
-    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
-    if (CA_file) {
-    	pj_strdup_with_null(pool, &cert->CA_file, CA_file);
-    }
-    if (CA_path) {
-    	pj_strdup_with_null(pool, &cert->CA_path, CA_path);
-    }
-    pj_strdup_with_null(pool, &cert->cert_file, cert_file);
-    pj_strdup_with_null(pool, &cert->privkey_file, privkey_file);
-    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
-
-    *p_cert = cert;
-
-    return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
-					const pj_ssl_cert_buffer *CA_buf,
-					const pj_ssl_cert_buffer *cert_buf,
-					const pj_ssl_cert_buffer *privkey_buf,
-					const pj_str_t *privkey_pass,
-					pj_ssl_cert_t **p_cert)
-{
-    pj_ssl_cert_t *cert;
-
-    PJ_ASSERT_RETURN(pool && CA_buf && cert_buf && privkey_buf, PJ_EINVAL);
-
-    cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
-    pj_strdup(pool, &cert->CA_buf, CA_buf);
-    pj_strdup(pool, &cert->cert_buf, cert_buf);
-    pj_strdup(pool, &cert->privkey_buf, privkey_buf);
-    pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass);
-
-    *p_cert = cert;
-
-    return PJ_SUCCESS;
-}
-
-/* Set SSL socket credentials. */
-PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
-					    pj_ssl_sock_t *ssock,
-					    pj_pool_t *pool,
-					    const pj_ssl_cert_t *cert)
-{
-    pj_ssl_cert_t *cert_;
-
-    PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL);
-
-    cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
-    pj_memcpy(cert_, cert, sizeof(pj_ssl_cert_t));
-    pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file);
-    pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path);
-    pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file);
-    pj_strdup_with_null(pool, &cert_->privkey_file, &cert->privkey_file);
-    pj_strdup_with_null(pool, &cert_->privkey_pass, &cert->privkey_pass);
-
-    pj_strdup(pool, &cert_->CA_buf, &cert->CA_buf);
-    pj_strdup(pool, &cert_->cert_buf, &cert->cert_buf);
-    pj_strdup(pool, &cert_->privkey_buf, &cert->privkey_buf);
-
-    ssock->cert = cert_;
-
-    return PJ_SUCCESS;
-}
-
-
-/* Get available ciphers. */
-PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[],
-					         unsigned *cipher_num)
-{
-    unsigned i;
-
-    PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL);
-
-    if (openssl_cipher_num == 0) {
-	init_openssl();
-	shutdown_openssl();
-    }
-
-    if (openssl_cipher_num == 0) {
-	*cipher_num = 0;
-	return PJ_ENOTFOUND;
-    }
-
-    *cipher_num = PJ_MIN(*cipher_num, openssl_cipher_num);
-
-    for (i = 0; i < *cipher_num; ++i)
-	ciphers[i] = openssl_ciphers[i].id;
-
-    return PJ_SUCCESS;
-}
-
-
-/* Get cipher name string */
-PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
-{
-    unsigned i;
-
-    if (openssl_cipher_num == 0) {
-	init_openssl();
-	shutdown_openssl();
-    }
-
-    for (i = 0; i < openssl_cipher_num; ++i) {
-	if (cipher == openssl_ciphers[i].id)
-	    return openssl_ciphers[i].name;
-    }
-
-    return NULL;
-}
-
-/* Get cipher identifier */
-PJ_DEF(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name)
-{
-    unsigned i;
-
-    if (openssl_cipher_num == 0) {
-        init_openssl();
-        shutdown_openssl();
-    }
-
-    for (i = 0; i < openssl_cipher_num; ++i) {
-        if (!pj_ansi_stricmp(openssl_ciphers[i].name, cipher_name))
-            return openssl_ciphers[i].id;
-    }
-
-    return PJ_TLS_UNKNOWN_CIPHER;
-}
-
-/* Check if the specified cipher is supported by SSL/TLS backend. */
-PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher)
-{
-    unsigned i;
-
-    if (openssl_cipher_num == 0) {
-	init_openssl();
-	shutdown_openssl();
-    }
-
-    for (i = 0; i < openssl_cipher_num; ++i) {
-	if (cipher == openssl_ciphers[i].id)
-	    return PJ_TRUE;
-    }
-
-    return PJ_FALSE;
-}
-
-/* Get available curves. */
-PJ_DEF(pj_status_t) pj_ssl_curve_get_availables(pj_ssl_curve curves[],
-						unsigned *curve_num)
-{
-    unsigned i;
-
-    PJ_ASSERT_RETURN(curves && curve_num, PJ_EINVAL);
-
-    if (openssl_curves_num == 0) {
-	init_openssl();
-	shutdown_openssl();
-    }
-
-    if (openssl_curves_num == 0) {
-	*curve_num = 0;
-	return PJ_ENOTFOUND;
-    }
-
-    *curve_num = PJ_MIN(*curve_num, openssl_curves_num);
-
-    for (i = 0; i < *curve_num; ++i)
-	curves[i] = openssl_curves[i].id;
-
-    return PJ_SUCCESS;
-}
-
-/* Get curve name string. */
-PJ_DEF(const char*) pj_ssl_curve_name(pj_ssl_curve curve)
-{
-    unsigned i;
-
-    if (openssl_curves_num == 0) {
-	init_openssl();
-	shutdown_openssl();
-    }
-
-    for (i = 0; i < openssl_curves_num; ++i) {
-	if (curve == openssl_curves[i].id)
-	    return openssl_curves[i].name;
-    }
-
-    return NULL;
-}
-
-/* Get curve ID from curve name string. */
-PJ_DEF(pj_ssl_curve) pj_ssl_curve_id(const char *curve_name)
-{
-    unsigned i;
-
-    if (openssl_curves_num == 0) {
-        init_openssl();
-        shutdown_openssl();
-    }
-
-    for (i = 0; i < openssl_curves_num; ++i) {
-        if (openssl_curves[i].name &&
-        	!pj_ansi_stricmp(openssl_curves[i].name, curve_name))
-        {
-            return openssl_curves[i].id;
-        }
-    }
-
-    return PJ_TLS_UNKNOWN_CURVE;
-}
-
-/* Check if the specified curve is supported by SSL/TLS backend. */
-PJ_DEF(pj_bool_t) pj_ssl_curve_is_supported(pj_ssl_curve curve)
-{
-    unsigned i;
-
-    if (openssl_curves_num == 0) {
-	init_openssl();
-	shutdown_openssl();
-    }
-
-    for (i = 0; i < openssl_curves_num; ++i) {
-	if (curve == openssl_curves[i].id)
-	    return PJ_TRUE;
-    }
-
-    return PJ_FALSE;
-}
-
-/*
- * Create SSL socket instance. 
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
-					const pj_ssl_sock_param *param,
-					pj_ssl_sock_t **p_ssock)
-{
-    pj_ssl_sock_t *ssock;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL);
-    PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP);
-
-    pool = pj_pool_create(pool->factory, "ssl%p", 512, 512, NULL);
-
-    /* Create secure socket */
-    ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t);
-    ssock->pool = pool;
-    ssock->sock = PJ_INVALID_SOCKET;
-    ssock->ssl_state = SSL_STATE_NULL;
-    pj_list_init(&ssock->write_pending);
-    pj_list_init(&ssock->write_pending_empty);
-    pj_list_init(&ssock->send_pending);
-    pj_timer_entry_init(&ssock->timer, 0, ssock, &on_timer);
-    pj_ioqueue_op_key_init(&ssock->handshake_op_key,
-			   sizeof(pj_ioqueue_op_key_t));
-
-    /* Create secure socket mutex */
-    status = pj_lock_create_recursive_mutex(pool, pool->obj_name,
-					    &ssock->write_mutex);
-    if (status != PJ_SUCCESS) {
-	pj_pool_release(pool);
-	return status;
-    }
-
-    /* Init secure socket param */
-    pj_ssl_sock_param_copy(pool, &ssock->param, param);
-
-    if (ssock->param.grp_lock) {
-	pj_grp_lock_add_ref(ssock->param.grp_lock);
-	pj_grp_lock_add_handler(ssock->param.grp_lock, pool, ssock,
-				ssl_on_destroy);
-    }
-
-    ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3;
-    if (!ssock->param.timer_heap) {
-	PJ_LOG(3,(ssock->pool->obj_name, "Warning: timer heap is not "
-		  "available. It is recommended to supply one to avoid "
-	          "a race condition if more than one worker threads "
-	          "are used."));
-    }
-
-    /* Finally */
-    *p_ssock = ssock;
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Close the secure socket. This will unregister the socket from the
- * ioqueue and ultimately close the socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock)
-{
-    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
-
-    if (!ssock->pool)
-	return PJ_SUCCESS;
-
-    if (ssock->timer.id != TIMER_NONE) {
-	pj_timer_heap_cancel(ssock->param.timer_heap, &ssock->timer);
-	ssock->timer.id = TIMER_NONE;
-    }
-
-    reset_ssl_sock_state(ssock);
-    if (ssock->param.grp_lock) {
-	pj_grp_lock_dec_ref(ssock->param.grp_lock);
-    } else {
-	ssl_on_destroy(ssock);
-    }
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Associate arbitrary data with the secure socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock,
-					      void *user_data)
-{
-    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
-
-    ssock->param.user_data = user_data;
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Retrieve the user data previously associated with this secure
- * socket.
- */
-PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock)
-{
-    PJ_ASSERT_RETURN(ssock, NULL);
-
-    return ssock->param.user_data;
-}
-
-
-/*
- * Retrieve the local address and port used by specified SSL socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
-					  pj_ssl_sock_info *info)
-{
-    pj_bzero(info, sizeof(*info));
-
-    /* Established flag */
-    info->established = (ssock->ssl_state == SSL_STATE_ESTABLISHED);
-
-    /* Protocol */
-    info->proto = ssock->param.proto;
-
-    /* Local address */
-    pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
-    
-    if (info->established) {
-	const SSL_CIPHER *cipher;
-
-	/* Current cipher */
-	cipher = SSL_get_current_cipher(ssock->ossl_ssl);
-	if (cipher) {
-	    info->cipher = (SSL_CIPHER_get_id(cipher) & 0x00FFFFFF);
-	} else {
-	    info->cipher = PJ_TLS_UNKNOWN_CIPHER;
-	}
-
-	/* Remote address */
-	pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
-
-	/* Certificates info */
-	info->local_cert_info = &ssock->local_cert_info;
-	info->remote_cert_info = &ssock->remote_cert_info;
-
-	/* Verification status */
-	info->verify_status = ssock->verify_status;
-    }
-
-    /* Last known OpenSSL error code */
-    info->last_native_err = ssock->last_err;
-
-    /* Group lock */
-    info->grp_lock = ssock->param.grp_lock;
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Starts read operation on this secure socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock,
-					    pj_pool_t *pool,
-					    unsigned buff_size,
-					    pj_uint32_t flags)
-{
-    void **readbuf;
-    unsigned i;
-
-    PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL);
-
-    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
-	return PJ_EINVALIDOP;
-
-    readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, 
-				      sizeof(void*));
-
-    for (i=0; i<ssock->param.async_cnt; ++i) {
-	readbuf[i] = pj_pool_alloc(pool, buff_size);
-    }
-
-    return pj_ssl_sock_start_read2(ssock, pool, buff_size, 
-				   readbuf, flags);
-}
-
-
-/*
- * Same as #pj_ssl_sock_start_read(), except that the application
- * supplies the buffers for the read operation so that the acive socket
- * does not have to allocate the buffers.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock,
-					     pj_pool_t *pool,
-					     unsigned buff_size,
-					     void *readbuf[],
-					     pj_uint32_t flags)
-{
-    unsigned i;
-
-    PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL);
-
-    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
-	return PJ_EINVALIDOP;
-
-    /* Create SSL socket read buffer */
-    ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, 
-					       ssock->param.async_cnt,
-					       sizeof(read_data_t));
-
-    /* Store SSL socket read buffer pointer in the activesock read buffer */
-    for (i=0; i<ssock->param.async_cnt; ++i) {
-	read_data_t **p_ssock_rbuf = 
-			OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]);
-
-	ssock->ssock_rbuf[i].data = readbuf[i];
-	ssock->ssock_rbuf[i].len = 0;
-
-	*p_ssock_rbuf = &ssock->ssock_rbuf[i];
-    }
-
-    ssock->read_size = buff_size;
-    ssock->read_started = PJ_TRUE;
-    ssock->read_flags = flags;
-
-    return PJ_SUCCESS;
-}
-
-
-/*
- * Same as pj_ssl_sock_start_read(), except that this function is used
- * only for datagram sockets, and it will trigger \a on_data_recvfrom()
- * callback instead.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock,
-						pj_pool_t *pool,
-						unsigned buff_size,
-						pj_uint32_t flags)
-{
-    PJ_UNUSED_ARG(ssock);
-    PJ_UNUSED_ARG(pool);
-    PJ_UNUSED_ARG(buff_size);
-    PJ_UNUSED_ARG(flags);
-
-    return PJ_ENOTSUP;
-}
-
-
-/*
- * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() 
- * operation takes the buffer from the argument rather than creating
- * new ones.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock,
-						 pj_pool_t *pool,
-						 unsigned buff_size,
-						 void *readbuf[],
-						 pj_uint32_t flags)
-{
-    PJ_UNUSED_ARG(ssock);
-    PJ_UNUSED_ARG(pool);
-    PJ_UNUSED_ARG(buff_size);
-    PJ_UNUSED_ARG(readbuf);
-    PJ_UNUSED_ARG(flags);
-
-    return PJ_ENOTSUP;
-}
-
-/* Write plain data to SSL and flush write BIO. */
-static pj_status_t ssl_write(pj_ssl_sock_t *ssock, 
-			     pj_ioqueue_op_key_t *send_key,
-			     const void *data,
-			     pj_ssize_t size,
-			     unsigned flags)
-{
-    pj_status_t status;
-    int nwritten;
-
-    /* Write the plain data to SSL, after SSL encrypts it, write BIO will
-     * contain the secured data to be sent via socket. Note that re-
-     * negotitation may be on progress, so sending data should be delayed
-     * until re-negotiation is completed.
+
+static pj_status_t ssl_read(pj_ssl_sock_t *ssock, void *data, int *size)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    int size_ = *size;
+    int len = size_;
+
+    /* SSL_read() may write some data to write buffer when re-negotiation
+     * is on progress, so let's protect it with write mutex.
      */
     pj_lock_acquire(ssock->write_mutex);
-    /* Don't write to SSL if send buffer is full and some data is in
-     * write BIO already, just return PJ_ENOMEM.
-     */
-    if (ssock->send_buf_pending.data_len) {
-	pj_lock_release(ssock->write_mutex);
-	return PJ_ENOMEM;
-    }
-    nwritten = SSL_write(ssock->ossl_ssl, data, (int)size);
+    *size = size_ = SSL_read(ossock->ossl_ssl, data, size_);
     pj_lock_release(ssock->write_mutex);
-    
-    if (nwritten == size) {
-	/* All data written, flush write BIO to network socket */
-	status = flush_write_bio(ssock, send_key, size, flags);
-    } else if (nwritten <= 0) {
+
+    if (size_ <= 0) {
+	pj_status_t status;
+	int err = SSL_get_error(ossock->ossl_ssl, size_);
+
+	/* SSL might just return SSL_ERROR_WANT_READ in 
+	 * re-negotiation.
+	 */
+	if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) {
+	    if (err == SSL_ERROR_SYSCALL && size_ == -1 &&
+		ERR_peek_error() == 0 && errno == 0)
+	    {
+		status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
+					      err, len);
+		PJ_LOG(4,("SSL", "SSL_read() = -1, with "
+				 "SSL_ERROR_SYSCALL, no SSL error, "
+				 "and errno = 0 - skip BIO error"));
+		/* Ignore these errors */
+	    } else {
+		/* Reset SSL socket state, then return PJ_FALSE */
+		status = STATUS_FROM_SSL_ERR2("Read", ssock, size_,
+		        		      err, len);
+		ssl_reset_sock_state(ssock);
+		return status;
+	    }
+	}
+	
+	/* Need renegotiation */
+	return PJ_EEOF;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/* Write plain data to SSL and flush write BIO. */
+static pj_status_t ssl_write(pj_ssl_sock_t *ssock, const void *data,
+			     pj_ssize_t size, int *nwritten)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    pj_status_t status = PJ_SUCCESS;
+
+    *nwritten = SSL_write(ossock->ossl_ssl, data, (int)size);
+    if (*nwritten <= 0) {
 	/* SSL failed to process the data, it may just that re-negotiation
 	 * is on progress.
 	 */
 	int err;
-	err = SSL_get_error(ssock->ossl_ssl, nwritten);
+	err = SSL_get_error(ossock->ossl_ssl, *nwritten);
 	if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) {
-	    /* Re-negotiation is on progress, flush re-negotiation data */
-	    status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0);
-	    if (status == PJ_SUCCESS || status == PJ_EPENDING)
-		/* Just return PJ_EBUSY when re-negotiation is on progress */
-		status = PJ_EBUSY;
+	    status = PJ_EEOF;
 	} else {
 	    /* Some problem occured */
-	    status = STATUS_FROM_SSL_ERR2("Write", ssock, nwritten, err, size);
-	}
-    } else {
-	/* nwritten < *size, shouldn't happen, unless write BIO cannot hold 
+	    status = STATUS_FROM_SSL_ERR2("Write", ssock, *nwritten,
+	    				  err, size);
+	}
+    } else if (*nwritten < size) {
+	/* nwritten < size, shouldn't happen, unless write BIO cannot hold 
 	 * the whole secured data, perhaps because of insufficient memory.
 	 */
@@ -3229,417 +1742,19 @@
 }
 
-/* Flush delayed data sending in the write pending list. */
-static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock)
-{
-    /* Check for another ongoing flush */
-    if (ssock->flushing_write_pend)
-	return PJ_EBUSY;
-
-    pj_lock_acquire(ssock->write_mutex);
-
-    /* Again, check for another ongoing flush */
-    if (ssock->flushing_write_pend) {
-	pj_lock_release(ssock->write_mutex);
-	return PJ_EBUSY;
-    }
-
-    /* Set ongoing flush flag */
-    ssock->flushing_write_pend = PJ_TRUE;
-
-    while (!pj_list_empty(&ssock->write_pending)) {
-        write_data_t *wp;
-	pj_status_t status;
-
-	wp = ssock->write_pending.next;
-
-	/* Ticket #1573: Don't hold mutex while calling socket send. */
-	pj_lock_release(ssock->write_mutex);
-
-	status = ssl_write(ssock, &wp->key, wp->data.ptr, 
-			   wp->plain_data_len, wp->flags);
-	if (status != PJ_SUCCESS) {
-	    /* Reset ongoing flush flag first. */
-	    ssock->flushing_write_pend = PJ_FALSE;
-	    return status;
-	}
-
-	pj_lock_acquire(ssock->write_mutex);
-	pj_list_erase(wp);
-	pj_list_push_back(&ssock->write_pending_empty, wp);
-    }
-
-    /* Reset ongoing flush flag */
-    ssock->flushing_write_pend = PJ_FALSE;
-
-    pj_lock_release(ssock->write_mutex);
-
-    return PJ_SUCCESS;
-}
-
-/* Sending is delayed, push back the sending data into pending list. */
-static pj_status_t delay_send (pj_ssl_sock_t *ssock,
-			       pj_ioqueue_op_key_t *send_key,
-			       const void *data,
-			       pj_ssize_t size,
-			       unsigned flags)
-{
-    write_data_t *wp;
-
-    pj_lock_acquire(ssock->write_mutex);
-
-    /* Init write pending instance */
-    if (!pj_list_empty(&ssock->write_pending_empty)) {
-	wp = ssock->write_pending_empty.next;
-	pj_list_erase(wp);
-    } else {
-	wp = PJ_POOL_ZALLOC_T(ssock->pool, write_data_t);
-    }
-
-    wp->app_key = send_key;
-    wp->plain_data_len = size;
-    wp->data.ptr = data;
-    wp->flags = flags;
-
-    pj_list_push_back(&ssock->write_pending, wp);
-    
-    pj_lock_release(ssock->write_mutex);
-
-    /* Must return PJ_EPENDING */
-    return PJ_EPENDING;
-}
-
-/**
- * Send data using the socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock,
-				      pj_ioqueue_op_key_t *send_key,
-				      const void *data,
-				      pj_ssize_t *size,
-				      unsigned flags)
-{
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL);
-
-    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
-	return PJ_EINVALIDOP;
-
-    // Ticket #1573: Don't hold mutex while calling PJLIB socket send().
-    //pj_lock_acquire(ssock->write_mutex);
-
-    /* Flush delayed send first. Sending data might be delayed when 
-     * re-negotiation is on-progress.
-     */
-    status = flush_delayed_send(ssock);
-    if (status == PJ_EBUSY) {
-	/* Re-negotiation or flushing is on progress, delay sending */
-	status = delay_send(ssock, send_key, data, *size, flags);
-	goto on_return;
-    } else if (status != PJ_SUCCESS) {
-	goto on_return;
-    }
-
-    /* Write data to SSL */
-    status = ssl_write(ssock, send_key, data, *size, flags);
-    if (status == PJ_EBUSY) {
-	/* Re-negotiation is on progress, delay sending */
-	status = delay_send(ssock, send_key, data, *size, flags);
-    }
-
-on_return:
-    //pj_lock_release(ssock->write_mutex);
-    return status;
-}
-
-
-/**
- * Send datagram using the socket.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock,
-					pj_ioqueue_op_key_t *send_key,
-					const void *data,
-					pj_ssize_t *size,
-					unsigned flags,
-					const pj_sockaddr_t *addr,
-					int addr_len)
-{
-    PJ_UNUSED_ARG(ssock);
-    PJ_UNUSED_ARG(send_key);
-    PJ_UNUSED_ARG(data);
-    PJ_UNUSED_ARG(size);
-    PJ_UNUSED_ARG(flags);
-    PJ_UNUSED_ARG(addr);
-    PJ_UNUSED_ARG(addr_len);
-
-    return PJ_ENOTSUP;
-}
-
-
-/**
- * Starts asynchronous socket accept() operations on this secure socket. 
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock,
-					      pj_pool_t *pool,
-					      const pj_sockaddr_t *localaddr,
-					      int addr_len)
-{
-    return pj_ssl_sock_start_accept2(ssock, pool, localaddr, addr_len,
-    				     &ssock->param);
-}
-
-
-/**
- * Same as #pj_ssl_sock_start_accept(), but application provides parameter
- * for new accepted secure sockets.
- */
-PJ_DEF(pj_status_t)
-pj_ssl_sock_start_accept2(pj_ssl_sock_t *ssock,
-			  pj_pool_t *pool,
-			  const pj_sockaddr_t *localaddr,
-			  int addr_len,
-			  const pj_ssl_sock_param *newsock_param)
-{
-    pj_activesock_cb asock_cb;
-    pj_activesock_cfg asock_cfg;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL);
-
-    /* Verify new socket parameters */
-    if (newsock_param->grp_lock != ssock->param.grp_lock ||
-        newsock_param->sock_af != ssock->param.sock_af ||
-        newsock_param->sock_type != ssock->param.sock_type)
-    {
-        return PJ_EINVAL;
-    }
-
-    /* Create socket */
-    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, 
-			    &ssock->sock);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Apply SO_REUSEADDR */
-    if (ssock->param.reuse_addr) {
-	int enabled = 1;
-	status = pj_sock_setsockopt(ssock->sock, pj_SOL_SOCKET(),
-				    pj_SO_REUSEADDR(),
-				    &enabled, sizeof(enabled));
-	if (status != PJ_SUCCESS) {
-	    PJ_PERROR(4,(ssock->pool->obj_name, status,
-		         "Warning: error applying SO_REUSEADDR"));
-	}
-    }
-
-    /* Apply QoS, if specified */
-    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
-				&ssock->param.qos_params, 2, 
-				ssock->pool->obj_name, NULL);
-
-    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
-	goto on_error;
-
-    /* Apply socket options, if specified */
-    if (ssock->param.sockopt_params.cnt) {
-	status = pj_sock_setsockopt_params(ssock->sock, 
-					   &ssock->param.sockopt_params);
-
-	if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error)
-	    goto on_error;
-    }
-
-    /* Bind socket */
-    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Start listening to the address */
-    status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Create active socket */
-    pj_activesock_cfg_default(&asock_cfg);
-    asock_cfg.async_cnt = ssock->param.async_cnt;
-    asock_cfg.concurrency = ssock->param.concurrency;
-    asock_cfg.whole_data = PJ_TRUE;
-    asock_cfg.grp_lock = ssock->param.grp_lock;
-
-    pj_bzero(&asock_cb, sizeof(asock_cb));
-    //asock_cb.on_accept_complete = asock_on_accept_complete;
-    asock_cb.on_accept_complete2 = asock_on_accept_complete2;
-
-    status = pj_activesock_create(pool,
-				  ssock->sock, 
-				  ssock->param.sock_type,
-				  &asock_cfg,
-				  ssock->param.ioqueue, 
-				  &asock_cb,
-				  ssock,
-				  &ssock->asock);
-
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Start accepting */
-    pj_ssl_sock_param_copy(pool, &ssock->newsock_param, newsock_param);
-    ssock->newsock_param.grp_lock = NULL;
-    status = pj_activesock_start_accept(ssock->asock, pool);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Update local address */
-    ssock->addr_len = addr_len;
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, 
-				 &ssock->addr_len);
-    if (status != PJ_SUCCESS)
-	pj_sockaddr_cp(&ssock->local_addr, localaddr);
-
-    ssock->is_server = PJ_TRUE;
-
-    return PJ_SUCCESS;
-
-on_error:
-    reset_ssl_sock_state(ssock);
-    return status;
-}
-
-
-/**
- * Starts asynchronous socket connect() operation.
- */
-PJ_DEF(pj_status_t) pj_ssl_sock_start_connect( pj_ssl_sock_t *ssock,
-					       pj_pool_t *pool,
-					       const pj_sockaddr_t *localaddr,
-					       const pj_sockaddr_t *remaddr,
-					       int addr_len)
-{
-    pj_activesock_cb asock_cb;
-    pj_activesock_cfg asock_cfg;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len,
-		     PJ_EINVAL);
-
-    /* Create socket */
-    status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, 
-			    &ssock->sock);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Apply QoS, if specified */
-    status = pj_sock_apply_qos2(ssock->sock, ssock->param.qos_type,
-				&ssock->param.qos_params, 2, 
-				ssock->pool->obj_name, NULL);
-    if (status != PJ_SUCCESS && !ssock->param.qos_ignore_error)
-	goto on_error;
-
-    /* Apply socket options, if specified */
-    if (ssock->param.sockopt_params.cnt) {
-	status = pj_sock_setsockopt_params(ssock->sock, 
-					   &ssock->param.sockopt_params);
-
-	if (status != PJ_SUCCESS && !ssock->param.sockopt_ignore_error)
-	    goto on_error;
-    }
-
-    /* Bind socket */
-    status = pj_sock_bind(ssock->sock, localaddr, addr_len);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Create active socket */
-    pj_activesock_cfg_default(&asock_cfg);
-    asock_cfg.async_cnt = ssock->param.async_cnt;
-    asock_cfg.concurrency = ssock->param.concurrency;
-    asock_cfg.whole_data = PJ_TRUE;
-    asock_cfg.grp_lock = ssock->param.grp_lock;
-
-    pj_bzero(&asock_cb, sizeof(asock_cb));
-    asock_cb.on_connect_complete = asock_on_connect_complete;
-    asock_cb.on_data_read = asock_on_data_read;
-    asock_cb.on_data_sent = asock_on_data_sent;
-
-    status = pj_activesock_create(pool,
-				  ssock->sock, 
-				  ssock->param.sock_type,
-				  &asock_cfg,
-				  ssock->param.ioqueue, 
-				  &asock_cb,
-				  ssock,
-				  &ssock->asock);
-
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Save remote address */
-    pj_sockaddr_cp(&ssock->rem_addr, remaddr);
-
-    /* Start timer */
-    if (ssock->param.timer_heap && (ssock->param.timeout.sec != 0 ||
-	ssock->param.timeout.msec != 0))
-    {
-	pj_assert(ssock->timer.id == TIMER_NONE);
-	ssock->timer.id = TIMER_HANDSHAKE_TIMEOUT;
-	status = pj_timer_heap_schedule(ssock->param.timer_heap,
-					&ssock->timer,
-				        &ssock->param.timeout);
-	if (status != PJ_SUCCESS) {
-	    ssock->timer.id = TIMER_NONE;
-	    status = PJ_SUCCESS;
-	}
-    }
-
-    status = pj_activesock_start_connect(ssock->asock, pool, remaddr,
-					 addr_len);
-
-    if (status == PJ_SUCCESS)
-	asock_on_connect_complete(ssock->asock, PJ_SUCCESS);
-    else if (status != PJ_EPENDING)
-	goto on_error;
-
-    /* Update local address */
-    ssock->addr_len = addr_len;
-    status = pj_sock_getsockname(ssock->sock, &ssock->local_addr,
-				 &ssock->addr_len);
-    /* Note that we may not get an IP address here. This can
-     * happen for example on Windows, where getsockname()
-     * would return 0.0.0.0 if socket has just started the
-     * async connect. In this case, just leave the local
-     * address with 0.0.0.0 for now; it will be updated
-     * once the socket is established.
-     */
-
-    /* Update SSL state */
-    ssock->is_server = PJ_FALSE;
-
-    return PJ_EPENDING;
-
-on_error:
-    reset_ssl_sock_state(ssock);
-    return status;
-}
-
-
-PJ_DEF(pj_status_t) pj_ssl_sock_renegotiate(pj_ssl_sock_t *ssock)
-{
+
+static pj_status_t ssl_renegotiate(pj_ssl_sock_t *ssock)
+{
+    ossl_sock_t *ossock = (ossl_sock_t *)ssock;
+    pj_status_t status = PJ_SUCCESS;
     int ret;
-    pj_status_t status;
-
-    PJ_ASSERT_RETURN(ssock, PJ_EINVAL);
-
-    if (ssock->ssl_state != SSL_STATE_ESTABLISHED) 
-	return PJ_EINVALIDOP;
-
-    if (SSL_renegotiate_pending(ssock->ossl_ssl))
+
+    if (SSL_renegotiate_pending(ossock->ossl_ssl))
 	return PJ_EPENDING;
 
-    ret = SSL_renegotiate(ssock->ossl_ssl);
+    ret = SSL_renegotiate(ossock->ossl_ssl);
     if (ret <= 0) {
 	status = GET_SSL_STATUS(ssock);
-    } else {
-	status = do_handshake(ssock);
-    }
-
+    }
+    
     return status;
 }
