h1_stream.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/http/private/h1_stream.h>
  6. #include <aws/http/private/h1_connection.h>
  7. #include <aws/http/private/h1_encoder.h>
  8. #include <aws/http/status_code.h>
  9. #include <aws/io/logging.h>
  10. #include <aws/io/stream.h>
  11. #include <inttypes.h>
  12. static void s_stream_destroy(struct aws_http_stream *stream_base) {
  13. struct aws_h1_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h1_stream, base);
  14. AWS_ASSERT(
  15. stream->synced_data.api_state != AWS_H1_STREAM_API_STATE_ACTIVE &&
  16. "Stream should be complete (or never-activated) when stream destroyed");
  17. AWS_ASSERT(
  18. aws_linked_list_empty(&stream->thread_data.pending_chunk_list) &&
  19. aws_linked_list_empty(&stream->synced_data.pending_chunk_list) &&
  20. "Chunks should be marked complete before stream destroyed");
  21. aws_h1_encoder_message_clean_up(&stream->encoder_message);
  22. aws_byte_buf_clean_up(&stream->incoming_storage_buf);
  23. aws_mem_release(stream->base.alloc, stream);
  24. }
  25. static struct aws_h1_connection *s_get_h1_connection(const struct aws_h1_stream *stream) {
  26. return AWS_CONTAINER_OF(stream->base.owning_connection, struct aws_h1_connection, base);
  27. }
  28. static void s_stream_lock_synced_data(struct aws_h1_stream *stream) {
  29. aws_h1_connection_lock_synced_data(s_get_h1_connection(stream));
  30. }
  31. static void s_stream_unlock_synced_data(struct aws_h1_stream *stream) {
  32. aws_h1_connection_unlock_synced_data(s_get_h1_connection(stream));
  33. }
  34. static void s_stream_cross_thread_work_task(struct aws_channel_task *task, void *arg, enum aws_task_status status) {
  35. (void)task;
  36. struct aws_h1_stream *stream = arg;
  37. struct aws_h1_connection *connection = s_get_h1_connection(stream);
  38. if (status != AWS_TASK_STATUS_RUN_READY) {
  39. goto done;
  40. }
  41. AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "id=%p: Running stream cross-thread work task.", (void *)&stream->base);
  42. /* BEGIN CRITICAL SECTION */
  43. s_stream_lock_synced_data(stream);
  44. stream->synced_data.is_cross_thread_work_task_scheduled = false;
  45. int api_state = stream->synced_data.api_state;
  46. bool found_chunks = !aws_linked_list_empty(&stream->synced_data.pending_chunk_list);
  47. aws_linked_list_move_all_back(&stream->thread_data.pending_chunk_list, &stream->synced_data.pending_chunk_list);
  48. stream->encoder_message.trailer = stream->synced_data.pending_trailer;
  49. stream->synced_data.pending_trailer = NULL;
  50. bool has_outgoing_response = stream->synced_data.has_outgoing_response;
  51. uint64_t pending_window_update = stream->synced_data.pending_window_update;
  52. stream->synced_data.pending_window_update = 0;
  53. s_stream_unlock_synced_data(stream);
  54. /* END CRITICAL SECTION */
  55. /* If we have any new outgoing data, prompt the connection to try and send it. */
  56. bool new_outgoing_data = found_chunks;
  57. /* If we JUST learned about having an outgoing response, that's a reason to try sending data */
  58. if (has_outgoing_response && !stream->thread_data.has_outgoing_response) {
  59. stream->thread_data.has_outgoing_response = true;
  60. new_outgoing_data = true;
  61. }
  62. if (new_outgoing_data && (api_state == AWS_H1_STREAM_API_STATE_ACTIVE)) {
  63. aws_h1_connection_try_write_outgoing_stream(connection);
  64. }
  65. /* Add to window size using saturated sum to prevent overflow.
  66. * Saturating is fine because it's a u64, the stream could never receive that much data. */
  67. stream->thread_data.stream_window =
  68. aws_add_u64_saturating(stream->thread_data.stream_window, pending_window_update);
  69. if ((pending_window_update > 0) && (api_state == AWS_H1_STREAM_API_STATE_ACTIVE)) {
  70. /* Now that stream window is larger, connection might have buffered
  71. * data to send, or might need to increment its own window */
  72. aws_h1_connection_try_process_read_messages(connection);
  73. }
  74. done:
  75. /* Release reference that kept stream alive until task ran */
  76. aws_http_stream_release(&stream->base);
  77. }
  78. /* Note the update in synced_data, and schedule the cross_thread_work_task if necessary */
  79. static void s_stream_update_window(struct aws_http_stream *stream_base, size_t increment_size) {
  80. if (increment_size == 0) {
  81. return;
  82. }
  83. if (!stream_base->owning_connection->stream_manual_window_management) {
  84. return;
  85. }
  86. struct aws_h1_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h1_stream, base);
  87. bool should_schedule_task = false;
  88. { /* BEGIN CRITICAL SECTION */
  89. s_stream_lock_synced_data(stream);
  90. /* Saturated sum. It's a u64. The stream could never receive that much data. */
  91. stream->synced_data.pending_window_update =
  92. aws_add_u64_saturating(stream->synced_data.pending_window_update, increment_size);
  93. /* Don't alert the connection unless the stream is active */
  94. if (stream->synced_data.api_state == AWS_H1_STREAM_API_STATE_ACTIVE) {
  95. if (!stream->synced_data.is_cross_thread_work_task_scheduled) {
  96. stream->synced_data.is_cross_thread_work_task_scheduled = true;
  97. should_schedule_task = true;
  98. }
  99. }
  100. s_stream_unlock_synced_data(stream);
  101. } /* END CRITICAL SECTION */
  102. if (should_schedule_task) {
  103. /* Keep stream alive until task completes */
  104. aws_atomic_fetch_add(&stream->base.refcount, 1);
  105. AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "id=%p: Scheduling stream cross-thread work task.", (void *)stream_base);
  106. aws_channel_schedule_task_now(
  107. stream->base.owning_connection->channel_slot->channel, &stream->cross_thread_work_task);
  108. }
  109. }
  110. static int s_stream_write_chunk(struct aws_http_stream *stream_base, const struct aws_http1_chunk_options *options) {
  111. AWS_PRECONDITION(stream_base);
  112. AWS_PRECONDITION(options);
  113. struct aws_h1_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h1_stream, base);
  114. if (options->chunk_data == NULL && options->chunk_data_size > 0) {
  115. AWS_LOGF_ERROR(
  116. AWS_LS_HTTP_STREAM, "id=%p: Chunk data cannot be NULL if data size is non-zero", (void *)stream_base);
  117. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  118. }
  119. struct aws_h1_chunk *chunk = aws_h1_chunk_new(stream_base->alloc, options);
  120. if (AWS_UNLIKELY(NULL == chunk)) {
  121. AWS_LOGF_ERROR(
  122. AWS_LS_HTTP_STREAM,
  123. "id=%p: Failed to initialize streamed chunk, error %d (%s).",
  124. (void *)stream_base,
  125. aws_last_error(),
  126. aws_error_name(aws_last_error()));
  127. return AWS_OP_ERR;
  128. }
  129. int error_code = 0;
  130. bool should_schedule_task = false;
  131. { /* BEGIN CRITICAL SECTION */
  132. s_stream_lock_synced_data(stream);
  133. /* Can only add chunks while stream is active. */
  134. if (stream->synced_data.api_state != AWS_H1_STREAM_API_STATE_ACTIVE) {
  135. error_code = (stream->synced_data.api_state == AWS_H1_STREAM_API_STATE_INIT)
  136. ? AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED
  137. : AWS_ERROR_HTTP_STREAM_HAS_COMPLETED;
  138. goto unlock;
  139. }
  140. /* Prevent user trying to submit chunks without having set the required headers.
  141. * This check also prevents a server-user submitting chunks before the response has been submitted. */
  142. if (!stream->synced_data.using_chunked_encoding) {
  143. AWS_LOGF_ERROR(
  144. AWS_LS_HTTP_STREAM,
  145. "id=%p: Cannot write chunks without 'transfer-encoding: chunked' header.",
  146. (void *)stream_base);
  147. error_code = AWS_ERROR_INVALID_STATE;
  148. goto unlock;
  149. }
  150. if (stream->synced_data.has_final_chunk) {
  151. AWS_LOGF_ERROR(
  152. AWS_LS_HTTP_STREAM, "id=%p: Cannot write additional chunk after final chunk.", (void *)stream_base);
  153. error_code = AWS_ERROR_INVALID_STATE;
  154. goto unlock;
  155. }
  156. /* success */
  157. if (chunk->data_size == 0) {
  158. stream->synced_data.has_final_chunk = true;
  159. }
  160. aws_linked_list_push_back(&stream->synced_data.pending_chunk_list, &chunk->node);
  161. should_schedule_task = !stream->synced_data.is_cross_thread_work_task_scheduled;
  162. stream->synced_data.is_cross_thread_work_task_scheduled = true;
  163. unlock:
  164. s_stream_unlock_synced_data(stream);
  165. } /* END CRITICAL SECTION */
  166. if (error_code) {
  167. AWS_LOGF_ERROR(
  168. AWS_LS_HTTP_STREAM,
  169. "id=%p: Failed to add chunk, error %d (%s)",
  170. (void *)stream_base,
  171. error_code,
  172. aws_error_name(error_code));
  173. aws_h1_chunk_destroy(chunk);
  174. return aws_raise_error(error_code);
  175. }
  176. AWS_LOGF_TRACE(
  177. AWS_LS_HTTP_STREAM,
  178. "id=%p: Adding chunk with size %" PRIu64 " to stream",
  179. (void *)stream,
  180. options->chunk_data_size);
  181. if (should_schedule_task) {
  182. /* Keep stream alive until task completes */
  183. aws_atomic_fetch_add(&stream->base.refcount, 1);
  184. AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "id=%p: Scheduling stream cross-thread work task.", (void *)stream_base);
  185. aws_channel_schedule_task_now(
  186. stream->base.owning_connection->channel_slot->channel, &stream->cross_thread_work_task);
  187. } else {
  188. AWS_LOGF_TRACE(
  189. AWS_LS_HTTP_STREAM, "id=%p: Stream cross-thread work task was already scheduled.", (void *)stream_base);
  190. }
  191. return AWS_OP_SUCCESS;
  192. }
  193. static int s_stream_add_trailer(struct aws_http_stream *stream_base, const struct aws_http_headers *trailing_headers) {
  194. AWS_PRECONDITION(stream_base);
  195. AWS_PRECONDITION(trailing_headers);
  196. struct aws_h1_stream *stream = AWS_CONTAINER_OF(stream_base, struct aws_h1_stream, base);
  197. struct aws_h1_trailer *trailer = aws_h1_trailer_new(stream_base->alloc, trailing_headers);
  198. if (AWS_UNLIKELY(NULL == trailer)) {
  199. AWS_LOGF_ERROR(
  200. AWS_LS_HTTP_STREAM,
  201. "id=%p: Failed to initialize streamed trailer, error %d (%s).",
  202. (void *)stream_base,
  203. aws_last_error(),
  204. aws_error_name(aws_last_error()));
  205. return AWS_OP_ERR;
  206. }
  207. int error_code = 0;
  208. bool should_schedule_task = false;
  209. { /* BEGIN CRITICAL SECTION */
  210. s_stream_lock_synced_data(stream);
  211. /* Can only add trailers while stream is active. */
  212. if (stream->synced_data.api_state != AWS_H1_STREAM_API_STATE_ACTIVE) {
  213. error_code = (stream->synced_data.api_state == AWS_H1_STREAM_API_STATE_INIT)
  214. ? AWS_ERROR_HTTP_STREAM_NOT_ACTIVATED
  215. : AWS_ERROR_HTTP_STREAM_HAS_COMPLETED;
  216. goto unlock;
  217. }
  218. if (!stream->synced_data.using_chunked_encoding) {
  219. AWS_LOGF_ERROR(
  220. AWS_LS_HTTP_STREAM,
  221. "id=%p: Cannot write trailers without 'transfer-encoding: chunked' header.",
  222. (void *)stream_base);
  223. error_code = AWS_ERROR_INVALID_STATE;
  224. goto unlock;
  225. }
  226. if (stream->synced_data.has_added_trailer) {
  227. AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Cannot write trailers twice.", (void *)stream_base);
  228. error_code = AWS_ERROR_INVALID_STATE;
  229. goto unlock;
  230. }
  231. if (stream->synced_data.has_final_chunk) {
  232. AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Cannot write trailers after final chunk.", (void *)stream_base);
  233. error_code = AWS_ERROR_INVALID_STATE;
  234. goto unlock;
  235. }
  236. stream->synced_data.has_added_trailer = true;
  237. stream->synced_data.pending_trailer = trailer;
  238. should_schedule_task = !stream->synced_data.is_cross_thread_work_task_scheduled;
  239. stream->synced_data.is_cross_thread_work_task_scheduled = true;
  240. unlock:
  241. s_stream_unlock_synced_data(stream);
  242. } /* END CRITICAL SECTION */
  243. if (error_code) {
  244. AWS_LOGF_ERROR(
  245. AWS_LS_HTTP_STREAM,
  246. "id=%p: Failed to add trailer, error %d (%s)",
  247. (void *)stream_base,
  248. error_code,
  249. aws_error_name(error_code));
  250. aws_h1_trailer_destroy(trailer);
  251. return aws_raise_error(error_code);
  252. }
  253. AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "id=%p: Adding trailer to stream", (void *)stream);
  254. if (should_schedule_task) {
  255. /* Keep stream alive until task completes */
  256. aws_atomic_fetch_add(&stream->base.refcount, 1);
  257. AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "id=%p: Scheduling stream cross-thread work task.", (void *)stream_base);
  258. aws_channel_schedule_task_now(
  259. stream->base.owning_connection->channel_slot->channel, &stream->cross_thread_work_task);
  260. } else {
  261. AWS_LOGF_TRACE(
  262. AWS_LS_HTTP_STREAM, "id=%p: Stream cross-thread work task was already scheduled.", (void *)stream_base);
  263. }
  264. return AWS_OP_SUCCESS;
  265. }
  266. static const struct aws_http_stream_vtable s_stream_vtable = {
  267. .destroy = s_stream_destroy,
  268. .update_window = s_stream_update_window,
  269. .activate = aws_h1_stream_activate,
  270. .http1_write_chunk = s_stream_write_chunk,
  271. .http1_add_trailer = s_stream_add_trailer,
  272. .http2_reset_stream = NULL,
  273. .http2_get_received_error_code = NULL,
  274. .http2_get_sent_error_code = NULL,
  275. };
  276. static struct aws_h1_stream *s_stream_new_common(
  277. struct aws_http_connection *connection_base,
  278. void *user_data,
  279. aws_http_on_incoming_headers_fn *on_incoming_headers,
  280. aws_http_on_incoming_header_block_done_fn *on_incoming_header_block_done,
  281. aws_http_on_incoming_body_fn *on_incoming_body,
  282. aws_http_on_stream_complete_fn *on_complete,
  283. aws_http_on_stream_destroy_fn *on_destroy) {
  284. struct aws_h1_connection *connection = AWS_CONTAINER_OF(connection_base, struct aws_h1_connection, base);
  285. struct aws_h1_stream *stream = aws_mem_calloc(connection_base->alloc, 1, sizeof(struct aws_h1_stream));
  286. if (!stream) {
  287. return NULL;
  288. }
  289. stream->base.vtable = &s_stream_vtable;
  290. stream->base.alloc = connection_base->alloc;
  291. stream->base.owning_connection = connection_base;
  292. stream->base.user_data = user_data;
  293. stream->base.on_incoming_headers = on_incoming_headers;
  294. stream->base.on_incoming_header_block_done = on_incoming_header_block_done;
  295. stream->base.on_incoming_body = on_incoming_body;
  296. stream->base.on_complete = on_complete;
  297. stream->base.on_destroy = on_destroy;
  298. aws_channel_task_init(
  299. &stream->cross_thread_work_task, s_stream_cross_thread_work_task, stream, "http1_stream_cross_thread_work");
  300. aws_linked_list_init(&stream->thread_data.pending_chunk_list);
  301. aws_linked_list_init(&stream->synced_data.pending_chunk_list);
  302. stream->thread_data.stream_window = connection->initial_stream_window_size;
  303. /* Stream refcount starts at 1 for user and is incremented upon activation for the connection */
  304. aws_atomic_init_int(&stream->base.refcount, 1);
  305. return stream;
  306. }
  307. struct aws_h1_stream *aws_h1_stream_new_request(
  308. struct aws_http_connection *client_connection,
  309. const struct aws_http_make_request_options *options) {
  310. struct aws_h1_stream *stream = s_stream_new_common(
  311. client_connection,
  312. options->user_data,
  313. options->on_response_headers,
  314. options->on_response_header_block_done,
  315. options->on_response_body,
  316. options->on_complete,
  317. options->on_destroy);
  318. if (!stream) {
  319. return NULL;
  320. }
  321. /* Transform request if necessary */
  322. if (client_connection->proxy_request_transform) {
  323. if (client_connection->proxy_request_transform(options->request, client_connection->user_data)) {
  324. goto error;
  325. }
  326. }
  327. stream->base.client_data = &stream->base.client_or_server_data.client;
  328. stream->base.client_data->response_status = AWS_HTTP_STATUS_CODE_UNKNOWN;
  329. /* Validate request and cache info that the encoder will eventually need */
  330. if (aws_h1_encoder_message_init_from_request(
  331. &stream->encoder_message,
  332. client_connection->alloc,
  333. options->request,
  334. &stream->thread_data.pending_chunk_list)) {
  335. goto error;
  336. }
  337. /* RFC-7230 Section 6.3: The "close" connection option is used to signal
  338. * that a connection will not persist after the current request/response*/
  339. if (stream->encoder_message.has_connection_close_header) {
  340. stream->is_final_stream = true;
  341. }
  342. stream->synced_data.using_chunked_encoding = stream->encoder_message.has_chunked_encoding_header;
  343. return stream;
  344. error:
  345. s_stream_destroy(&stream->base);
  346. return NULL;
  347. }
  348. struct aws_h1_stream *aws_h1_stream_new_request_handler(const struct aws_http_request_handler_options *options) {
  349. struct aws_h1_stream *stream = s_stream_new_common(
  350. options->server_connection,
  351. options->user_data,
  352. options->on_request_headers,
  353. options->on_request_header_block_done,
  354. options->on_request_body,
  355. options->on_complete,
  356. options->on_destroy);
  357. if (!stream) {
  358. return NULL;
  359. }
  360. /* This code is only executed in server mode and can only be invoked from the event-loop thread so don't worry
  361. * with the lock here. */
  362. stream->base.id = aws_http_connection_get_next_stream_id(options->server_connection);
  363. /* Request-handler (server) streams don't need user to call activate() on them.
  364. * Since these these streams can only be created on the event-loop thread,
  365. * it's not possible for callbacks to fire before the stream pointer is returned.
  366. * (Clients must call stream.activate() because they might create a stream on any thread) */
  367. stream->synced_data.api_state = AWS_H1_STREAM_API_STATE_ACTIVE;
  368. stream->base.server_data = &stream->base.client_or_server_data.server;
  369. stream->base.server_data->on_request_done = options->on_request_done;
  370. aws_atomic_fetch_add(&stream->base.refcount, 1);
  371. return stream;
  372. }
  373. int aws_h1_stream_send_response(struct aws_h1_stream *stream, struct aws_http_message *response) {
  374. struct aws_h1_connection *connection = s_get_h1_connection(stream);
  375. int error_code = 0;
  376. /* Validate the response and cache info that encoder will eventually need.
  377. * The encoder_message object will be moved into the stream later while holding the lock */
  378. struct aws_h1_encoder_message encoder_message;
  379. bool body_headers_ignored = stream->base.request_method == AWS_HTTP_METHOD_HEAD;
  380. if (aws_h1_encoder_message_init_from_response(
  381. &encoder_message,
  382. stream->base.alloc,
  383. response,
  384. body_headers_ignored,
  385. &stream->thread_data.pending_chunk_list)) {
  386. error_code = aws_last_error();
  387. goto error;
  388. }
  389. bool should_schedule_task = false;
  390. { /* BEGIN CRITICAL SECTION */
  391. s_stream_lock_synced_data(stream);
  392. if (stream->synced_data.api_state == AWS_H1_STREAM_API_STATE_COMPLETE) {
  393. error_code = AWS_ERROR_HTTP_STREAM_HAS_COMPLETED;
  394. } else if (stream->synced_data.has_outgoing_response) {
  395. AWS_LOGF_ERROR(AWS_LS_HTTP_STREAM, "id=%p: Response already created on the stream", (void *)&stream->base);
  396. error_code = AWS_ERROR_INVALID_STATE;
  397. } else {
  398. stream->synced_data.has_outgoing_response = true;
  399. stream->encoder_message = encoder_message;
  400. if (encoder_message.has_connection_close_header) {
  401. /* This will be the last stream connection will process, new streams will be rejected */
  402. stream->is_final_stream = true;
  403. /* Note: We're touching the connection's synced_data, which is OK
  404. * because an h1_connection and all its h1_streams share a single lock. */
  405. connection->synced_data.new_stream_error_code = AWS_ERROR_HTTP_CONNECTION_CLOSED;
  406. }
  407. stream->synced_data.using_chunked_encoding = stream->encoder_message.has_chunked_encoding_header;
  408. should_schedule_task = !stream->synced_data.is_cross_thread_work_task_scheduled;
  409. stream->synced_data.is_cross_thread_work_task_scheduled = true;
  410. }
  411. s_stream_unlock_synced_data(stream);
  412. } /* END CRITICAL SECTION */
  413. if (error_code) {
  414. goto error;
  415. }
  416. /* Success! */
  417. AWS_LOGF_DEBUG(
  418. AWS_LS_HTTP_STREAM, "id=%p: Created response on connection=%p: ", (void *)stream, (void *)connection);
  419. if (should_schedule_task) {
  420. /* Keep stream alive until task completes */
  421. aws_atomic_fetch_add(&stream->base.refcount, 1);
  422. AWS_LOGF_TRACE(AWS_LS_HTTP_STREAM, "id=%p: Scheduling stream cross-thread work task.", (void *)&stream->base);
  423. aws_channel_schedule_task_now(
  424. stream->base.owning_connection->channel_slot->channel, &stream->cross_thread_work_task);
  425. } else {
  426. AWS_LOGF_TRACE(
  427. AWS_LS_HTTP_STREAM, "id=%p: Stream cross-thread work task was already scheduled.", (void *)&stream->base);
  428. }
  429. return AWS_OP_SUCCESS;
  430. error:
  431. AWS_LOGF_ERROR(
  432. AWS_LS_HTTP_STREAM,
  433. "id=%p: Sending response on the stream failed, error %d (%s)",
  434. (void *)&stream->base,
  435. error_code,
  436. aws_error_name(error_code));
  437. aws_h1_encoder_message_clean_up(&encoder_message);
  438. return aws_raise_error(error_code);
  439. }