Example #1
0
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);
}
Example #2
0
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;
}