diff --git a/daemon/http.c b/daemon/http.c index 8bc3f8c946c974a68e0ad02c8396fb21c1bdfa7f..14e60babde02a23eefcaa5ac25db270ab2288bd1 100644 --- a/daemon/http.c +++ b/daemon/http.c @@ -1,4 +1,5 @@ /* + * * Copyright (C) 2020 CZ.NIC, z.s.p.o * * Initial Author: Jan Hák <jan.hak@nic.cz> @@ -424,7 +425,6 @@ static void on_pkt_write(struct http_data *data, int status) return; data->on_write(data->req, status); - free(data); } @@ -589,22 +589,33 @@ static int http_send_response(nghttp2_session *h2, int32_t stream_id, MAKE_NV("cache-control", 13, max_age, max_age_len), }; - ret = nghttp2_submit_response(h2, stream_id, hdrs, sizeof(hdrs)/sizeof(*hdrs), prov); + ret = nghttp2_session_set_stream_user_data(h2, stream_id, (void*)data); if (ret != 0) { - kr_log_verbose("[http] nghttp2_submit_response failed: %s\n", nghttp2_strerror(ret)); + kr_log_verbose("[http] failed to set stream user data: %s\n", nghttp2_strerror(ret)); + free(data); return kr_error(EIO); } - ret = nghttp2_session_set_stream_user_data(h2, stream_id, (void*)data); + ret = nghttp2_submit_response(h2, stream_id, hdrs, sizeof(hdrs)/sizeof(*hdrs), prov); if (ret != 0) { - kr_log_verbose("[http] failed to set stream user data: %s\n", nghttp2_strerror(ret)); + kr_log_verbose("[http] nghttp2_submit_response failed: %s\n", nghttp2_strerror(ret)); + nghttp2_session_set_stream_user_data(h2, stream_id, NULL); // just in case + free(data); return kr_error(EIO); } ret = nghttp2_session_send(h2); if(ret < 0) { kr_log_verbose("[http] nghttp2_session_send failed: %s\n", nghttp2_strerror(ret)); - return kr_error(EIO); + + /* At this point, there was an error in some nghttp2 callback. The on_pkt_write() + * callback which also calls free(data) may or may not have been called. Therefore, + * we must guarantee it will have been called by explicitly closing the stream. + * Afterwards, we have no option but to pretend this function was a success. If we + * returned an error, qr_task_send() logic would lead to a double-free because + * on_write() was already called. */ + nghttp2_submit_rst_stream(h2, NGHTTP2_FLAG_NONE, stream_id, NGHTTP2_INTERNAL_ERROR); + return 0; } return 0; @@ -612,6 +623,10 @@ static int http_send_response(nghttp2_session *h2, int32_t stream_id, /* * Send HTTP/2 stream data created from packet's wire buffer. + * + * If this function returns an error, the on_write() callback isn't (and + * musn't be!) called, since such errors are handled in an upper layer - in + * qr_task_step() in daemon/worker. */ static int http_write_pkt(nghttp2_session *h2, knot_pkt_t *pkt, int32_t stream_id, uv_write_t *req, uv_write_cb on_write)