diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index 33f7d379..681aecff 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c @@ -559,15 +559,20 @@ decrement_buckets(struct bufferevent_openssl *bev_ssl) bev_ssl->counts.n_read = num_r; } -/* returns -1 on internal error, 0 on stall, 1 on progress */ -static int -do_read(struct bufferevent_openssl *bev_ssl, int n_to_read) -{ +#define OP_MADE_PROGRESS 1 +#define OP_BLOCKED 2 +#define OP_ERR 4 + +/* Return a bitmask of OP_MADE_PROGRESS (if we read anything); OP_BLOCKED (if + we're now blocked); and OP_ERR (if an error occurred). */ + static int +do_read(struct bufferevent_openssl *bev_ssl, int n_to_read) { /* Requires lock */ struct bufferevent *bev = &bev_ssl->bev.bev; struct evbuffer *input = bev->input; - int r, n, i, n_used = 0, blocked = 0, atmost; + int r, n, i, n_used = 0, atmost; struct evbuffer_iovec space[2]; + int result = 0; atmost = bufferevent_get_read_max_(&bev_ssl->bev); if (n_to_read > atmost) @@ -575,16 +580,17 @@ do_read(struct bufferevent_openssl *bev_ssl, int n_to_read) n = evbuffer_reserve_space(input, n_to_read, space, 2); if (n < 0) - return -1; + return OP_ERR; for (i=0; ibev.read_suspended) break; r = SSL_read(bev_ssl->ssl, space[i].iov_base, space[i].iov_len); if (r>0) { + result |= OP_MADE_PROGRESS; if (bev_ssl->read_blocked_on_write) if (clear_rbow(bev_ssl) < 0) - return -1; + return OP_ERR | result; ++n_used; space[i].iov_len = r; decrement_buckets(bev_ssl); @@ -596,20 +602,20 @@ do_read(struct bufferevent_openssl *bev_ssl, int n_to_read) /* Can't read until underlying has more data. */ if (bev_ssl->read_blocked_on_write) if (clear_rbow(bev_ssl) < 0) - return -1; + return OP_ERR | result; break; case SSL_ERROR_WANT_WRITE: /* This read operation requires a write, and the * underlying is full */ if (!bev_ssl->read_blocked_on_write) if (set_rbow(bev_ssl) < 0) - return -1; + return OP_ERR | result; break; default: conn_closed(bev_ssl, BEV_EVENT_READING, err, r); break; } - blocked = 1; + result |= OP_BLOCKED; break; /* out of the loop */ } } @@ -620,16 +626,19 @@ do_read(struct bufferevent_openssl *bev_ssl, int n_to_read) BEV_RESET_GENERIC_READ_TIMEOUT(bev); } - return blocked ? 0 : 1; + return result; } +/* Return a bitmask of OP_MADE_PROGRESS (if we wrote anything); OP_BLOCKED (if + we're now blocked); and OP_ERR (if an error occurred). */ static int do_write(struct bufferevent_openssl *bev_ssl, int atmost) { - int i, r, n, n_written = 0, blocked=0; + int i, r, n, n_written = 0; struct bufferevent *bev = &bev_ssl->bev.bev; struct evbuffer *output = bev->output; struct evbuffer_iovec space[8]; + int result = 0; if (bev_ssl->last_write > 0) atmost = bev_ssl->last_write; @@ -638,7 +647,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) n = evbuffer_peek(output, atmost, NULL, space, 8); if (n < 0) - return -1; + return OP_ERR | result; if (n > 8) n = 8; @@ -655,9 +664,10 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) r = SSL_write(bev_ssl->ssl, space[i].iov_base, space[i].iov_len); if (r > 0) { + result |= OP_MADE_PROGRESS; if (bev_ssl->write_blocked_on_read) if (clear_wbor(bev_ssl) < 0) - return -1; + return OP_ERR | result; n_written += r; bev_ssl->last_write = -1; decrement_buckets(bev_ssl); @@ -669,7 +679,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) /* Can't read until underlying has more data. */ if (bev_ssl->write_blocked_on_read) if (clear_wbor(bev_ssl) < 0) - return -1; + return OP_ERR | result; bev_ssl->last_write = space[i].iov_len; break; case SSL_ERROR_WANT_READ: @@ -677,7 +687,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) * underlying is full */ if (!bev_ssl->write_blocked_on_read) if (set_wbor(bev_ssl) < 0) - return -1; + return OP_ERR | result; bev_ssl->last_write = space[i].iov_len; break; default: @@ -685,7 +695,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) bev_ssl->last_write = -1; break; } - blocked = 1; + result |= OP_BLOCKED; break; } } @@ -697,7 +707,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) if (evbuffer_get_length(output) <= bev->wm_write.low) bufferevent_run_writecb_(bev); } - return blocked ? 0 : 1; + return result; } #define WRITE_FRAME 15000 @@ -757,11 +767,11 @@ consider_reading(struct bufferevent_openssl *bev_ssl) { int r; int n_to_read; - int read_data = 0; + int all_result_flags = 0; while (bev_ssl->write_blocked_on_read) { r = do_write(bev_ssl, WRITE_FRAME); - if (r <= 0) + if (r & (OP_BLOCKED|OP_ERR)) break; } if (bev_ssl->write_blocked_on_read) @@ -770,10 +780,11 @@ consider_reading(struct bufferevent_openssl *bev_ssl) n_to_read = bytes_to_read(bev_ssl); while (n_to_read) { - if (do_read(bev_ssl, n_to_read) <= 0) - break; + r = do_read(bev_ssl, n_to_read); + all_result_flags |= r; - read_data = 1; + if (r & (OP_BLOCKED|OP_ERR)) + break; /* Read all pending data. This won't hit the network * again, and will (most importantly) put us in a state @@ -803,7 +814,7 @@ consider_reading(struct bufferevent_openssl *bev_ssl) n_to_read = bytes_to_read(bev_ssl); } - if (read_data == 1) { + if (all_result_flags & OP_MADE_PROGRESS) { struct bufferevent *bev = &bev_ssl->bev.bev; struct evbuffer *input = bev->input; @@ -831,9 +842,7 @@ consider_writing(struct bufferevent_openssl *bev_ssl) while (bev_ssl->read_blocked_on_write) { r = do_read(bev_ssl, 1024); /* XXXX 1024 is a hack */ - if (r <= 0) - break; - else { + if (r & OP_MADE_PROGRESS) { struct bufferevent *bev = &bev_ssl->bev.bev; struct evbuffer *input = bev->input; @@ -841,6 +850,8 @@ consider_writing(struct bufferevent_openssl *bev_ssl) bufferevent_run_readcb_(bev); } } + if (r & (OP_ERR|OP_BLOCKED)) + break; } if (bev_ssl->read_blocked_on_write) return; @@ -858,7 +869,7 @@ consider_writing(struct bufferevent_openssl *bev_ssl) else n_to_write = WRITE_FRAME; r = do_write(bev_ssl, n_to_write); - if (r <= 0) + if (r & (OP_BLOCKED|OP_ERR)) break; }