static int before_frame_send_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, void *userp) { h2_session *session = (h2_session *)userp; (void)ngh2; if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } /* Set the need to flush output when we have added one of the * following frame types */ switch (frame->hd.type) { case NGHTTP2_RST_STREAM: case NGHTTP2_WINDOW_UPDATE: case NGHTTP2_PUSH_PROMISE: case NGHTTP2_PING: case NGHTTP2_GOAWAY: session->flush = 1; break; default: break; } if (APLOGctrace2(session->c)) { char buffer[256]; frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_session(%ld): before_frame_send %s", session->id, buffer); } return 0; }
apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, apr_thread_cond_t *iowait) { apr_status_t status; int acquired; AP_DEBUG_ASSERT(m); if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) { if (m->aborted) { status = APR_ECONNABORTED; } else { m->added_output = iowait; status = apr_thread_cond_timedwait(m->added_output, m->lock, timeout); if (APLOGctrace2(m->c)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c, "h2_mplx(%ld): trywait on data for %f ms)", m->id, timeout/1000.0); } m->added_output = NULL; } leave_mutex(m, acquired); } return status; }
static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io, apr_read_type_e block, h2_conn_io_on_read_cb on_read_cb, void *puser, int *pdone) { apr_status_t status = APR_SUCCESS; apr_size_t readlen = 0; *pdone = 0; while (status == APR_SUCCESS && !*pdone && !APR_BRIGADE_EMPTY(io->input)) { apr_bucket* bucket = APR_BRIGADE_FIRST(io->input); if (APR_BUCKET_IS_METADATA(bucket)) { /* we do nothing regarding any meta here */ } else { const char *bucket_data = NULL; apr_size_t bucket_length = 0; status = apr_bucket_read(bucket, &bucket_data, &bucket_length, block); if (status == APR_SUCCESS && bucket_length > 0) { if (APLOGctrace2(io->connection)) { char buffer[32]; h2_util_hex_dump(buffer, sizeof(buffer)/sizeof(buffer[0]), bucket_data, bucket_length); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection, "h2_conn_io(%ld): read %d bytes: %s", io->connection->id, (int)bucket_length, buffer); } if (bucket_length > 0) { apr_size_t consumed = 0; status = on_read_cb(bucket_data, bucket_length, &consumed, pdone, puser); if (status == APR_SUCCESS && bucket_length > consumed) { /* We have data left in the bucket. Split it. */ status = apr_bucket_split(bucket, consumed); } readlen += consumed; } } } apr_bucket_delete(bucket); } if (readlen == 0 && status == APR_SUCCESS && block == APR_NONBLOCK_READ) { return APR_EAGAIN; } return status; }
static int on_frame_not_send_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, int lib_error_code, void *userp) { h2_session *session = (h2_session *)userp; if (APLOGctrace2(session->c)) { char buffer[256]; frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_session: callback on_frame_not_send error=%d %s", lib_error_code, buffer); } return 0; }
static int on_frame_send_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, void *userp) { h2_session *session = (h2_session *)userp; (void)ngh2; if (APLOGctrace2(session->c)) { char buffer[256]; frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_session(%ld): on_frame_send %s", session->id, buffer); } return 0; }
static int before_frame_send_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, void *userp) { h2_session *session = (h2_session *)userp; if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } if (APLOGctrace2(session->c)) { char buffer[256]; frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, "h2_session(%ld): before_frame_send %s", session->id, buffer); } return 0; }
static int on_invalid_frame_recv_cb(nghttp2_session *ngh2, const nghttp2_frame *frame, uint32_t error_code, void *userp) { h2_session *session = (h2_session *)userp; if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } if (APLOGctrace2(session->c)) { char buffer[256]; frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, "h2_session: callback on_invalid_frame_recv error=%d %s", (int)error_code, buffer); } return 0; }
apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout, apr_thread_cond_t *iowait) { AP_DEBUG_ASSERT(m); if (m->aborted) { return APR_ECONNABORTED; } apr_status_t status = apr_thread_mutex_lock(m->lock); if (APR_SUCCESS == status) { m->added_output = iowait; status = apr_thread_cond_timedwait(m->added_output, m->lock, timeout); if (APLOGctrace2(m->c)) { ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, m->c, "h2_mplx(%ld): trywait on data for %f ms)", m->id, timeout/1000.0); } m->added_output = NULL; apr_thread_mutex_unlock(m->lock); } return status; }
/** * nghttp2 session has received a complete frame. Most, it uses * for processing of internal state. HEADER and DATA frames however * we need to handle ourself. */ static int on_frame_recv_cb(nghttp2_session *ng2s, const nghttp2_frame *frame, void *userp) { int rv; h2_session *session = (h2_session *)userp; apr_status_t status = APR_SUCCESS; if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } ++session->frames_received; ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, "h2_session(%ld): on_frame_rcv #%ld, type=%d", session->id, (long)session->frames_received, frame->hd.type); switch (frame->hd.type) { case NGHTTP2_HEADERS: { int eos; h2_stream * stream = h2_stream_set_get(session->streams, frame->hd.stream_id); if (stream == NULL) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, APLOGNO(02921) "h2_session: stream(%ld-%d): HEADERS frame " "for unknown stream", session->id, (int)frame->hd.stream_id); rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM); status = stream_end_headers(session, stream, eos); break; } case NGHTTP2_DATA: { h2_stream * stream = h2_stream_set_get(session->streams, frame->hd.stream_id); if (stream == NULL) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, APLOGNO(02922) "h2_session: stream(%ld-%d): DATA frame " "for unknown stream", session->id, (int)frame->hd.stream_id); rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } break; } case NGHTTP2_PRIORITY: { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, "h2_session: stream(%ld-%d): PRIORITY frame " " weight=%d, dependsOn=%d, exclusive=%d", session->id, (int)frame->hd.stream_id, frame->priority.pri_spec.weight, frame->priority.pri_spec.stream_id, frame->priority.pri_spec.exclusive); break; } default: if (APLOGctrace2(session->c)) { char buffer[256]; frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, "h2_session: on_frame_rcv %s", buffer); } break; } /* only DATA and HEADERS frame can bear END_STREAM flag. Other frame types may have other flag which has the same value, so we have to check the frame type first. */ if ((frame->hd.type == NGHTTP2_DATA || frame->hd.type == NGHTTP2_HEADERS) && frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { h2_stream * stream = h2_stream_set_get(session->streams, frame->hd.stream_id); if (stream != NULL) { status = h2_stream_write_eos(stream); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, "h2_stream(%ld-%d): input closed", session->id, (int)frame->hd.stream_id); } } if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, APLOGNO(02923) "h2_session: stream(%ld-%d): error handling frame", session->id, (int)frame->hd.stream_id); rv = nghttp2_submit_rst_stream(ng2s, NGHTTP2_FLAG_NONE, frame->hd.stream_id, NGHTTP2_INTERNAL_ERROR); if (nghttp2_is_fatal(rv)) { return NGHTTP2_ERR_CALLBACK_FAILURE; } return 0; } return 0; }
/** * nghttp2 session has received a complete frame. Most, it uses * for processing of internal state. HEADER and DATA frames however * we need to handle ourself. */ static int on_frame_recv_cb(nghttp2_session *ng2s, const nghttp2_frame *frame, void *userp) { h2_session *session = (h2_session *)userp; if (session->aborted) { return NGHTTP2_ERR_CALLBACK_FAILURE; } apr_status_t status = APR_SUCCESS; ++session->frames_received; ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, "h2_session(%ld): on_frame_rcv #%ld, type=%d", session->id, session->frames_received, frame->hd.type); switch (frame->hd.type) { case NGHTTP2_HEADERS: { h2_stream * stream = h2_stream_set_get(session->streams, frame->hd.stream_id); if (stream == NULL) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, "h2_session: stream(%ld-%d): HEADERS frame " "for unknown stream", session->id, (int)frame->hd.stream_id); return NGHTTP2_ERR_INVALID_STREAM_ID; } if (frame->hd.flags & NGHTTP2_FLAG_END_HEADERS) { int eos = (frame->hd.flags & NGHTTP2_FLAG_END_STREAM); status = stream_end_headers(session, stream, eos); } break; } case NGHTTP2_DATA: { h2_stream * stream = h2_stream_set_get(session->streams, frame->hd.stream_id); if (stream == NULL) { ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c, "h2_session: stream(%ld-%d): DATA frame " "for unknown stream", session->id, (int)frame->hd.stream_id); return NGHTTP2_ERR_PROTO; } break; } default: if (APLOGctrace2(session->c)) { char buffer[256]; frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0])); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c, "h2_session: on_frame_rcv %s", buffer); } break; } if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { h2_stream * stream = h2_stream_set_get(session->streams, frame->hd.stream_id); if (stream != NULL) { status = h2_stream_write_eos(stream); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c, "h2_stream(%ld-%d): input closed", session->id, (int)frame->hd.stream_id); } } if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, "h2_session: stream(%ld-%d): error handling frame", session->id, (int)frame->hd.stream_id); return NGHTTP2_ERR_INVALID_STREAM_STATE; } return 0; }
static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io, apr_read_type_e block, h2_conn_io_on_read_cb on_read_cb, void *puser, int *pdone) { apr_status_t status = APR_SUCCESS; apr_size_t readlen = 0; *pdone = 0; while (status == APR_SUCCESS && !*pdone && !APR_BRIGADE_EMPTY(io->input)) { apr_bucket* bucket = APR_BRIGADE_FIRST(io->input); if (APR_BUCKET_IS_METADATA(bucket)) { /* we do nothing regarding any meta here */ } else { const char *bucket_data = NULL; apr_size_t bucket_length = 0; status = apr_bucket_read(bucket, &bucket_data, &bucket_length, block); if (status == APR_SUCCESS && bucket_length > 0) { if (APLOGctrace2(io->connection)) { char buffer[32]; h2_util_hex_dump(buffer, sizeof(buffer)/sizeof(buffer[0]), bucket_data, bucket_length); ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection, "h2_conn_io(%ld): read %ld bytes: %s", io->connection->id, bucket_length, buffer); } if (io->preface_bytes_left > 0) { /* still requiring bytes from the http/2 preface */ size_t pre_offset = HTTP2_PREFACE_LEN - io->preface_bytes_left; apr_size_t check_len = io->preface_bytes_left; if (check_len > bucket_length) { check_len = bucket_length; } if (strncmp(HTTP2_PREFACE+pre_offset, bucket_data, check_len)) { /* preface mismatch */ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EMISMATCH, io->connection, "h2_conn_io(%ld): preface check", io->connection->id); return APR_EMISMATCH; } io->preface_bytes_left -= check_len; bucket_data += check_len; bucket_length -= check_len; ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection, "h2_conn_io(%ld): preface check: %d bytes " "matched, remaining %d", io->connection->id, (int)check_len, io->preface_bytes_left); } if (bucket_length > 0) { apr_size_t consumed = 0; status = on_read_cb(bucket_data, bucket_length, &consumed, pdone, puser); if (status == APR_SUCCESS && bucket_length > consumed) { /* We have data left in the bucket. Split it. */ status = apr_bucket_split(bucket, consumed); } readlen += consumed; } } } apr_bucket_delete(bucket); } if (readlen == 0 && status == APR_SUCCESS && block == APR_NONBLOCK_READ) { return APR_EAGAIN; } return status; }