|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <assert.h>
|
|
Packit |
90a5c9 |
#include <stdio.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <apr_date.h>
|
|
Packit |
90a5c9 |
#include <apr_lib.h>
|
|
Packit |
90a5c9 |
#include <apr_strings.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include <httpd.h>
|
|
Packit |
90a5c9 |
#include <http_core.h>
|
|
Packit |
90a5c9 |
#include <http_log.h>
|
|
Packit |
90a5c9 |
#include <http_connection.h>
|
|
Packit |
90a5c9 |
#include <http_protocol.h>
|
|
Packit |
90a5c9 |
#include <http_request.h>
|
|
Packit |
90a5c9 |
#include <util_time.h>
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "h2_private.h"
|
|
Packit |
90a5c9 |
#include "h2_headers.h"
|
|
Packit |
90a5c9 |
#include "h2_from_h1.h"
|
|
Packit |
90a5c9 |
#include "h2_task.h"
|
|
Packit |
90a5c9 |
#include "h2_util.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* This routine is called by apr_table_do and merges all instances of
|
|
Packit |
90a5c9 |
* the passed field values into a single array that will be further
|
|
Packit |
90a5c9 |
* processed by some later routine. Originally intended to help split
|
|
Packit |
90a5c9 |
* and recombine multiple Vary fields, though it is generic to any field
|
|
Packit |
90a5c9 |
* consisting of comma/space-separated tokens.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static int uniq_field_values(void *d, const char *key, const char *val)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_array_header_t *values;
|
|
Packit |
90a5c9 |
char *start;
|
|
Packit |
90a5c9 |
char *e;
|
|
Packit |
90a5c9 |
char **strpp;
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
(void)key;
|
|
Packit |
90a5c9 |
values = (apr_array_header_t *)d;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
e = apr_pstrdup(values->pool, val);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
do {
|
|
Packit |
90a5c9 |
/* Find a non-empty fieldname */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (*e == ',' || apr_isspace(*e)) {
|
|
Packit |
90a5c9 |
++e;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (*e == '\0') {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
start = e;
|
|
Packit |
90a5c9 |
while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
|
|
Packit |
90a5c9 |
++e;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (*e != '\0') {
|
|
Packit |
90a5c9 |
*e++ = '\0';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Now add it to values if it isn't already represented.
|
|
Packit |
90a5c9 |
* Could be replaced by a ap_array_strcasecmp() if we had one.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
for (i = 0, strpp = (char **) values->elts; i < values->nelts;
|
|
Packit |
90a5c9 |
++i, ++strpp) {
|
|
Packit |
90a5c9 |
if (*strpp && apr_strnatcasecmp(*strpp, start) == 0) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (i == values->nelts) { /* if not found */
|
|
Packit |
90a5c9 |
*(char **)apr_array_push(values) = start;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
} while (*e != '\0');
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Since some clients choke violently on multiple Vary fields, or
|
|
Packit |
90a5c9 |
* Vary fields with duplicate tokens, combine any multiples and remove
|
|
Packit |
90a5c9 |
* any duplicates.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static void fix_vary(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_array_header_t *varies;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
varies = apr_array_make(r->pool, 5, sizeof(char *));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Extract all Vary fields from the headers_out, separate each into
|
|
Packit |
90a5c9 |
* its comma-separated fieldname values, and then add them to varies
|
|
Packit |
90a5c9 |
* if not already present in the array.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
apr_table_do(uniq_field_values, varies, r->headers_out, "Vary", NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we found any, replace old Vary fields with unique-ified value */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (varies->nelts > 0) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Vary",
|
|
Packit |
90a5c9 |
apr_array_pstrcat(r->pool, varies, ','));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void set_basic_http_header(apr_table_t *headers, request_rec *r,
|
|
Packit |
90a5c9 |
apr_pool_t *pool)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *date = NULL;
|
|
Packit |
90a5c9 |
const char *proxy_date = NULL;
|
|
Packit |
90a5c9 |
const char *server = NULL;
|
|
Packit |
90a5c9 |
const char *us = ap_get_server_banner();
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* keep the set-by-proxy server and date headers, otherwise
|
|
Packit |
90a5c9 |
* generate a new server header / date header
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (r && r->proxyreq != PROXYREQ_NONE) {
|
|
Packit |
90a5c9 |
proxy_date = apr_table_get(r->headers_out, "Date");
|
|
Packit |
90a5c9 |
if (!proxy_date) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* proxy_date needs to be const. So use date for the creation of
|
|
Packit |
90a5c9 |
* our own Date header and pass it over to proxy_date later to
|
|
Packit |
90a5c9 |
* avoid a compiler warning.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
date = apr_palloc(pool, APR_RFC822_DATE_LEN);
|
|
Packit |
90a5c9 |
ap_recent_rfc822_date(date, r->request_time);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
server = apr_table_get(r->headers_out, "Server");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
date = apr_palloc(pool, APR_RFC822_DATE_LEN);
|
|
Packit |
90a5c9 |
ap_recent_rfc822_date(date, r? r->request_time : apr_time_now());
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_table_setn(headers, "Date", proxy_date ? proxy_date : date );
|
|
Packit |
90a5c9 |
if (r) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Date");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!server && *us) {
|
|
Packit |
90a5c9 |
server = us;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (server) {
|
|
Packit |
90a5c9 |
apr_table_setn(headers, "Server", server);
|
|
Packit |
90a5c9 |
if (r) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Server");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int copy_header(void *ctx, const char *name, const char *value)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_table_t *headers = ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_table_add(headers, name, value);
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static h2_headers *create_response(h2_task *task, request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *clheader;
|
|
Packit |
90a5c9 |
const char *ctype;
|
|
Packit |
90a5c9 |
apr_table_t *headers;
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Now that we are ready to send a response, we need to combine the two
|
|
Packit |
90a5c9 |
* header field tables into a single table. If we don't do this, our
|
|
Packit |
90a5c9 |
* later attempts to set or unset a given fieldname might be bypassed.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (!apr_is_empty_table(r->err_headers_out)) {
|
|
Packit |
90a5c9 |
r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
|
|
Packit |
90a5c9 |
r->headers_out);
|
|
Packit |
90a5c9 |
apr_table_clear(r->err_headers_out);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Remove the 'Vary' header field if the client can't handle it.
|
|
Packit |
90a5c9 |
* Since this will have nasty effects on HTTP/1.1 caches, force
|
|
Packit |
90a5c9 |
* the response into HTTP/1.0 mode.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Vary");
|
|
Packit |
90a5c9 |
r->proto_num = HTTP_VERSION(1,0);
|
|
Packit |
90a5c9 |
apr_table_setn(r->subprocess_env, "force-response-1.0", "1");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
fix_vary(r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Now remove any ETag response header field if earlier processing
|
|
Packit |
90a5c9 |
* says so (such as a 'FileETag None' directive).
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (apr_table_get(r->notes, "no-etag") != NULL) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "ETag");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* determine the protocol and whether we should use keepalives. */
|
|
Packit |
90a5c9 |
ap_set_keepalive(r);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (AP_STATUS_IS_HEADER_ONLY(r->status)) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Transfer-Encoding");
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Content-Length");
|
|
Packit |
90a5c9 |
r->content_type = r->content_encoding = NULL;
|
|
Packit |
90a5c9 |
r->content_languages = NULL;
|
|
Packit |
90a5c9 |
r->clength = r->chunked = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (r->chunked) {
|
|
Packit |
90a5c9 |
apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Content-Length");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ctype = ap_make_content_type(r, r->content_type);
|
|
Packit |
90a5c9 |
if (ctype) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Content-Type", ctype);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r->content_encoding) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Content-Encoding",
|
|
Packit |
90a5c9 |
r->content_encoding);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!apr_is_empty_array(r->content_languages)) {
|
|
Packit |
90a5c9 |
unsigned int i;
|
|
Packit |
90a5c9 |
char *token;
|
|
Packit |
90a5c9 |
char **languages = (char **)(r->content_languages->elts);
|
|
Packit |
90a5c9 |
const char *field = apr_table_get(r->headers_out, "Content-Language");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (field && (token = ap_get_list_item(r->pool, &field)) != NULL) {
|
|
Packit |
90a5c9 |
for (i = 0; i < r->content_languages->nelts; ++i) {
|
|
Packit |
90a5c9 |
if (!apr_strnatcasecmp(token, languages[i]))
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (i == r->content_languages->nelts) {
|
|
Packit |
90a5c9 |
*((char **) apr_array_push(r->content_languages)) = token;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
field = apr_array_pstrcat(r->pool, r->content_languages, ',');
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Content-Language", field);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Control cachability for non-cachable responses if not already set by
|
|
Packit |
90a5c9 |
* some other part of the server configuration.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
|
|
Packit |
90a5c9 |
char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
|
|
Packit |
90a5c9 |
ap_recent_rfc822_date(date, r->request_time);
|
|
Packit |
90a5c9 |
apr_table_add(r->headers_out, "Expires", date);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* This is a hack, but I can't find anyway around it. The idea is that
|
|
Packit |
90a5c9 |
* we don't want to send out 0 Content-Lengths if it is a head request.
|
|
Packit |
90a5c9 |
* This happens when modules try to outsmart the server, and return
|
|
Packit |
90a5c9 |
* if they see a HEAD request. Apache 1.3 handlers were supposed to
|
|
Packit |
90a5c9 |
* just return in that situation, and the core handled the HEAD. In
|
|
Packit |
90a5c9 |
* 2.0, if a handler returns, then the core sends an EOS bucket down
|
|
Packit |
90a5c9 |
* the filter stack, and the content-length filter computes a C-L of
|
|
Packit |
90a5c9 |
* zero and that gets put in the headers, and we end up sending a
|
|
Packit |
90a5c9 |
* zero C-L to the client. We can't just remove the C-L filter,
|
|
Packit |
90a5c9 |
* because well behaved 2.0 handlers will send their data down the stack,
|
|
Packit |
90a5c9 |
* and we will compute a real C-L for the head request. RBB
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (r->header_only
|
|
Packit |
90a5c9 |
&& (clheader = apr_table_get(r->headers_out, "Content-Length"))
|
|
Packit |
90a5c9 |
&& !strcmp(clheader, "0")) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Content-Length");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
headers = apr_table_make(r->pool, 10);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
set_basic_http_header(headers, r, r->pool);
|
|
Packit |
90a5c9 |
if (r->status == HTTP_NOT_MODIFIED) {
|
|
Packit |
90a5c9 |
apr_table_do(copy_header, headers, r->headers_out,
|
|
Packit |
90a5c9 |
"ETag",
|
|
Packit |
90a5c9 |
"Content-Location",
|
|
Packit |
90a5c9 |
"Expires",
|
|
Packit |
90a5c9 |
"Cache-Control",
|
|
Packit |
90a5c9 |
"Vary",
|
|
Packit |
90a5c9 |
"Warning",
|
|
Packit |
90a5c9 |
"WWW-Authenticate",
|
|
Packit |
90a5c9 |
"Proxy-Authenticate",
|
|
Packit |
90a5c9 |
"Set-Cookie",
|
|
Packit |
90a5c9 |
"Set-Cookie2",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
apr_table_do(copy_header, headers, r->headers_out, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return h2_headers_rcreate(r, r->status, headers, r->pool);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef enum {
|
|
Packit |
90a5c9 |
H2_RP_STATUS_LINE,
|
|
Packit |
90a5c9 |
H2_RP_HEADER_LINE,
|
|
Packit |
90a5c9 |
H2_RP_DONE
|
|
Packit |
90a5c9 |
} h2_rp_state_t;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct h2_response_parser {
|
|
Packit |
90a5c9 |
h2_rp_state_t state;
|
|
Packit |
90a5c9 |
h2_task *task;
|
|
Packit |
90a5c9 |
int http_status;
|
|
Packit |
90a5c9 |
apr_array_header_t *hlines;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *tmp;
|
|
Packit |
90a5c9 |
} h2_response_parser;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t parse_header(h2_response_parser *parser, char *line) {
|
|
Packit |
90a5c9 |
const char *hline;
|
|
Packit |
90a5c9 |
if (line[0] == ' ' || line[0] == '\t') {
|
|
Packit |
90a5c9 |
char **plast;
|
|
Packit |
90a5c9 |
/* continuation line from the header before this */
|
|
Packit |
90a5c9 |
while (line[0] == ' ' || line[0] == '\t') {
|
|
Packit |
90a5c9 |
++line;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
plast = apr_array_pop(parser->hlines);
|
|
Packit |
90a5c9 |
if (plast == NULL) {
|
|
Packit |
90a5c9 |
/* not well formed */
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
hline = apr_psprintf(parser->task->pool, "%s %s", *plast, line);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* new header line */
|
|
Packit |
90a5c9 |
hline = apr_pstrdup(parser->task->pool, line);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(parser->hlines, const char*) = hline;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t get_line(h2_response_parser *parser, apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
char *line, apr_size_t len)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_task *task = parser->task;
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!parser->tmp) {
|
|
Packit |
90a5c9 |
parser->tmp = apr_brigade_create(task->pool, task->c->bucket_alloc);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
status = apr_brigade_split_line(parser->tmp, bb, APR_BLOCK_READ,
|
|
Packit |
90a5c9 |
HUGE_STRING_LEN);
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
--len;
|
|
Packit |
90a5c9 |
status = apr_brigade_flatten(parser->tmp, line, &len;;
|
|
Packit |
90a5c9 |
if (status == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
/* we assume a non-0 containing line and remove trailing crlf. */
|
|
Packit |
90a5c9 |
line[len] = '\0';
|
|
Packit |
90a5c9 |
if (len >= 2 && !strcmp(H2_CRLF, line + len - 2)) {
|
|
Packit |
90a5c9 |
len -= 2;
|
|
Packit |
90a5c9 |
line[len] = '\0';
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(parser->tmp);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): read response line: %s",
|
|
Packit |
90a5c9 |
task->id, line);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* this does not look like a complete line yet */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): read response, incomplete line: %s",
|
|
Packit |
90a5c9 |
task->id, line);
|
|
Packit |
90a5c9 |
return APR_EAGAIN;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(parser->tmp);
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_table_t *make_table(h2_response_parser *parser)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_task *task = parser->task;
|
|
Packit |
90a5c9 |
apr_array_header_t *hlines = parser->hlines;
|
|
Packit |
90a5c9 |
if (hlines) {
|
|
Packit |
90a5c9 |
apr_table_t *headers = apr_table_make(task->pool, hlines->nelts);
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (i = 0; i < hlines->nelts; ++i) {
|
|
Packit |
90a5c9 |
char *hline = ((char **)hlines->elts)[i];
|
|
Packit |
90a5c9 |
char *sep = ap_strchr(hline, ':');
|
|
Packit |
90a5c9 |
if (!sep) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, task->c,
|
|
Packit |
90a5c9 |
APLOGNO(02955) "h2_task(%s): invalid header[%d] '%s'",
|
|
Packit |
90a5c9 |
task->id, i, (char*)hline);
|
|
Packit |
90a5c9 |
/* not valid format, abort */
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
(*sep++) = '\0';
|
|
Packit |
90a5c9 |
while (*sep == ' ' || *sep == '\t') {
|
|
Packit |
90a5c9 |
++sep;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!h2_util_ignore_header(hline)) {
|
|
Packit |
90a5c9 |
apr_table_merge(headers, hline, sep);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return headers;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return apr_table_make(task->pool, 0);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t pass_response(h2_task *task, ap_filter_t *f,
|
|
Packit |
90a5c9 |
h2_response_parser *parser)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_bucket *b;
|
|
Packit |
90a5c9 |
apr_status_t status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_headers *response = h2_headers_create(parser->http_status,
|
|
Packit |
90a5c9 |
make_table(parser),
|
|
Packit |
90a5c9 |
NULL, 0, task->pool);
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(parser->tmp);
|
|
Packit |
90a5c9 |
b = h2_bucket_headers_create(task->c->bucket_alloc, response);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(parser->tmp, b);
|
|
Packit |
90a5c9 |
b = apr_bucket_flush_create(task->c->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(parser->tmp, b);
|
|
Packit |
90a5c9 |
status = ap_pass_brigade(f->next, parser->tmp);
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(parser->tmp);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* reset parser for possible next response */
|
|
Packit |
90a5c9 |
parser->state = H2_RP_STATUS_LINE;
|
|
Packit |
90a5c9 |
apr_array_clear(parser->hlines);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (response->status >= 200) {
|
|
Packit |
90a5c9 |
task->output.sent_response = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c,
|
|
Packit |
90a5c9 |
APLOGNO(03197) "h2_task(%s): passed response %d",
|
|
Packit |
90a5c9 |
task->id, response->status);
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t parse_status(h2_task *task, char *line)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_response_parser *parser = task->output.rparser;
|
|
Packit |
90a5c9 |
int sindex = (apr_date_checkmask(line, "HTTP/#.# ###*")? 9 :
|
|
Packit |
90a5c9 |
(apr_date_checkmask(line, "HTTP/# ###*")? 7 : 0));
|
|
Packit |
90a5c9 |
if (sindex > 0) {
|
|
Packit |
90a5c9 |
int k = sindex + 3;
|
|
Packit |
90a5c9 |
char keepchar = line[k];
|
|
Packit |
90a5c9 |
line[k] = '\0';
|
|
Packit |
90a5c9 |
parser->http_status = atoi(&line[sindex]);
|
|
Packit |
90a5c9 |
line[k] = keepchar;
|
|
Packit |
90a5c9 |
parser->state = H2_RP_HEADER_LINE;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/* Seems like there is garbage on the connection. May be a leftover
|
|
Packit |
90a5c9 |
* from a previous proxy request.
|
|
Packit |
90a5c9 |
* This should only happen if the H2_RESPONSE filter is not yet in
|
|
Packit |
90a5c9 |
* place (post_read_request has not been reached and the handler wants
|
|
Packit |
90a5c9 |
* to write something. Probably just the interim response we are
|
|
Packit |
90a5c9 |
* waiting for. But if there is other data hanging around before
|
|
Packit |
90a5c9 |
* that, this needs to fail. */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, APLOGNO(03467)
|
|
Packit |
90a5c9 |
"h2_task(%s): unable to parse status line: %s",
|
|
Packit |
90a5c9 |
task->id, line);
|
|
Packit |
90a5c9 |
return APR_EINVAL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_from_h1_parse_response(h2_task *task, ap_filter_t *f,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_response_parser *parser = task->output.rparser;
|
|
Packit |
90a5c9 |
char line[HUGE_STRING_LEN];
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!parser) {
|
|
Packit |
90a5c9 |
parser = apr_pcalloc(task->pool, sizeof(*parser));
|
|
Packit |
90a5c9 |
parser->task = task;
|
|
Packit |
90a5c9 |
parser->state = H2_RP_STATUS_LINE;
|
|
Packit |
90a5c9 |
parser->hlines = apr_array_make(task->pool, 10, sizeof(char *));
|
|
Packit |
90a5c9 |
task->output.rparser = parser;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
while (!APR_BRIGADE_EMPTY(bb) && status == APR_SUCCESS) {
|
|
Packit |
90a5c9 |
switch (parser->state) {
|
|
Packit |
90a5c9 |
case H2_RP_STATUS_LINE:
|
|
Packit |
90a5c9 |
case H2_RP_HEADER_LINE:
|
|
Packit |
90a5c9 |
status = get_line(parser, bb, line, sizeof(line));
|
|
Packit |
90a5c9 |
if (status == APR_EAGAIN) {
|
|
Packit |
90a5c9 |
/* need more data */
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (parser->state == H2_RP_STATUS_LINE) {
|
|
Packit |
90a5c9 |
/* instead of parsing, just take it directly */
|
|
Packit |
90a5c9 |
status = parse_status(task, line);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (line[0] == '\0') {
|
|
Packit |
90a5c9 |
/* end of headers, pass response onward */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): end of response", task->id);
|
|
Packit |
90a5c9 |
return pass_response(task, f, parser);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): response header %s", task->id, line);
|
|
Packit |
90a5c9 |
status = parse_header(parser, line);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_filter_headers_out(ap_filter_t *f, apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_task *task = f->ctx;
|
|
Packit |
90a5c9 |
request_rec *r = f->r;
|
|
Packit |
90a5c9 |
apr_bucket *b, *bresp, *body_bucket = NULL, *next;
|
|
Packit |
90a5c9 |
ap_bucket_error *eb = NULL;
|
|
Packit |
90a5c9 |
h2_headers *response = NULL;
|
|
Packit |
90a5c9 |
int headers_passing = 0;
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): output_filter called", task->id);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!task->output.sent_response && !f->c->aborted) {
|
|
Packit |
90a5c9 |
/* check, if we need to send the response now. Until we actually
|
|
Packit |
90a5c9 |
* see a DATA bucket or some EOS/EOR, we do not do so. */
|
|
Packit |
90a5c9 |
for (b = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
b != APR_BRIGADE_SENTINEL(bb);
|
|
Packit |
90a5c9 |
b = APR_BUCKET_NEXT(b))
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (AP_BUCKET_IS_ERROR(b) && !eb) {
|
|
Packit |
90a5c9 |
eb = b->data;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (AP_BUCKET_IS_EOC(b)) {
|
|
Packit |
90a5c9 |
/* If we see an EOC bucket it is a signal that we should get out
|
|
Packit |
90a5c9 |
* of the way doing nothing.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_remove_output_filter(f);
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, f->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): eoc bucket passed", task->id);
|
|
Packit |
90a5c9 |
return ap_pass_brigade(f->next, bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (H2_BUCKET_IS_HEADERS(b)) {
|
|
Packit |
90a5c9 |
headers_passing = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!APR_BUCKET_IS_FLUSH(b)) {
|
|
Packit |
90a5c9 |
body_bucket = b;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (eb) {
|
|
Packit |
90a5c9 |
int st = eb->status;
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03047)
|
|
Packit |
90a5c9 |
"h2_task(%s): err bucket status=%d", task->id, st);
|
|
Packit |
90a5c9 |
/* throw everything away and replace it with the error response
|
|
Packit |
90a5c9 |
* generated by ap_die() */
|
|
Packit |
90a5c9 |
apr_brigade_cleanup(bb);
|
|
Packit |
90a5c9 |
ap_die(st, r);
|
|
Packit |
90a5c9 |
return AP_FILTER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (body_bucket || !headers_passing) {
|
|
Packit |
90a5c9 |
/* time to insert the response bucket before the body or if
|
|
Packit |
90a5c9 |
* no h2_headers is passed, e.g. the response is empty */
|
|
Packit |
90a5c9 |
response = create_response(task, r);
|
|
Packit |
90a5c9 |
if (response == NULL) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_NOTICE, 0, f->c, APLOGNO(03048)
|
|
Packit |
90a5c9 |
"h2_task(%s): unable to create response", task->id);
|
|
Packit |
90a5c9 |
return APR_ENOMEM;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
bresp = h2_bucket_headers_create(f->c->bucket_alloc, response);
|
|
Packit |
90a5c9 |
if (body_bucket) {
|
|
Packit |
90a5c9 |
APR_BUCKET_INSERT_BEFORE(body_bucket, bresp);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_HEAD(bb, bresp);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
task->output.sent_response = 1;
|
|
Packit |
90a5c9 |
r->sent_bodyct = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r->header_only) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): header_only, cleanup output brigade",
|
|
Packit |
90a5c9 |
task->id);
|
|
Packit |
90a5c9 |
b = body_bucket? body_bucket : APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
while (b != APR_BRIGADE_SENTINEL(bb)) {
|
|
Packit |
90a5c9 |
next = APR_BUCKET_NEXT(b);
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_EOS(b) || AP_BUCKET_IS_EOR(b)) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(b);
|
|
Packit |
90a5c9 |
apr_bucket_destroy(b);
|
|
Packit |
90a5c9 |
b = next;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (task->output.sent_response) {
|
|
Packit |
90a5c9 |
/* lets get out of the way, our task is done */
|
|
Packit |
90a5c9 |
ap_remove_output_filter(f);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return ap_pass_brigade(f->next, bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void make_chunk(h2_task *task, apr_bucket_brigade *bb,
|
|
Packit |
90a5c9 |
apr_bucket *first, apr_off_t chunk_len,
|
|
Packit |
90a5c9 |
apr_bucket *tail)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* Surround the buckets [first, tail[ with new buckets carrying the
|
|
Packit |
90a5c9 |
* HTTP/1.1 chunked encoding format. If tail is NULL, the chunk extends
|
|
Packit |
90a5c9 |
* to the end of the brigade. */
|
|
Packit |
90a5c9 |
char buffer[128];
|
|
Packit |
90a5c9 |
apr_bucket *c;
|
|
Packit |
90a5c9 |
int len;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
len = apr_snprintf(buffer, H2_ALEN(buffer),
|
|
Packit |
90a5c9 |
"%"APR_UINT64_T_HEX_FMT"\r\n", (apr_uint64_t)chunk_len);
|
|
Packit |
90a5c9 |
c = apr_bucket_heap_create(buffer, len, NULL, bb->bucket_alloc);
|
|
Packit |
90a5c9 |
APR_BUCKET_INSERT_BEFORE(first, c);
|
|
Packit |
90a5c9 |
c = apr_bucket_heap_create("\r\n", 2, NULL, bb->bucket_alloc);
|
|
Packit |
90a5c9 |
if (tail) {
|
|
Packit |
90a5c9 |
APR_BUCKET_INSERT_BEFORE(tail, c);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
APR_BRIGADE_INSERT_TAIL(bb, c);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
task->input.chunked_total += chunk_len;
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): added chunk %ld, total %ld",
|
|
Packit |
90a5c9 |
task->id, (long)chunk_len, (long)task->input.chunked_total);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int ser_header(void *ctx, const char *name, const char *value)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb = ctx;
|
|
Packit |
90a5c9 |
apr_brigade_printf(bb, NULL, NULL, "%s: %s\r\n", name, value);
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t read_and_chunk(ap_filter_t *f, h2_task *task,
|
|
Packit |
90a5c9 |
apr_read_type_e block) {
|
|
Packit |
90a5c9 |
request_rec *r = f->r;
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb = task->input.bbchunk;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!bb) {
|
|
Packit |
90a5c9 |
bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
|
|
Packit |
90a5c9 |
task->input.bbchunk = bb;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (APR_BRIGADE_EMPTY(bb)) {
|
|
Packit |
90a5c9 |
apr_bucket *b, *next, *first_data = NULL;
|
|
Packit |
90a5c9 |
apr_bucket_brigade *tmp;
|
|
Packit |
90a5c9 |
apr_off_t bblen = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* get more data from the lower layer filters. Always do this
|
|
Packit |
90a5c9 |
* in larger pieces, since we handle the read modes ourself. */
|
|
Packit |
90a5c9 |
status = ap_get_brigade(f->next, bb,
|
|
Packit |
90a5c9 |
AP_MODE_READBYTES, block, 32*1024);
|
|
Packit |
90a5c9 |
if (status == APR_EOF) {
|
|
Packit |
90a5c9 |
if (!task->input.eos) {
|
|
Packit |
90a5c9 |
status = apr_brigade_puts(bb, NULL, NULL, "0\r\n\r\n");
|
|
Packit |
90a5c9 |
task->input.eos = 1;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_remove_input_filter(f);
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (status != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (b = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
b != APR_BRIGADE_SENTINEL(bb) && !task->input.eos;
|
|
Packit |
90a5c9 |
b = next) {
|
|
Packit |
90a5c9 |
next = APR_BUCKET_NEXT(b);
|
|
Packit |
90a5c9 |
if (APR_BUCKET_IS_METADATA(b)) {
|
|
Packit |
90a5c9 |
if (first_data) {
|
|
Packit |
90a5c9 |
make_chunk(task, bb, first_data, bblen, b);
|
|
Packit |
90a5c9 |
first_data = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (H2_BUCKET_IS_HEADERS(b)) {
|
|
Packit |
90a5c9 |
h2_headers *headers = h2_bucket_headers_get(b);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_assert(headers);
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
|
Packit |
90a5c9 |
"h2_task(%s): receiving trailers", task->id);
|
|
Packit |
90a5c9 |
tmp = apr_brigade_split_ex(bb, b, NULL);
|
|
Packit |
90a5c9 |
if (!apr_is_empty_table(headers->headers)) {
|
|
Packit |
90a5c9 |
status = apr_brigade_puts(bb, NULL, NULL, "0\r\n");
|
|
Packit |
90a5c9 |
apr_table_do(ser_header, bb, headers->headers, NULL);
|
|
Packit |
90a5c9 |
status = apr_brigade_puts(bb, NULL, NULL, "\r\n");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
status = apr_brigade_puts(bb, NULL, NULL, "0\r\n\r\n");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
r->trailers_in = apr_table_clone(r->pool, headers->headers);
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(b);
|
|
Packit |
90a5c9 |
apr_bucket_destroy(b);
|
|
Packit |
90a5c9 |
APR_BRIGADE_CONCAT(bb, tmp);
|
|
Packit |
90a5c9 |
apr_brigade_destroy(tmp);
|
|
Packit |
90a5c9 |
task->input.eos = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (APR_BUCKET_IS_EOS(b)) {
|
|
Packit |
90a5c9 |
tmp = apr_brigade_split_ex(bb, b, NULL);
|
|
Packit |
90a5c9 |
status = apr_brigade_puts(bb, NULL, NULL, "0\r\n\r\n");
|
|
Packit |
90a5c9 |
APR_BRIGADE_CONCAT(bb, tmp);
|
|
Packit |
90a5c9 |
apr_brigade_destroy(tmp);
|
|
Packit |
90a5c9 |
task->input.eos = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (b->length == 0) {
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(b);
|
|
Packit |
90a5c9 |
apr_bucket_destroy(b);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
if (!first_data) {
|
|
Packit |
90a5c9 |
first_data = b;
|
|
Packit |
90a5c9 |
bblen = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
bblen += b->length;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (first_data) {
|
|
Packit |
90a5c9 |
make_chunk(task, bb, first_data, bblen, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_filter_request_in(ap_filter_t* f,
|
|
Packit |
90a5c9 |
apr_bucket_brigade* bb,
|
|
Packit |
90a5c9 |
ap_input_mode_t mode,
|
|
Packit |
90a5c9 |
apr_read_type_e block,
|
|
Packit |
90a5c9 |
apr_off_t readbytes)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_task *task = f->ctx;
|
|
Packit |
90a5c9 |
request_rec *r = f->r;
|
|
Packit |
90a5c9 |
apr_status_t status = APR_SUCCESS;
|
|
Packit |
90a5c9 |
apr_bucket *b, *next;
|
|
Packit |
90a5c9 |
core_server_config *conf =
|
|
Packit |
90a5c9 |
(core_server_config *) ap_get_module_config(r->server->module_config,
|
|
Packit |
90a5c9 |
&core_module);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, f->r,
|
|
Packit |
90a5c9 |
"h2_task(%s): request filter, exp=%d", task->id, r->expecting_100);
|
|
Packit |
90a5c9 |
if (!task->request->chunked) {
|
|
Packit |
90a5c9 |
status = ap_get_brigade(f->next, bb, mode, block, readbytes);
|
|
Packit |
90a5c9 |
/* pipe data through, just take care of trailers */
|
|
Packit |
90a5c9 |
for (b = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
b != APR_BRIGADE_SENTINEL(bb); b = next) {
|
|
Packit |
90a5c9 |
next = APR_BUCKET_NEXT(b);
|
|
Packit |
90a5c9 |
if (H2_BUCKET_IS_HEADERS(b)) {
|
|
Packit |
90a5c9 |
h2_headers *headers = h2_bucket_headers_get(b);
|
|
Packit |
90a5c9 |
ap_assert(headers);
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
|
Packit |
90a5c9 |
"h2_task(%s): receiving trailers", task->id);
|
|
Packit |
90a5c9 |
r->trailers_in = headers->headers;
|
|
Packit |
90a5c9 |
if (conf && conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE) {
|
|
Packit |
90a5c9 |
r->headers_in = apr_table_overlay(r->pool, r->headers_in,
|
|
Packit |
90a5c9 |
r->trailers_in);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
APR_BUCKET_REMOVE(b);
|
|
Packit |
90a5c9 |
apr_bucket_destroy(b);
|
|
Packit |
90a5c9 |
ap_remove_input_filter(f);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (headers->raw_bytes && h2_task_logio_add_bytes_in) {
|
|
Packit |
90a5c9 |
h2_task_logio_add_bytes_in(task->c, headers->raw_bytes);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Things are more complicated. The standard HTTP input filter, which
|
|
Packit |
90a5c9 |
* does a lot what we do not want to duplicate, also cares about chunked
|
|
Packit |
90a5c9 |
* transfer encoding and trailers.
|
|
Packit |
90a5c9 |
* We need to simulate chunked encoding for it to be happy.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((status = read_and_chunk(f, task, block)) != APR_SUCCESS) {
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (mode == AP_MODE_EXHAUSTIVE) {
|
|
Packit |
90a5c9 |
/* return all we have */
|
|
Packit |
90a5c9 |
APR_BRIGADE_CONCAT(bb, task->input.bbchunk);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (mode == AP_MODE_READBYTES) {
|
|
Packit |
90a5c9 |
status = h2_brigade_concat_length(bb, task->input.bbchunk, readbytes);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (mode == AP_MODE_SPECULATIVE) {
|
|
Packit |
90a5c9 |
status = h2_brigade_copy_length(bb, task->input.bbchunk, readbytes);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (mode == AP_MODE_GETLINE) {
|
|
Packit |
90a5c9 |
/* we are reading a single LF line, e.g. the HTTP headers.
|
|
Packit |
90a5c9 |
* this has the nasty side effect to split the bucket, even
|
|
Packit |
90a5c9 |
* though it ends with CRLF and creates a 0 length bucket */
|
|
Packit |
90a5c9 |
status = apr_brigade_split_line(bb, task->input.bbchunk, block,
|
|
Packit |
90a5c9 |
HUGE_STRING_LEN);
|
|
Packit |
90a5c9 |
if (APLOGctrace1(f->c)) {
|
|
Packit |
90a5c9 |
char buffer[1024];
|
|
Packit |
90a5c9 |
apr_size_t len = sizeof(buffer)-1;
|
|
Packit |
90a5c9 |
apr_brigade_flatten(bb, buffer, &len;;
|
|
Packit |
90a5c9 |
buffer[len] = 0;
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
|
|
Packit |
90a5c9 |
"h2_task(%s): getline: %s",
|
|
Packit |
90a5c9 |
task->id, buffer);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* Hmm, well. There is mode AP_MODE_EATCRLF, but we chose not
|
|
Packit |
90a5c9 |
* to support it. Seems to work. */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOTIMPL, f->c,
|
|
Packit |
90a5c9 |
APLOGNO(02942)
|
|
Packit |
90a5c9 |
"h2_task, unsupported READ mode %d", mode);
|
|
Packit |
90a5c9 |
status = APR_ENOTIMPL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
h2_util_bb_log(f->c, task->stream_id, APLOG_TRACE2, "forwarding input", bb);
|
|
Packit |
90a5c9 |
return status;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_status_t h2_filter_trailers_out(ap_filter_t *f, apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
h2_task *task = f->ctx;
|
|
Packit |
90a5c9 |
request_rec *r = f->r;
|
|
Packit |
90a5c9 |
apr_bucket *b, *e;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (task && r) {
|
|
Packit |
90a5c9 |
/* Detect the EOS/EOR bucket and forward any trailers that may have
|
|
Packit |
90a5c9 |
* been set to our h2_headers.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
for (b = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
b != APR_BRIGADE_SENTINEL(bb);
|
|
Packit |
90a5c9 |
b = APR_BUCKET_NEXT(b))
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if ((APR_BUCKET_IS_EOS(b) || AP_BUCKET_IS_EOR(b))
|
|
Packit |
90a5c9 |
&& r->trailers_out && !apr_is_empty_table(r->trailers_out)) {
|
|
Packit |
90a5c9 |
h2_headers *headers;
|
|
Packit |
90a5c9 |
apr_table_t *trailers;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03049)
|
|
Packit |
90a5c9 |
"h2_task(%s): sending trailers", task->id);
|
|
Packit |
90a5c9 |
trailers = apr_table_clone(r->pool, r->trailers_out);
|
|
Packit |
90a5c9 |
headers = h2_headers_rcreate(r, HTTP_OK, trailers, r->pool);
|
|
Packit |
90a5c9 |
e = h2_bucket_headers_create(bb->bucket_alloc, headers);
|
|
Packit |
90a5c9 |
APR_BUCKET_INSERT_BEFORE(b, e);
|
|
Packit |
90a5c9 |
apr_table_clear(r->trailers_out);
|
|
Packit |
90a5c9 |
ap_remove_output_filter(f);
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return ap_pass_brigade(f->next, bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|