apr_status_t h2_session_goaway(h2_session *session, apr_status_t reason) { assert(session); apr_status_t status = APR_SUCCESS; if (session->aborted) { return APR_EINVAL; } int rv = 0; if (reason == APR_SUCCESS) { rv = nghttp2_submit_shutdown_notice(session->ngh2); } else { int err = 0; int last_id = nghttp2_session_get_last_proc_stream_id(session->ngh2); rv = nghttp2_submit_goaway(session->ngh2, last_id, NGHTTP2_FLAG_NONE, err, NULL, 0); } if (rv != 0) { status = APR_EGENERAL; ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, "session(%ld): submit goaway: %s", session->id, nghttp2_strerror(rv)); } return status; }
static apr_status_t h2_session_abort_int(h2_session *session, int reason) { AP_DEBUG_ASSERT(session); if (!session->aborted) { session->aborted = 1; if (session->ngh2) { if (!reason) { nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, session->max_stream_received, reason, NULL, 0); nghttp2_session_send(session->ngh2); h2_conn_io_flush(&session->io); } else { const char *err = nghttp2_strerror(reason); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "session(%ld): aborting session, reason=%d %s", session->id, reason, err); if (NGHTTP2_ERR_EOF == reason) { /* This is our way of indication that the connection is * gone. No use to send any GOAWAY frames. */ nghttp2_session_terminate_session(session->ngh2, reason); } else { /* The connection might still be there and we shut down * with GOAWAY and reason information. */ nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE, session->max_stream_received, reason, (const uint8_t *)err, strlen(err)); nghttp2_session_send(session->ngh2); h2_conn_io_flush(&session->io); } } } h2_mplx_abort(session->mplx); } return APR_SUCCESS; }
static void run_nghttp2_session_send(void) { nghttp2_session *session; nghttp2_session_callbacks callbacks; nghttp2_nv nv[] = {MAKE_NV(":host", "example.org"), MAKE_NV(":scheme", "https")}; nghttp2_data_provider data_prd; nghttp2_settings_entry iv[2]; my_user_data ud; int rv; memset(&callbacks, 0, sizeof(nghttp2_session_callbacks)); callbacks.send_callback = null_send_callback; data_prd.read_callback = fixed_length_data_source_read_callback; ud.data_source_length = 64 * 1024; iv[0].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE; iv[0].value = 4096; iv[1].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; iv[1].value = 100; rv = nghttp2_session_client_new3(&session, &callbacks, &ud, NULL, nghttp2_mem_fm()); if (rv != 0) { goto client_new_fail; } rv = nghttp2_submit_request(session, NULL, nv, ARRLEN(nv), &data_prd, NULL); if (rv < 0) { goto fail; } rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, -1, NULL, nv, ARRLEN(nv), NULL); if (rv < 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } /* The HEADERS submitted by the previous nghttp2_submit_headers will have stream ID 3. Send HEADERS to that stream. */ rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, ARRLEN(nv), NULL); if (rv != 0) { goto fail; } rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, 3, NGHTTP2_CANCEL); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } /* Sending against half-closed stream */ rv = nghttp2_submit_headers(session, NGHTTP2_FLAG_NONE, 3, NULL, nv, ARRLEN(nv), NULL); if (rv != 0) { goto fail; } rv = nghttp2_submit_data(session, NGHTTP2_FLAG_END_STREAM, 3, &data_prd); if (rv != 0) { goto fail; } rv = nghttp2_submit_ping(session, NGHTTP2_FLAG_NONE, NULL); if (rv != 0) { goto fail; } rv = nghttp2_submit_settings(session, NGHTTP2_FLAG_NONE, iv, 2); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } rv = nghttp2_submit_goaway(session, NGHTTP2_FLAG_NONE, 100, NGHTTP2_NO_ERROR, NULL, 0); if (rv != 0) { goto fail; } rv = nghttp2_session_send(session); if (rv != 0) { goto fail; } fail: nghttp2_session_del(session); client_new_fail:; }