/* Write some previously-buffered backend data upstream on the * secure socket using OpenSSL */ static void client_write(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; int sz; proxystate *ps = (proxystate *)w->data; assert(!ringbuffer_is_empty(&ps->ring_up)); char * next = ringbuffer_read_next(&ps->ring_up, &sz); t = SSL_write(ps->ssl, next, sz); if (t > 0) { if (t == sz) { ringbuffer_read_pop(&ps->ring_up); safe_enable_io(ps, &ps->ev_r_down); // can be re-enabled b/c we've popped if (ringbuffer_is_empty(&ps->ring_up)) { if (ps->want_shutdown) { shutdown_proxy(ps, SHUTDOWN_HARD); return; } ev_io_stop(loop, &ps->ev_w_up); } } else { ringbuffer_read_skip(&ps->ring_up, t); } } else { int err = SSL_get_error(ps->ssl, t); if (err == SSL_ERROR_WANT_READ) { start_handshake(ps, err); } else if (err == SSL_ERROR_WANT_WRITE) {} /* incomplete SSL data */ else handle_fatal_ssl_error(ps, err); } }
/* Write some data, previously received on the secure upstream socket, * out of the downstream buffer and onto the backend socket */ static void back_write(struct ev_loop *loop, ev_io *w, int revents) { (void) revents; int t; proxystate *ps = (proxystate *)w->data; int fd = w->fd; int sz; assert(!ringbuffer_is_empty(&ps->ring_down)); char *next = ringbuffer_read_next(&ps->ring_down, &sz); t = send(fd, next, sz, MSG_NOSIGNAL); if (t > 0) { if (t == sz) { ringbuffer_read_pop(&ps->ring_down); safe_enable_io(ps, &ps->ev_r_up); if (ringbuffer_is_empty(&ps->ring_down)) { if (ps->want_shutdown) { shutdown_proxy(ps, SHUTDOWN_HARD); return; // dealloc'd } ev_io_stop(loop, &ps->ev_w_down); } } else { ringbuffer_read_skip(&ps->ring_down, t); } } else { assert(t == -1); handle_socket_errno(ps); } }
bud_client_error_t bud_client_backend_in(bud_client_t* client) { size_t size; int written; int err; bud_client_error_t cerr; written = 0; while (!ringbuffer_is_empty(&client->backend.input)) { char* data; data = ringbuffer_read_next(&client->backend.input, &size); written = SSL_write(client->ssl, data, size); DBG(&client->frontend, "SSL_write() => %d", written); DBG(&client->frontend, "frontend.output => %d", ringbuffer_size(&client->frontend.output)); if (written < 0) break; ASSERT(written == (int) size, "SSL_write() did unexpected partial write"); ringbuffer_read_skip(&client->backend.input, written); /* info_cb() has closed front-end */ if (client->frontend.close != kBudProgressNone) return bud_client_ok(&client->backend); } cerr = bud_client_throttle(client, &client->frontend, &client->frontend.output); if (!bud_is_ok(cerr.err) && cerr.err.code != kBudErrClientThrottle) return cerr; if (written >= 0) return bud_client_ok(&client->backend); err = SSL_get_error(client->ssl, written); if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_X509_LOOKUP) { return bud_client_ok(&client->backend); } return bud_client_error(bud_error_num(kBudErrClientSSLWrite, err), &client->backend); }
void ringbuffer_read_pop(ringbuffer* rb) { size_t avail; avail = RB_BUF_SIZE(rb->read_head); ringbuffer_read_skip(rb, avail); }
void bud_client_send_cb(uv_write_t* req, int status) { bud_client_t* client; bud_client_error_t cerr; bud_client_side_t* side; bud_client_side_t* opposite; /* Closing, ignore */ if (status == UV_ECANCELED) return; client = req->data; if (req == &client->frontend.write_req) { side = &client->frontend; opposite = &client->backend; } else { side = &client->backend; opposite = &client->frontend; } if (status != 0) { side->write = kBudProgressDone; bud_client_close( client, bud_client_error(bud_error_num(kBudErrClientWriteCb, status), side)); return; } /* Consume written data */ DBG(side, "write_cb => %d", side->write_size); ringbuffer_read_skip(&side->output, side->write_size); /* Skip data in xforward parser */ if (side == &client->backend) bud_client_xforward_skip(client, side->write_size); side->write = kBudProgressNone; side->write_size = 0; if (opposite->reading == kBudProgressNone) { if ((client->retry == kBudProgressRunning || client->connect == kBudProgressRunning) && opposite == &client->backend) { /* Set reading mark on backend to resume it after reconnecting */ opposite->reading = kBudProgressRunning; } else if (opposite->close != kBudProgressDone && side->close != kBudProgressDone && side->shutdown != kBudProgressDone && !ringbuffer_is_full(&side->output)) { DBG_LN(opposite, "read_start"); cerr = bud_client_read_start(client, opposite); if (!bud_is_ok(cerr.err)) return bud_client_close(client, cerr); opposite->reading = kBudProgressRunning; } } /* Cycle again */ cerr = bud_client_cycle(client); if (!bud_is_ok(cerr.err)) return bud_client_close(client, cerr); if (side->close == kBudProgressRunning || side->shutdown == kBudProgressRunning) { if (!ringbuffer_is_empty(&side->output)) return; /* No new data, destroy or shutdown */ if (side->shutdown == kBudProgressRunning) { cerr = bud_client_shutdown(client, side); if (!bud_is_ok(cerr.err)) bud_client_close(client, cerr); return; } bud_client_close(client, bud_client_ok(side)); } }
bud_client_error_t bud_client_send(bud_client_t* client, bud_client_side_t* side) { char* out[RING_BUFFER_COUNT]; uv_buf_t buf[RING_BUFFER_COUNT]; uv_buf_t* pbuf; size_t size[ARRAY_SIZE(out)]; size_t count; size_t i; int r; bud_client_error_t cerr; /* Already writing */ if (side->write != kBudProgressNone) goto done; /* If client is closed - stop sending */ if (client->close == kBudProgressDone) goto done; /* Backend still connecting */ if (side == &client->backend && client->connect != kBudProgressDone) goto done; count = ARRAY_SIZE(out); side->write_size = ringbuffer_read_nextv(&side->output, out, size, &count); if (side->write_size == 0) goto done; DBG(side, "uv_write(%ld) iovcnt: %ld", side->write_size, count); side->write_req.data = client; for (i = 0; i < count; i++) buf[i] = uv_buf_init(out[i], size[i]); /* Try writing without queueing first */ r = uv_try_write((uv_stream_t*) &side->tcp, buf, count); ASSERT((r >= 0 && (size_t) r <= side->write_size) || r < 0, "Value returned by uv_try_write is OOB"); /* Fully written */ if (r == (int) side->write_size) { DBG_LN(side, "immediate write"); /* NOTE: not causing recursion */ bud_client_send_cb(&side->write_req, 0); goto done; } if (r == UV_ENOSYS || r == UV_EAGAIN) { /* Not supported try_write */ r = 0; } else if (r < 0) { cerr = bud_client_error(bud_error_num(kBudErrClientTryWrite, r), side); goto fatal; } /* Skip partially written bytes */ ringbuffer_read_skip(&side->output, r); /* Partially written */ side->write_size -= r; pbuf = buf; for (; r > 0; pbuf++, count--) { if ((int) pbuf->len > r) { /* Split */ pbuf->base += r; pbuf->len -= r; r = 0; break; } else { r -= pbuf->len; } } DBG(side, "async uv_write(%ld) follow up: %ld", side->write_size, count); r = uv_write(&side->write_req, (uv_stream_t*) &side->tcp, pbuf, count, bud_client_send_cb); if (r != 0) { cerr = bud_client_error(bud_error_num(kBudErrClientWrite, r), side); goto fatal; } DBG_LN(side, "queued write"); side->write = kBudProgressRunning; done: return bud_client_ok(side); fatal: side->write = kBudProgressDone; return cerr; }
int main() { int i; int j; int r; int after; ssize_t len; char* ptr; data = malloc(TEST_DATA_SIZE); assert(data != NULL); ringbuffer_init(&rb); /* Fill test data */ for (i = 0; i < TEST_DATA_SIZE; i++) data[i] = (i * i) % 137; /* Fill ringbuffer */ i = 0; after = 0; while (i < TEST_DATA_SIZE - TEST_INSERT_LEN) { if (after) len = TEST_DATA_SIZE - i - TEST_INSERT_LEN; else len = TEST_INSERT_OFF - i; ptr = ringbuffer_write_ptr(&rb, &len); ASSERT(ptr != NULL); /* Always make progress */ ASSERT(len > 0); if (after) memcpy(ptr, data + i + TEST_INSERT_LEN, len); else memcpy(ptr, data + i, len); i += len; r = ringbuffer_write_append(&rb, len); ASSERT(r == 0); if (i == TEST_INSERT_OFF) after = 1; } ASSERT(ringbuffer_size(&rb) == TEST_DATA_SIZE - TEST_INSERT_LEN); /* Insert stuff */ ringbuffer_insert(&rb, TEST_INSERT_OFF, data + TEST_INSERT_OFF, TEST_INSERT_LEN); /* Read from it */ i = 0; while (i < TEST_DATA_SIZE) { len = TEST_DATA_SIZE - i; ptr = ringbuffer_read_next(&rb, &len); ASSERT(ptr != NULL); /* Always make progress */ ASSERT(len > 0); for (j = 0; j < len; j++) ASSERT(ptr[j] == data[i + j]); ringbuffer_read_skip(&rb, len); i += len; } /* Destroy it */ ringbuffer_destroy(&rb); return 0; }
void ringbuffer_read_pop(ringbuffer* rb) { size_t avail; avail = rb->read_head->write_pos - rb->read_head->read_pos; ringbuffer_read_skip(rb, avail); }