summaryrefslogtreecommitdiffstats
path: root/modules/http2/h2_push.h
blob: 947b73bc854aa2fc68c0c11fc011e0a0036fe7ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.
 */

#ifndef __mod_h2__h2_push__
#define __mod_h2__h2_push__

#include <http_protocol.h>

#include "h2.h"
#include "h2_headers.h"

struct h2_request;
struct h2_ngheader;
struct h2_session;
struct h2_stream;

typedef struct h2_push {
    const struct h2_request *req;
    h2_priority *priority;
} h2_push;

typedef enum {
    H2_PUSH_DIGEST_APR_HASH,
    H2_PUSH_DIGEST_SHA256
} h2_push_digest_type;

/*******************************************************************************
 * push diary 
 *
 * - The push diary keeps track of resources already PUSHed via HTTP/2 on this
 *   connection. It records a hash value from the absolute URL of the resource
 *   pushed.
 * - Lacking openssl, 
 * - with openssl, it uses SHA256 to calculate the hash value, otherwise it
 *   falls back to apr_hashfunc_default()
 * - whatever the method to generate the hash, the diary keeps a maximum of 64
 *   bits per hash, limiting the memory consumption to about 
 *      H2PushDiarySize * 8 
 *   bytes. Entries are sorted by most recently used and oldest entries are
 *   forgotten first.
 * - While useful by itself to avoid duplicated PUSHes on the same connection,
 *   the original idea was that clients provided a 'Cache-Digest' header with
 *   the values of *their own* cached resources. This was described in
 *   <https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/> 
 *   and some subsequent revisions that tweaked values but kept the overall idea.
 * - The draft was abandoned by the IETF http-wg, as support from major clients,
 *   e.g. browsers, was lacking for various reasons.
 * - For these reasons, mod_h2 abandoned its support for client supplied values
 *   but keeps the diary. It seems to provide value for applications using PUSH,
 *   is configurable in size and defaults to a very moderate amount of memory
 *   used.
 * - The cache digest header is a Golomb Coded Set of hash values, but it may
 *   limit the amount of bits per hash value even further. For a good description
 *   of GCS, read here:
 *   <http://giovanni.bajo.it/post/47119962313/golomb-coded-sets-smaller-than-bloom-filters>
 ******************************************************************************/
 
 
/*
 * The push diary is based on the abandoned draft 
 * <https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/>
 * that describes how to use golomb filters.
 */

typedef struct h2_push_diary h2_push_diary;

typedef void h2_push_digest_calc(h2_push_diary *diary, apr_uint64_t *phash, h2_push *push);

struct h2_push_diary {
    apr_array_header_t  *entries;
    int         NMax; /* Maximum for N, should size change be necessary */
    int         N;    /* Current maximum number of entries, power of 2 */
    apr_uint64_t         mask; /* mask for relevant bits */
    unsigned int         mask_bits; /* number of relevant bits */
    const char          *authority;
    h2_push_digest_type  dtype;
    h2_push_digest_calc *dcalc;
};

/**
 * Determine the list of h2_push'es to send to the client on behalf of
 * the given request/response pair.
 *
 * @param p the pool to use
 * @param req the requst from the client
 * @param res the response from the server
 * @return array of h2_push addresses or NULL
 */
#if AP_HAS_RESPONSE_BUCKETS
apr_array_header_t *h2_push_collect(apr_pool_t *p,
                                    const struct h2_request *req,
                                    apr_uint32_t push_policy,
                                    const ap_bucket_response *res);
#else
apr_array_header_t *h2_push_collect(apr_pool_t *p,
                                    const struct h2_request *req,
                                    apr_uint32_t push_policy,
                                    const struct h2_headers *res);
#endif

/**
 * Create a new push diary for the given maximum number of entries.
 *
 * @param p the pool to use
 * @param N the max number of entries, rounded up to 2^x
 * @return the created diary, might be NULL of max_entries is 0
 */
h2_push_diary *h2_push_diary_create(apr_pool_t *p, int N);

/**
 * Filters the given pushes against the diary and returns only those pushes
 * that were newly entered in the diary.
 */
apr_array_header_t *h2_push_diary_update(struct h2_session *session, apr_array_header_t *pushes);

/**
 * Collect pushes for the given request/response pair, enter them into the
 * diary and return those pushes newly entered.
 */
#if AP_HAS_RESPONSE_BUCKETS
apr_array_header_t *h2_push_collect_update(struct h2_stream *stream,
                                           const struct h2_request *req,
                                           const ap_bucket_response *res);
#else
apr_array_header_t *h2_push_collect_update(struct h2_stream *stream,
                                           const struct h2_request *req,
                                           const struct h2_headers *res);
#endif

/**
 * Get a cache digest as described in 
 * https://datatracker.ietf.org/doc/draft-kazuho-h2-cache-digest/
 * from the contents of the push diary.
 *
 * @param diary the diary to calculdate the digest from
 * @param p the pool to use
 * @param authority the authority to get the data for, use NULL/"*" for all
 * @param pdata on successful return, the binary cache digest
 * @param plen on successful return, the length of the binary data
 */
apr_status_t h2_push_diary_digest_get(h2_push_diary *diary, apr_pool_t *p, 
                                      int maxP, const char *authority, 
                                      const char **pdata, apr_size_t *plen);

#endif /* defined(__mod_h2__h2_push__) */