diff --git a/bufferevent_openssl.c b/bufferevent_openssl.c index cbb7f8a5..da3963af 100644 --- a/bufferevent_openssl.c +++ b/bufferevent_openssl.c @@ -404,7 +404,10 @@ start_writing(struct bufferevent_openssl *bev_ssl) { int r = 0; if (bev_ssl->underlying) { - ; + if (bev_ssl->write_blocked_on_read) { + bufferevent_unsuspend_read_(bev_ssl->underlying, + BEV_SUSPEND_FILT_READ); + } } else { struct bufferevent *bev = &bev_ssl->bev.bev; r = bufferevent_add_event_(&bev->ev_write, &bev->timeout_write); @@ -435,7 +438,8 @@ stop_writing(struct bufferevent_openssl *bev_ssl) if (bev_ssl->read_blocked_on_write) return; if (bev_ssl->underlying) { - ; + bufferevent_unsuspend_read_(bev_ssl->underlying, + BEV_SUSPEND_FILT_READ); } else { struct bufferevent *bev = &bev_ssl->bev.bev; event_del(&bev->ev_write); @@ -716,7 +720,7 @@ do_write(struct bufferevent_openssl *bev_ssl, int atmost) if (bev_ssl->underlying) BEV_RESET_GENERIC_WRITE_TIMEOUT(bev); - bufferevent_trigger_nolock_(bev, EV_WRITE, 0); + bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS); } return result; } @@ -1040,17 +1044,11 @@ do_handshake(struct bufferevent_openssl *bev_ssl) print_err(err); switch (err) { case SSL_ERROR_WANT_WRITE: - if (!bev_ssl->underlying) { - stop_reading(bev_ssl); - return start_writing(bev_ssl); - } - return 0; + stop_reading(bev_ssl); + return start_writing(bev_ssl); case SSL_ERROR_WANT_READ: - if (!bev_ssl->underlying) { - stop_writing(bev_ssl); - return start_reading(bev_ssl); - } - return 0; + stop_writing(bev_ssl); + return start_reading(bev_ssl); default: conn_closed(bev_ssl, BEV_EVENT_READING, err, r); return -1; @@ -1086,6 +1084,13 @@ set_handshake_callbacks(struct bufferevent_openssl *bev_ssl, evutil_socket_t fd) be_openssl_handshakecb, be_openssl_handshakecb, be_openssl_eventcb, bev_ssl); + + if (fd < 0) + return 0; + + if (bufferevent_setfd(bev_ssl->underlying, fd)) + return 1; + return do_handshake(bev_ssl); } else { struct bufferevent *bev = &bev_ssl->bev.bev; @@ -1131,10 +1136,13 @@ be_openssl_outbuf_cb(struct evbuffer *buf, int r = 0; /* XXX need to hold a reference here. */ - if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN && - cbinfo->orig_size == 0) { - r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write, - &bev_ssl->bev.bev.timeout_write); + if (cbinfo->n_added && bev_ssl->state == BUFFEREVENT_SSL_OPEN) { + if (cbinfo->orig_size == 0) + r = bufferevent_add_event_(&bev_ssl->bev.bev.ev_write, + &bev_ssl->bev.bev.timeout_write); + + if (bev_ssl->underlying) + consider_writing(bev_ssl); } /* XXX Handle r < 0 */ (void)r; @@ -1286,17 +1294,24 @@ be_openssl_ctrl(struct bufferevent *bev, struct bufferevent_openssl *bev_ssl = upcast(bev); switch (op) { case BEV_CTRL_SET_FD: - if (bev_ssl->underlying) - return -1; - { + if (!bev_ssl->underlying) { BIO *bio; bio = BIO_new_socket(data->fd, 0); SSL_set_bio(bev_ssl->ssl, bio, bio); + } else { + BIO *bio; + if (!(bio = BIO_new_bufferevent(bev_ssl->underlying, 0))) + return -1; + SSL_set_bio(bev_ssl->ssl, bio, bio); } return be_openssl_set_fd(bev_ssl, bev_ssl->old_state, data->fd); case BEV_CTRL_GET_FD: - data->fd = event_get_fd(&bev->ev_read); + if (bev_ssl->underlying) { + data->fd = event_get_fd(&bev_ssl->underlying->ev_read); + } else { + data->fd = event_get_fd(&bev->ev_read); + } return 0; case BEV_CTRL_GET_UNDERLYING: data->ptr = bev_ssl->underlying; diff --git a/bufferevent_sock.c b/bufferevent_sock.c index 58095f37..93aedb33 100644 --- a/bufferevent_sock.c +++ b/bufferevent_sock.c @@ -249,8 +249,8 @@ bufferevent_writecb(evutil_socket_t fd, short event, void *arg) /* we need to fake the error if the connection was refused * immediately - usually connection to localhost on BSD */ if (bufev_p->connection_refused) { - bufev_p->connection_refused = 0; - c = -1; + bufev_p->connection_refused = 0; + c = -1; } if (c == 0) @@ -438,13 +438,12 @@ bufferevent_socket_connect(struct bufferevent *bev, /* The connect succeeded already. How very BSD of it. */ result = 0; bufev_p->connecting = 1; - event_active(&bev->ev_write, EV_WRITE, 1); + bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS); } else { /* The connect failed already. How very BSD of it. */ - bufev_p->connection_refused = 1; - bufev_p->connecting = 1; result = 0; - event_active(&bev->ev_write, EV_WRITE, 1); + bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, BEV_OPT_DEFER_CALLBACKS); + bufferevent_disable(bev, EV_WRITE|EV_READ); } goto done; diff --git a/test/regress_http.c b/test/regress_http.c index b88ec1bc..80100500 100644 --- a/test/regress_http.c +++ b/test/regress_http.c @@ -85,6 +85,7 @@ static void http_on_complete_cb(struct evhttp_request *req, void *arg); #define HTTP_BIND_IPV6 1 #define HTTP_BIND_SSL 2 +#define HTTP_SSL_FILTER 4 static int http_bind(struct evhttp *myhttp, ev_uint16_t *pport, int mask) { @@ -424,18 +425,25 @@ http_complete_write(evutil_socket_t fd, short what, void *arg) } static struct bufferevent * -create_bev(struct event_base *base, int fd, int ssl) +create_bev(struct event_base *base, int fd, int ssl_mask) { int flags = BEV_OPT_DEFER_CALLBACKS; struct bufferevent *bev = NULL; - if (!ssl) { + if (!ssl_mask) { bev = bufferevent_socket_new(base, fd, flags); } else { #ifdef EVENT__HAVE_OPENSSL SSL *ssl = SSL_new(get_ssl_ctx()); - bev = bufferevent_openssl_socket_new( - base, fd, ssl, BUFFEREVENT_SSL_CONNECTING, flags); + if (ssl_mask & HTTP_SSL_FILTER) { + struct bufferevent *underlying = + bufferevent_socket_new(base, fd, flags); + bev = bufferevent_openssl_filter_new( + base, underlying, ssl, BUFFEREVENT_SSL_CONNECTING, flags); + } else { + bev = bufferevent_openssl_socket_new( + base, fd, ssl, BUFFEREVENT_SSL_CONNECTING, flags); + } bufferevent_openssl_set_allow_dirty_shutdown(bev, 1); #endif } @@ -4519,6 +4527,8 @@ http_request_own_test(void *arg) #ifdef EVENT__HAVE_OPENSSL static void https_basic_test(void *arg) { return http_basic_test_impl(arg, 1); } +static void https_filter_basic_test(void *arg) +{ return http_basic_test_impl(arg, 1 | HTTP_SSL_FILTER); } static void https_incomplete_test(void *arg) { http_incomplete_test_(arg, 0, 1); } static void https_incomplete_timeout_test(void *arg) @@ -4533,6 +4543,8 @@ static void https_connection_retry_test(void *arg) { return http_connection_retry_test_impl(arg, 1); } static void https_chunk_out_test(void *arg) { return http_chunk_out_test_impl(arg, 1); } +static void https_filter_chunk_out_test(void *arg) +{ return http_chunk_out_test_impl(arg, 1 | HTTP_SSL_FILTER); } static void https_stream_out_test(void *arg) { return http_stream_out_test_impl(arg, 1); } static void https_connection_fail_test(void *arg) @@ -4620,6 +4632,7 @@ struct testcase_t http_testcases[] = { #ifdef EVENT__HAVE_OPENSSL HTTPS(basic), + HTTPS(filter_basic), HTTPS(simple), HTTPS(simple_dirty), HTTPS(incomplete), @@ -4628,6 +4641,7 @@ struct testcase_t http_testcases[] = { { "https_connection_retry_conn_address", https_connection_retry_conn_address_test, TT_ISOLATED|TT_OFF_BY_DEFAULT, &basic_setup, NULL }, HTTPS(chunk_out), + HTTPS(filter_chunk_out), HTTPS(stream_out), HTTPS(connection_fail), HTTPS(write_during_read), diff --git a/test/regress_ssl.c b/test/regress_ssl.c index 8a5524a8..681705fc 100644 --- a/test/regress_ssl.c +++ b/test/regress_ssl.c @@ -227,19 +227,20 @@ enum regress_openssl_type REGRESS_OPENSSL_FREED = 256, REGRESS_OPENSSL_TIMEOUT = 512, REGRESS_OPENSSL_SLEEP = 1024, + + REGRESS_OPENSSL_CLIENT_WRITE = 2048, }; static void bufferevent_openssl_check_fd(struct bufferevent *bev, int filter) { + tt_int_op(bufferevent_getfd(bev), !=, -1); + tt_int_op(bufferevent_setfd(bev, -1), ==, 0); if (filter) { - tt_int_op(bufferevent_getfd(bev), ==, -1); - tt_int_op(bufferevent_setfd(bev, -1), ==, -1); - } else { tt_int_op(bufferevent_getfd(bev), !=, -1); - tt_int_op(bufferevent_setfd(bev, -1), ==, 0); + } else { + tt_int_op(bufferevent_getfd(bev), ==, -1); } - tt_int_op(bufferevent_getfd(bev), ==, -1); end: ; @@ -322,6 +323,9 @@ eventcb(struct bufferevent *bev, short what, void *ctx) if (--pending_connect_events == 0) event_base_loopexit(exit_base, NULL); } + + if ((type & REGRESS_OPENSSL_CLIENT_WRITE) && (type & REGRESS_OPENSSL_CLIENT)) + evbuffer_add_printf(bufferevent_get_output(bev), "1\n"); } else if (what & BEV_EVENT_EOF) { TT_BLATHER(("Got a good EOF")); ++got_close; @@ -465,7 +469,8 @@ regress_bufferevent_openssl(void *arg) bufferevent_enable(bev1, EV_READ|EV_WRITE); bufferevent_enable(bev2, EV_READ|EV_WRITE); - evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); + if (!(type & REGRESS_OPENSSL_CLIENT_WRITE)) + evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); event_base_dispatch(data->base); @@ -488,7 +493,8 @@ regress_bufferevent_openssl(void *arg) bufferevent_set_timeouts(bev1, &t, &t); - evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); + if (!(type & REGRESS_OPENSSL_CLIENT_WRITE)) + evbuffer_add_printf(bufferevent_get_output(bev1), "1\n"); event_base_dispatch(data->base); @@ -731,8 +737,14 @@ struct testcase_t ssl_testcases[] = { #define T(a) ((void *)(a)) { "bufferevent_socketpair", regress_bufferevent_openssl, TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_SOCKETPAIR) }, + { "bufferevent_socketpair_write_after_connect", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_SOCKETPAIR|REGRESS_OPENSSL_CLIENT_WRITE) }, { "bufferevent_filter", regress_bufferevent_openssl, TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_FILTER) }, + { "bufferevent_filter_write_after_connect", regress_bufferevent_openssl, + TT_ISOLATED, &basic_setup, + T(REGRESS_OPENSSL_FILTER|REGRESS_OPENSSL_CLIENT_WRITE) }, { "bufferevent_renegotiate_socketpair", regress_bufferevent_openssl, TT_ISOLATED, &basic_setup, T(REGRESS_OPENSSL_SOCKETPAIR | REGRESS_OPENSSL_RENEGOTIATE) },