static void do_gnutls_write(liGnuTLSFilter *f) { const ssize_t blocksize = 16*1024; /* 16k */ char *block_data; off_t block_len; ssize_t r; off_t write_max; #ifdef USE_CORK gboolean corked = FALSE; #endif liChunkQueue *cq = f->plain_drain.out; f_acquire(f); f->write_wants_read = FALSE; /* use space in (encrypted) outgoing buffer as amounts of bytes we try to write from (plain) output * don't care if we write a little bit more than the limit allowed */ write_max = li_chunkqueue_limit_available(f->crypt_source.out); LI_FORCE_ASSERT(write_max >= 0); /* we set a limit! */ if (0 == write_max) goto out; /* if we start writing, try to write at least blocksize bytes */ if (write_max < blocksize) write_max = blocksize; if (NULL != f->session && !f->initial_handshaked_finished && !do_gnutls_handshake(f, TRUE)) goto out; if (NULL == f->session) { f_abort_gnutls(f); goto out; } #ifdef USE_CORK if (0 != cq->length && cq->queue.length > 1) { corked = TRUE; gnutls_record_cork(f->session); } #endif do { GError *err = NULL; liChunkIter ci; if (0 == cq->length) break; ci = li_chunkqueue_iter(cq); switch (li_chunkiter_read(ci, 0, blocksize, &block_data, &block_len, &err)) { case LI_HANDLER_GO_ON: break; case LI_HANDLER_ERROR: if (NULL != err) { _ERROR(f->srv, f->wrk, f->log_context, "Couldn't read data from chunkqueue: %s", err->message); g_error_free(err); } /* fall through */ default: f_abort_gnutls(f); goto out; } r = gnutls_record_send(f->session, block_data, block_len); if (r <= 0) { do_handle_error(f, "gnutls_record_send", r, TRUE); goto out; } li_chunkqueue_skip(cq, r); write_max -= r; } while (r == block_len && write_max > 0); if (cq->is_closed && 0 == cq->length) { r = gnutls_bye(f->session, GNUTLS_SHUT_RDWR); switch (r) { case GNUTLS_E_SUCCESS: case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: f->plain_source.out->is_closed = TRUE; f->crypt_source.out->is_closed = TRUE; f->crypt_drain.out->is_closed = TRUE; li_stream_disconnect(&f->crypt_source); /* plain in -> crypt out */ f_close_gnutls(f); break; default: do_handle_error(f, "gnutls_bye", r, TRUE); f_abort_gnutls(f); break; } } else if (0 < cq->length && 0 != li_chunkqueue_limit_available(f->crypt_source.out)) { li_stream_again_later(&f->plain_drain); } out: #ifdef USE_CORK if (NULL != f->session && corked) { corked = TRUE; gnutls_record_uncork(f->session, 0); } #endif f_release(f); }
static liHandlerResult fastcgi_statemachine(liVRequest *vr, fastcgi_connection *fcon) { liPlugin *p = fcon->ctx->plugin; switch (fcon->state) { case FS_WAIT_FOR_REQUEST: /* wait until we have either all data or the cqlimit is full */ if (-1 == vr->request.content_length || vr->request.content_length != vr->in->length) { if (0 != li_chunkqueue_limit_available(vr->in)) return LI_HANDLER_GO_ON; } fcon->state = FS_CONNECT; /* fall through */ case FS_CONNECT: do { fcon->fd = socket(fcon->ctx->socket.addr->plain.sa_family, SOCK_STREAM, 0); } while (-1 == fcon->fd && errno == EINTR); if (-1 == fcon->fd) { if (errno == EMFILE) { li_server_out_of_fds(vr->wrk->srv); } else if (errno != g_atomic_int_get(&fcon->ctx->last_errno)) { g_atomic_int_set(&fcon->ctx->last_errno, errno); VR_ERROR(vr, "Couldn't open socket: %s", g_strerror(errno)); } return LI_HANDLER_ERROR; } li_fd_init(fcon->fd); ev_io_set(&fcon->fd_watcher, fcon->fd, EV_READ | EV_WRITE); ev_io_start(vr->wrk->loop, &fcon->fd_watcher); /* fall through */ case FS_CONNECTING: if (-1 == connect(fcon->fd, &fcon->ctx->socket.addr->plain, fcon->ctx->socket.len)) { switch (errno) { case EINPROGRESS: case EALREADY: case EINTR: fcon->state = FS_CONNECTING; return LI_HANDLER_GO_ON; case EAGAIN: /* backend overloaded */ fastcgi_close(vr, p); li_vrequest_backend_overloaded(vr); return LI_HANDLER_GO_ON; case EISCONN: break; default: if (errno != g_atomic_int_get(&fcon->ctx->last_errno)) { g_atomic_int_set(&fcon->ctx->last_errno, errno); VR_ERROR(vr, "Couldn't connect to '%s': %s", li_sockaddr_to_string(fcon->ctx->socket, vr->wrk->tmp_str, TRUE)->str, g_strerror(errno)); } fastcgi_close(vr, p); li_vrequest_backend_dead(vr); return LI_HANDLER_GO_ON; } } g_atomic_int_set(&fcon->ctx->last_errno, 0); fcon->state = FS_CONNECTED; /* prepare stream */ fastcgi_send_begin(fcon); fastcgi_send_env(vr, fcon); /* fall through */ case FS_CONNECTED: fastcgi_forward_request(vr, fcon); break; case FS_DONE: break; } return LI_HANDLER_GO_ON; }