blob: d16fee29bac32f57e8f40027fdd3588f89fc5459 [file] [log] [blame]
/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <stdio.h>
#include <apr_strings.h>
#include <httpd.h>
#include <http_core.h>
#include <http_log.h>
#include <util_time.h>
#include <nghttp2/nghttp2.h>
#include "h2_private.h"
#include "h2_h2.h"
#include "h2_util.h"
#include "h2_request.h"
#include "h2_response.h"
static apr_table_t *parse_headers(apr_array_header_t *hlines, apr_pool_t *pool)
{
if (hlines) {
apr_table_t *headers = apr_table_make(pool, hlines->nelts);
int i;
for (i = 0; i < hlines->nelts; ++i) {
char *hline = ((char **)hlines->elts)[i];
char *sep = ap_strchr(hline, ':');
if (!sep) {
ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
APLOGNO(02955) "h2_response: invalid header[%d] '%s'",
i, (char*)hline);
/* not valid format, abort */
return NULL;
}
(*sep++) = '\0';
while (*sep == ' ' || *sep == '\t') {
++sep;
}
if (!h2_util_ignore_header(hline)) {
apr_table_merge(headers, hline, sep);
}
}
return headers;
}
else {
return apr_table_make(pool, 0);
}
}
static h2_response *h2_response_create_int(int stream_id,
int rst_error,
int http_status,
apr_table_t *headers,
apr_pool_t *pool)
{
h2_response *response;
const char *s;
if (!headers) {
return NULL;
}
response = apr_pcalloc(pool, sizeof(h2_response));
if (response == NULL) {
return NULL;
}
response->stream_id = stream_id;
response->rst_error = rst_error;
response->http_status = http_status? http_status : 500;
response->content_length = -1;
response->headers = headers;
s = apr_table_get(headers, "Content-Length");
if (s) {
char *end;
response->content_length = apr_strtoi64(s, &end, 10);
if (s == end) {
ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL,
pool, APLOGNO(02956)
"h2_response: content-length"
" value not parsed: %s", s);
response->content_length = -1;
}
}
return response;
}
h2_response *h2_response_create(int stream_id,
int rst_error,
int http_status,
apr_array_header_t *hlines,
apr_pool_t *pool)
{
return h2_response_create_int(stream_id, rst_error, http_status,
parse_headers(hlines, pool), pool);
}
h2_response *h2_response_rcreate(int stream_id, request_rec *r,
apr_table_t *header, apr_pool_t *pool)
{
h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
if (response == NULL) {
return NULL;
}
response->stream_id = stream_id;
response->http_status = r->status;
response->content_length = -1;
response->headers = header;
if (response->http_status == HTTP_FORBIDDEN) {
const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
if (cause) {
/* This request triggered a TLS renegotiation that is now allowed
* in HTTP/2. Tell the client that it should use HTTP/1.1 for this.
*/
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, response->http_status, r,
"h2_response(%ld-%d): renegotiate forbidden, cause: %s",
(long)r->connection->id, stream_id, cause);
response->rst_error = H2_ERR_HTTP_1_1_REQUIRED;
}
}
return response;
}
h2_response *h2_response_die(int stream_id, apr_status_t type,
const struct h2_request *req, apr_pool_t *pool)
{
apr_table_t *headers = apr_table_make(pool, 5);
char *date = NULL;
date = apr_palloc(pool, APR_RFC822_DATE_LEN);
ap_recent_rfc822_date(date, req->request_time);
apr_table_setn(headers, "Date", date);
apr_table_setn(headers, "Server", ap_get_server_banner());
return h2_response_create_int(stream_id, 0, 500, headers, pool);
}
h2_response *h2_response_clone(apr_pool_t *pool, h2_response *from)
{
h2_response *to = apr_pcalloc(pool, sizeof(h2_response));
to->stream_id = from->stream_id;
to->http_status = from->http_status;
to->content_length = from->content_length;
if (from->headers) {
to->headers = apr_table_clone(pool, from->headers);
}
if (from->trailers) {
to->trailers = apr_table_clone(pool, from->trailers);
}
return to;
}
void h2_response_set_trailers(h2_response *response, apr_table_t *trailers)
{
response->trailers = trailers;
}