static ssize_t process_output_ssl()

in c/src/ssl/schannel.cpp [1764:1855]


static ssize_t process_output_ssl( pn_transport_t *transport, unsigned int layer, char *buffer, size_t max_len)
{
  pni_ssl_t *ssl = transport->ssl;
  if (!ssl) return PN_EOS;
  ssl_log( transport, PN_LEVEL_TRACE, "process_output_ssl( max_len=%d )",max_len );

  ssize_t written = 0;
  ssize_t total_app_bytes = 0;
  bool work_pending;

  if (ssl->state == CLIENT_HELLO) {
    // output buffers eclusively for internal handshake use until negotiation complete
    client_handshake_init(transport);
    if (ssl->state == SSL_CLOSED)
      return PN_EOS;
    ssl->state = NEGOTIATING;
  }

  do {
    work_pending = false;

    if (ssl->network_out_pending > 0) {
      size_t wcount = _pni_min(ssl->network_out_pending, max_len);
      memmove(buffer, ssl->network_outp, wcount);
      ssl->network_outp += wcount;
      ssl->network_out_pending -= wcount;
      buffer += wcount;
      max_len -= wcount;
      written += wcount;
    }

    if (ssl->network_out_pending == 0 && ssl->state == RUNNING  && !ssl->app_output_closed) {
      // refill the buffer with app data and encrypt it

      char *app_data = ssl->sc_outbuf + ssl->sc_sizes.cbHeader;
      char *app_outp = app_data;
      size_t remaining = ssl->max_data_size;
      ssize_t app_bytes;
      do {
        app_bytes = transport->io_layers[layer+1]->process_output(transport, layer+1, app_outp, remaining);
        if (app_bytes > 0) {
          app_outp += app_bytes;
          remaining -= app_bytes;
          ssl_log( transport, PN_LEVEL_TRACE, "Gathered %d bytes from app to send to peer", app_bytes );
        } else {
          if (app_bytes < 0) {
            ssl_log(transport, PN_LEVEL_WARNING, "Application layer closed its output, error=%d (%d bytes pending send)",
                 (int) app_bytes, (int) ssl->network_out_pending);
            ssl->app_output_closed = app_bytes;
            if (ssl->app_input_closed)
              ssl->state = SHUTTING_DOWN;
          } else if (total_app_bytes == 0 && ssl->app_input_closed) {
            // We've drained all the App layer can provide
            ssl_log(transport, PN_LEVEL_WARNING, "Application layer blocked on input, closing");
            ssl->state = SHUTTING_DOWN;
            ssl->app_output_closed = PN_ERR;
          }
        }
      } while (app_bytes > 0);
      if (app_outp > app_data) {
        work_pending = (max_len > 0);
        ssl_encrypt(transport, app_data, app_outp - app_data);
      }
    }

    if (ssl->network_out_pending == 0) {
      if (ssl->state == SHUTTING_DOWN) {
        if (!ssl->queued_shutdown) {
          start_ssl_shutdown(transport);
          work_pending = true;
        } else {
          ssl->state = SSL_CLOSED;
        }
      }
      else if (ssl->state == NEGOTIATING && ssl->app_input_closed) {
        ssl->app_output_closed = PN_EOS;
        ssl->state = SSL_CLOSED;
      }
    }
  } while (work_pending);

  if (written == 0 && ssl->state == SSL_CLOSED) {
    written = ssl->app_output_closed ? ssl->app_output_closed : PN_EOS;
    if (transport->io_layers[layer]==&ssl_input_closed_layer) {
      transport->io_layers[layer] = &ssl_closed_layer;
    } else {
      transport->io_layers[layer] = &ssl_output_closed_layer;
    }
  }
  ssl_log(transport, PN_LEVEL_TRACE, "process_output_ssl() returning %d", (int) written);
  return written;
}