static void check_push(request_rec *r, const char *tag) { const h2_config *conf = h2_config_rget(r); if (!r->expecting_100 && conf && conf->push_list && conf->push_list->nelts > 0) { int i, old_status; const char *old_line; ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, "%s, early announcing %d resources for push", tag, conf->push_list->nelts); for (i = 0; i < conf->push_list->nelts; ++i) { h2_push_res *push = &APR_ARRAY_IDX(conf->push_list, i, h2_push_res); apr_table_addn(r->headers_out, "Link", apr_psprintf(r->pool, "<%s>; rel=preload%s", push->uri_ref, push->critical? "; critical" : "")); } old_status = r->status; old_line = r->status_line; r->status = 103; r->status_line = "103 Early Hints"; ap_send_interim_response(r, 1); r->status = old_status; r->status_line = old_line; } }
apr_status_t h2_request_rwrite(h2_request *req, request_rec *r) { apr_status_t status; req->config = h2_config_rget(r); req->method = r->method; req->scheme = (r->parsed_uri.scheme? r->parsed_uri.scheme : ap_http_scheme(r)); req->authority = r->hostname; req->path = apr_uri_unparse(r->pool, &r->parsed_uri, APR_URI_UNP_OMITSITEPART); if (!ap_strchr_c(req->authority, ':') && r->server && r->server->port) { apr_port_t defport = apr_uri_port_of_scheme(req->scheme); if (defport != r->server->port) { /* port info missing and port is not default for scheme: append */ req->authority = apr_psprintf(r->pool, "%s:%d", req->authority, (int)r->server->port); } } AP_DEBUG_ASSERT(req->scheme); AP_DEBUG_ASSERT(req->authority); AP_DEBUG_ASSERT(req->path); AP_DEBUG_ASSERT(req->method); status = add_all_h1_header(req, r->pool, r->headers_in); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, "h2_request(%d): rwrite %s host=%s://%s%s", req->id, req->method, req->scheme, req->authority, req->path); return status; }
static int h2_alt_svc_handler(request_rec *r) { h2_ctx *ctx; h2_config *cfg; int i; if (r->connection->keepalives > 0) { /* Only announce Alt-Svc on the first response */ return DECLINED; } ctx = h2_ctx_rget(r); if (h2_ctx_is_active(ctx) || h2_ctx_is_task(ctx)) { return DECLINED; } cfg = h2_config_rget(r); if (r->hostname && cfg && cfg->alt_svcs && cfg->alt_svcs->nelts > 0) { const char *alt_svc_used = apr_table_get(r->headers_in, "Alt-Svc-Used"); if (!alt_svc_used) { /* We have alt-svcs defined and client is not already using * one, announce the services that were configured and match. * The security of this connection determines if we allow * other host names or ports only. */ const char *alt_svc = ""; const char *svc_ma = ""; int secure = h2_h2_is_tls(r->connection); int ma = h2_config_geti(cfg, H2_CONF_ALT_SVC_MAX_AGE); if (ma >= 0) { svc_ma = apr_psprintf(r->pool, "; ma=%d", ma); } ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "h2_alt_svc: announce %s for %s:%d", (secure? "secure" : "insecure"), r->hostname, (int)r->server->port); for (i = 0; i < cfg->alt_svcs->nelts; ++i) { h2_alt_svc *as = h2_alt_svc_IDX(cfg->alt_svcs, i); const char *ahost = as->host; if (ahost && !apr_strnatcasecmp(ahost, r->hostname)) { ahost = NULL; } if (secure || !ahost) { alt_svc = apr_psprintf(r->pool, "%s%s%s=\"%s:%d\"%s", alt_svc, (*alt_svc? ", " : ""), as->alpn, ahost? ahost : "", as->port, svc_ma); } } if (*alt_svc) { apr_table_set(r->headers_out, "Alt-Svc", alt_svc); } } } return DECLINED; }
static int h2_h2_late_fixups(request_rec *r) { /* slave connection? */ if (r->connection->master) { h2_ctx *ctx = h2_ctx_rget(r); struct h2_task *task = h2_ctx_get_task(ctx); if (task) { /* check if we copy vs. setaside files in this location */ task->output.copy_files = h2_config_geti(h2_config_rget(r), H2_CONF_COPY_FILES); if (task->output.copy_files) { ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, "h2_slave_out(%s): copy_files on", task->id); h2_beam_on_file_beam(task->output.beam, h2_beam_no_files, NULL); } check_push(r, "late_fixup"); } } return DECLINED; }
apr_status_t h2_session_start(h2_session *session, int *rv) { apr_status_t status = APR_SUCCESS; h2_config *config; nghttp2_settings_entry settings[3]; AP_DEBUG_ASSERT(session); /* Start the conversation by submitting our SETTINGS frame */ *rv = 0; config = h2_config_get(session->c); if (session->r) { const char *s, *cs; apr_size_t dlen; h2_stream * stream; /* better for vhost matching */ config = h2_config_rget(session->r); /* 'h2c' mode: we should have a 'HTTP2-Settings' header with * base64 encoded client settings. */ s = apr_table_get(session->r->headers_in, "HTTP2-Settings"); if (!s) { ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EINVAL, session->r, APLOGNO(02931) "HTTP2-Settings header missing in request"); return APR_EINVAL; } cs = NULL; dlen = h2_util_base64url_decode(&cs, s, session->pool); if (APLOGrdebug(session->r)) { char buffer[128]; h2_util_hex_dump(buffer, 128, (char*)cs, dlen); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, session->r, "upgrading h2c session with HTTP2-Settings: %s -> %s (%d)", s, buffer, (int)dlen); } *rv = nghttp2_session_upgrade(session->ngh2, (uint8_t*)cs, dlen, NULL); if (*rv != 0) { status = APR_EINVAL; ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r, APLOGNO(02932) "nghttp2_session_upgrade: %s", nghttp2_strerror(*rv)); return status; } /* Now we need to auto-open stream 1 for the request we got. */ *rv = stream_open(session, 1); if (*rv != 0) { status = APR_EGENERAL; ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r, APLOGNO(02933) "open stream 1: %s", nghttp2_strerror(*rv)); return status; } stream = h2_stream_set_get(session->streams, 1); if (stream == NULL) { status = APR_EGENERAL; ap_log_rerror(APLOG_MARK, APLOG_ERR, status, session->r, APLOGNO(02934) "lookup of stream 1"); return status; } status = h2_stream_rwrite(stream, session->r); if (status != APR_SUCCESS) { return status; } status = stream_end_headers(session, stream, 1); if (status != APR_SUCCESS) { return status; } } settings[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; settings[0].value = (uint32_t)session->max_stream_count; settings[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; settings[1].value = h2_config_geti(config, H2_CONF_WIN_SIZE); settings[2].settings_id = NGHTTP2_SETTINGS_MAX_HEADER_LIST_SIZE; settings[2].value = 64*1024; *rv = nghttp2_submit_settings(session->ngh2, NGHTTP2_FLAG_NONE, settings, sizeof(settings)/sizeof(settings[0])); if (*rv != 0) { status = APR_EGENERAL; ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c, APLOGNO(02935) "nghttp2_submit_settings: %s", nghttp2_strerror(*rv)); } return status; }