]>
Commit | Line | Data |
---|---|---|
7faf2bdb | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7faf2bdb | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 66 HTTP Header Tools */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
c0941a6a | 12 | #include "acl/FilledChecklist.h" |
3b07476b | 13 | #include "acl/Gadgets.h" |
81ab22b6 | 14 | #include "base/EnumIterator.h" |
582c2af2 | 15 | #include "client_side.h" |
602d9612 | 16 | #include "client_side_request.h" |
f4698e0b | 17 | #include "comm/Connection.h" |
27bc2077 | 18 | #include "compat/strtoll.h" |
d7f4a0b7 | 19 | #include "ConfigParser.h" |
f4698e0b | 20 | #include "fde.h" |
602d9612 | 21 | #include "globals.h" |
4277583a | 22 | #include "http/RegisteredHeaders.h" |
d3dddfb5 | 23 | #include "http/Stream.h" |
27bc2077 AJ |
24 | #include "HttpHdrContRange.h" |
25 | #include "HttpHeader.h" | |
79cb238d | 26 | #include "HttpHeaderFieldInfo.h" |
3b07476b CT |
27 | #include "HttpHeaderTools.h" |
28 | #include "HttpRequest.h" | |
0eb49b6d | 29 | #include "MemBuf.h" |
4d5904f7 | 30 | #include "SquidConfig.h" |
582c2af2 | 31 | #include "Store.h" |
28204b3b | 32 | #include "StrList.h" |
582c2af2 | 33 | |
cb4f4424 | 34 | #if USE_OPENSSL |
f4698e0b CT |
35 | #include "ssl/support.h" |
36 | #endif | |
582c2af2 | 37 | |
f4698e0b | 38 | #include <algorithm> |
1a30fdf5 | 39 | #include <cerrno> |
f4698e0b | 40 | #include <string> |
7faf2bdb | 41 | |
789217a2 | 42 | static void httpHeaderPutStrvf(HttpHeader * hdr, Http::HdrType id, const char *fmt, va_list vargs); |
cde8f31b | 43 | static void httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd); |
de336bbe | 44 | |
d8b249ef | 45 | void |
97474590 | 46 | httpHeaderMaskInit(HttpHeaderMask * mask, int value) |
d8b249ef | 47 | { |
97474590 | 48 | memset(mask, value, sizeof(*mask)); |
d8b249ef | 49 | } |
50 | ||
2246b732 | 51 | /* same as httpHeaderPutStr, but formats the string using snprintf first */ |
2246b732 | 52 | void |
789217a2 | 53 | httpHeaderPutStrf(HttpHeader * hdr, Http::HdrType id, const char *fmt,...) |
2246b732 | 54 | { |
9bc73deb | 55 | va_list args; |
56 | va_start(args, fmt); | |
62e76326 | 57 | |
2246b732 | 58 | httpHeaderPutStrvf(hdr, id, fmt, args); |
59 | va_end(args); | |
60 | } | |
61 | ||
62 | /* used by httpHeaderPutStrf */ | |
63 | static void | |
789217a2 | 64 | httpHeaderPutStrvf(HttpHeader * hdr, Http::HdrType id, const char *fmt, va_list vargs) |
2246b732 | 65 | { |
2246b732 | 66 | MemBuf mb; |
2fe7eff9 | 67 | mb.init(); |
8ed3716a | 68 | mb.vappendf(fmt, vargs); |
a9925b40 | 69 | hdr->putStr(id, mb.buf); |
2fe7eff9 | 70 | mb.clean(); |
2246b732 | 71 | } |
72 | ||
9035d1d5 | 73 | /** wrapper arrounf PutContRange */ |
d192d11f | 74 | void |
47f6e231 | 75 | httpHeaderAddContRange(HttpHeader * hdr, HttpHdrRangeSpec spec, int64_t ent_len) |
d192d11f | 76 | { |
77 | HttpHdrContRange *cr = httpHdrContRangeCreate(); | |
78 | assert(hdr && ent_len >= 0); | |
79 | httpHdrContRangeSet(cr, spec, ent_len); | |
a9925b40 | 80 | hdr->putContRange(cr); |
3c670b50 | 81 | delete cr; |
d192d11f | 82 | } |
83 | ||
9035d1d5 | 84 | /** |
1c4feb50 AJ |
85 | * \return true if a given directive is found in the Connection header field-value. |
86 | * | |
87 | * \note if no Connection header exists we may check the Proxy-Connection header | |
99edd1c3 | 88 | */ |
1c4feb50 | 89 | bool |
d5f18517 | 90 | httpHeaderHasConnDir(const HttpHeader * hdr, const SBuf &directive) |
99edd1c3 | 91 | { |
30abd221 | 92 | String list; |
1c4feb50 | 93 | |
9bc73deb | 94 | /* what type of header do we have? */ |
1c4feb50 AJ |
95 | if (hdr->getList(Http::HdrType::CONNECTION, &list)) |
96 | return strListIsMember(&list, directive, ',') != 0; | |
62e76326 | 97 | |
95e78500 | 98 | #if USE_HTTP_VIOLATIONS |
1c4feb50 AJ |
99 | if (hdr->getList(Http::HdrType::PROXY_CONNECTION, &list)) |
100 | return strListIsMember(&list, directive, ',') != 0; | |
95e78500 | 101 | #endif |
30abd221 | 102 | |
1c4feb50 AJ |
103 | // else, no connection header for it to exist in |
104 | return false; | |
99edd1c3 | 105 | } |
106 | ||
b50e327b | 107 | /** handy to printf prefixes of potentially very long buffers */ |
7faf2bdb | 108 | const char * |
81858ebc | 109 | getStringPrefix(const char *str, size_t sz) |
7faf2bdb | 110 | { |
d8b249ef | 111 | #define SHORT_PREFIX_SIZE 512 |
7faf2bdb | 112 | LOCAL_ARRAY(char, buf, SHORT_PREFIX_SIZE); |
81858ebc | 113 | xstrncpy(buf, str, (sz+1 > SHORT_PREFIX_SIZE) ? SHORT_PREFIX_SIZE : sz); |
7faf2bdb | 114 | return buf; |
115 | } | |
b5107edb | 116 | |
b50e327b | 117 | /** |
2f8abb64 | 118 | * parses an int field, complains if something went wrong, returns true on |
b5107edb | 119 | * success |
120 | */ | |
121 | int | |
122 | httpHeaderParseInt(const char *start, int *value) | |
123 | { | |
124 | assert(value); | |
125 | *value = atoi(start); | |
62e76326 | 126 | |
b6a2f15e | 127 | if (!*value && !xisdigit(*start)) { |
bf8fe701 | 128 | debugs(66, 2, "failed to parse an int header field near '" << start << "'"); |
62e76326 | 129 | return 0; |
b5107edb | 130 | } |
62e76326 | 131 | |
b5107edb | 132 | return 1; |
133 | } | |
134 | ||
a1b9ec20 AR |
135 | bool |
136 | httpHeaderParseOffset(const char *start, int64_t *value, char **endPtr) | |
47f6e231 | 137 | { |
a1b9ec20 | 138 | char *end = nullptr; |
66e255bc | 139 | errno = 0; |
a1b9ec20 AR |
140 | const int64_t res = strtoll(start, &end, 10); |
141 | if (errno && !res) { | |
142 | debugs(66, 7, "failed to parse malformed offset in " << start); | |
143 | return false; | |
144 | } | |
145 | if (errno == ERANGE && (res == LLONG_MIN || res == LLONG_MAX)) { // no overflow | |
146 | debugs(66, 7, "failed to parse huge offset in " << start); | |
147 | return false; | |
148 | } | |
149 | if (start == end) { | |
150 | debugs(66, 7, "failed to parse empty offset"); | |
151 | return false; | |
81ab22b6 | 152 | } |
47f6e231 | 153 | *value = res; |
a1b9ec20 AR |
154 | if (endPtr) |
155 | *endPtr = end; | |
81ab22b6 | 156 | debugs(66, 7, "offset " << start << " parsed as " << res); |
a1b9ec20 | 157 | return true; |
47f6e231 | 158 | } |
159 | ||
b50e327b AJ |
160 | /** |
161 | * Parses a quoted-string field (RFC 2616 section 2.2), complains if | |
43ae1d95 | 162 | * something went wrong, returns non-zero on success. |
34460e19 | 163 | * Un-escapes quoted-pair characters found within the string. |
229af339 | 164 | * start should point at the first double-quote. |
43ae1d95 | 165 | */ |
166 | int | |
34460e19 | 167 | httpHeaderParseQuotedString(const char *start, const int len, String *val) |
43ae1d95 | 168 | { |
169 | const char *end, *pos; | |
30abd221 | 170 | val->clean(); |
9abd1514 | 171 | if (*start != '"') { |
bf95c10a | 172 | debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'"); |
6d97f5f1 | 173 | return 0; |
9abd1514 | 174 | } |
43ae1d95 | 175 | pos = start + 1; |
176 | ||
34460e19 | 177 | while (*pos != '"' && len > (pos-start)) { |
5f5ddef6 CT |
178 | |
179 | if (*pos =='\r') { | |
95dc7ff4 | 180 | ++pos; |
5f5ddef6 | 181 | if ((pos-start) > len || *pos != '\n') { |
bf95c10a | 182 | debugs(66, 2, "failed to parse a quoted-string header field with '\\r' octet " << (start-pos) |
5f5ddef6 CT |
183 | << " bytes into '" << start << "'"); |
184 | val->clean(); | |
185 | return 0; | |
186 | } | |
187 | } | |
188 | ||
189 | if (*pos == '\n') { | |
95dc7ff4 | 190 | ++pos; |
5f5ddef6 | 191 | if ( (pos-start) > len || (*pos != ' ' && *pos != '\t')) { |
bf95c10a | 192 | debugs(66, 2, "failed to parse multiline quoted-string header field '" << start << "'"); |
5f5ddef6 CT |
193 | val->clean(); |
194 | return 0; | |
195 | } | |
196 | // TODO: replace the entire LWS with a space | |
197 | val->append(" "); | |
95dc7ff4 | 198 | ++pos; |
bf95c10a | 199 | debugs(66, 2, "len < pos-start => " << len << " < " << (pos-start)); |
5f5ddef6 CT |
200 | continue; |
201 | } | |
202 | ||
a0133f10 | 203 | bool quoted = (*pos == '\\'); |
5f5ddef6 | 204 | if (quoted) { |
95dc7ff4 | 205 | ++pos; |
5f5ddef6 | 206 | if (!*pos || (pos-start) > len) { |
bf95c10a | 207 | debugs(66, 2, "failed to parse a quoted-string header field near '" << start << "'"); |
5f5ddef6 CT |
208 | val->clean(); |
209 | return 0; | |
210 | } | |
6d97f5f1 | 211 | } |
34460e19 | 212 | end = pos; |
c4b86597 | 213 | while (end < (start+len) && *end != '\\' && *end != '\"' && (unsigned char)*end > 0x1F && *end != 0x7F) |
95dc7ff4 | 214 | ++end; |
c4b86597 | 215 | if (((unsigned char)*end <= 0x1F && *end != '\r' && *end != '\n') || *end == 0x7F) { |
bf95c10a | 216 | debugs(66, 2, "failed to parse a quoted-string header field with CTL octet " << (start-pos) |
34460e19 AJ |
217 | << " bytes into '" << start << "'"); |
218 | val->clean(); | |
219 | return 0; | |
220 | } | |
6d97f5f1 A |
221 | val->append(pos, end-pos); |
222 | pos = end; | |
43ae1d95 | 223 | } |
5f5ddef6 CT |
224 | |
225 | if (*pos != '\"') { | |
bf95c10a | 226 | debugs(66, 2, "failed to parse a quoted-string header field which did not end with \" "); |
5f5ddef6 CT |
227 | val->clean(); |
228 | return 0; | |
229 | } | |
9abd1514 | 230 | /* Make sure it's defined even if empty "" */ |
b38b26cb | 231 | if (!val->termedBuf()) |
2fe0439c | 232 | val->assign("", 0); |
9abd1514 | 233 | return 1; |
43ae1d95 | 234 | } |
235 | ||
e7ce227f | 236 | SBuf |
22381a6b AR |
237 | httpHeaderQuoteString(const char *raw) |
238 | { | |
239 | assert(raw); | |
240 | ||
3cc0f4e7 AR |
241 | // TODO: Optimize by appending a sequence of characters instead of a char. |
242 | // This optimization may be easier with Tokenizer after raw becomes SBuf. | |
243 | ||
e7ce227f AR |
244 | // RFC 7230 says a "sender SHOULD NOT generate a quoted-pair in a |
245 | // quoted-string except where necessary" (i.e., DQUOTE and backslash) | |
22381a6b AR |
246 | bool needInnerQuote = false; |
247 | for (const char *s = raw; !needInnerQuote && *s; ++s) | |
248 | needInnerQuote = *s == '"' || *s == '\\'; | |
249 | ||
e7ce227f | 250 | SBuf quotedStr; |
22381a6b AR |
251 | quotedStr.append('"'); |
252 | ||
253 | if (needInnerQuote) { | |
254 | for (const char *s = raw; *s; ++s) { | |
255 | if (*s == '"' || *s == '\\') | |
256 | quotedStr.append('\\'); | |
257 | quotedStr.append(*s); | |
258 | } | |
259 | } else { | |
260 | quotedStr.append(raw); | |
261 | } | |
e7ce227f | 262 | |
22381a6b AR |
263 | quotedStr.append('"'); |
264 | return quotedStr; | |
265 | } | |
266 | ||
b50e327b AJ |
267 | /** |
268 | * Checks the anonymizer (header_access) configuration. | |
4f4611bf | 269 | * |
b50e327b AJ |
270 | * \retval 0 Header is explicitly blocked for removal |
271 | * \retval 1 Header is explicitly allowed | |
272 | * \retval 1 Header has been replaced, the current version can be used. | |
273 | * \retval 1 Header has no access controls to test | |
6bccf575 | 274 | */ |
2d72d4fd | 275 | static int |
82a9ea6c | 276 | httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, HeaderManglers *hms, const AccessLogEntryPointer &al) |
6bccf575 | 277 | { |
e9b1e111 | 278 | int retval; |
279 | ||
6bccf575 | 280 | assert(e); |
8c01ada0 | 281 | |
001d55dc | 282 | const headerMangler *hm = hms->find(*e); |
3b07476b | 283 | |
b50e327b | 284 | /* mangler or checklist went away. default allow */ |
af6a12ee | 285 | if (!hm || !hm->access_list) { |
81ab22b6 | 286 | debugs(66, 7, "couldn't find mangler or access list. Allowing"); |
b50e327b AJ |
287 | return 1; |
288 | } | |
289 | ||
aee3523a | 290 | ACLFilledChecklist checklist(hm->access_list, request, nullptr); |
62e76326 | 291 | |
82a9ea6c EB |
292 | checklist.al = al; |
293 | if (al && al->reply) { | |
294 | checklist.reply = al->reply.getRaw(); | |
295 | HTTPMSGLOCK(checklist.reply); | |
296 | } | |
297 | ||
1c2b4465 CT |
298 | // XXX: The two "It was denied" clauses below mishandle cases with no |
299 | // matching rules, violating the "If no rules within the set have matching | |
300 | // ACLs, the header field is left as is" promise in squid.conf. | |
301 | // TODO: Use Acl::Answer::implicit. See HttpStateData::forwardUpgrade(). | |
06bf5384 | 302 | if (checklist.fastCheck().allowed()) { |
b50e327b | 303 | /* aclCheckFast returns true for allow. */ |
81ab22b6 | 304 | debugs(66, 7, "checklist for mangler is positive. Mangle"); |
62e76326 | 305 | retval = 1; |
aee3523a | 306 | } else if (nullptr == hm->replacement) { |
62e76326 | 307 | /* It was denied, and we don't have any replacement */ |
81ab22b6 | 308 | debugs(66, 7, "checklist denied, we have no replacement. Pass"); |
1c2b4465 | 309 | // XXX: We said "Pass", but the caller will delete on zero retval. |
62e76326 | 310 | retval = 0; |
a453662f | 311 | } else { |
62e76326 | 312 | /* It was denied, but we have a replacement. Replace the |
313 | * header on the fly, and return that the new header | |
314 | * is allowed. | |
315 | */ | |
81ab22b6 | 316 | debugs(66, 7, "checklist denied but we have replacement. Replace"); |
62e76326 | 317 | e->value = hm->replacement; |
318 | retval = 1; | |
a453662f | 319 | } |
e9b1e111 | 320 | |
e9b1e111 | 321 | return retval; |
6bccf575 | 322 | } |
323 | ||
b50e327b | 324 | /** Mangles headers for a list of headers. */ |
6bccf575 | 325 | void |
cde8f31b | 326 | httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep) |
6bccf575 | 327 | { |
328 | HttpHeaderEntry *e; | |
329 | HttpHeaderPos p = HttpHeaderInitPos; | |
62e76326 | 330 | |
cde8f31b NH |
331 | /* check with anonymizer tables */ |
332 | HeaderManglers *hms = nullptr; | |
333 | HeaderWithAclList *headersAdd = nullptr; | |
334 | ||
335 | switch (req_or_rep) { | |
b7290021 SM |
336 | case ROR_REQUEST: |
337 | hms = Config.request_header_access; | |
338 | headersAdd = Config.request_header_add; | |
339 | break; | |
340 | case ROR_REPLY: | |
341 | hms = Config.reply_header_access; | |
342 | headersAdd = Config.reply_header_add; | |
343 | break; | |
cde8f31b NH |
344 | } |
345 | ||
346 | if (hms) { | |
347 | int headers_deleted = 0; | |
348 | while ((e = l->getEntry(&p))) { | |
82a9ea6c | 349 | if (httpHdrMangle(e, request, hms, al) == 0) |
cde8f31b NH |
350 | l->delAt(p, headers_deleted); |
351 | } | |
ba9fb01d | 352 | |
cde8f31b NH |
353 | if (headers_deleted) |
354 | l->refreshMask(); | |
355 | } | |
356 | ||
357 | if (headersAdd && !headersAdd->empty()) { | |
358 | httpHdrAdd(l, request, al, *headersAdd); | |
359 | } | |
6bccf575 | 360 | } |
5967c0bf | 361 | |
3b07476b | 362 | static |
001d55dc | 363 | void header_mangler_clean(headerMangler &m) |
3b07476b CT |
364 | { |
365 | aclDestroyAccessList(&m.access_list); | |
366 | safe_free(m.replacement); | |
367 | } | |
368 | ||
369 | static | |
370 | void header_mangler_dump_access(StoreEntry * entry, const char *option, | |
001d55dc | 371 | const headerMangler &m, const char *name) |
3b07476b | 372 | { |
aee3523a | 373 | if (m.access_list != nullptr) { |
3b07476b CT |
374 | storeAppendPrintf(entry, "%s ", option); |
375 | dump_acl_access(entry, name, m.access_list); | |
376 | } | |
377 | } | |
378 | ||
379 | static | |
380 | void header_mangler_dump_replacement(StoreEntry * entry, const char *option, | |
001d55dc | 381 | const headerMangler &m, const char *name) |
3b07476b CT |
382 | { |
383 | if (m.replacement) | |
384 | storeAppendPrintf(entry, "%s %s %s\n", option, name, m.replacement); | |
385 | } | |
386 | ||
387 | HeaderManglers::HeaderManglers() | |
388 | { | |
389 | memset(known, 0, sizeof(known)); | |
390 | memset(&all, 0, sizeof(all)); | |
391 | } | |
392 | ||
393 | HeaderManglers::~HeaderManglers() | |
394 | { | |
81ab22b6 | 395 | for (auto i : WholeEnum<Http::HdrType>()) |
3b07476b CT |
396 | header_mangler_clean(known[i]); |
397 | ||
81ab22b6 FC |
398 | for (auto i : custom) |
399 | header_mangler_clean(i.second); | |
3b07476b CT |
400 | |
401 | header_mangler_clean(all); | |
402 | } | |
403 | ||
404 | void | |
405 | HeaderManglers::dumpAccess(StoreEntry * entry, const char *name) const | |
5967c0bf | 406 | { |
81ab22b6 FC |
407 | for (auto id : WholeEnum<Http::HdrType>()) |
408 | header_mangler_dump_access(entry, name, known[id], Http::HeaderLookupTable.lookup(id).name); | |
5967c0bf | 409 | |
81ab22b6 FC |
410 | for (auto i : custom) |
411 | header_mangler_dump_access(entry, name, i.second, i.first.c_str()); | |
3b07476b CT |
412 | |
413 | header_mangler_dump_access(entry, name, all, "All"); | |
5967c0bf | 414 | } |
3b07476b CT |
415 | |
416 | void | |
417 | HeaderManglers::dumpReplacement(StoreEntry * entry, const char *name) const | |
418 | { | |
81ab22b6 FC |
419 | for (auto id : WholeEnum<Http::HdrType>()) { |
420 | header_mangler_dump_replacement(entry, name, known[id], Http::HeaderLookupTable.lookup(id).name); | |
3b07476b CT |
421 | } |
422 | ||
81ab22b6 FC |
423 | for (auto i: custom) { |
424 | header_mangler_dump_replacement(entry, name, i.second, i.first.c_str()); | |
3b07476b CT |
425 | } |
426 | ||
427 | header_mangler_dump_replacement(entry, name, all, "All"); | |
428 | } | |
429 | ||
001d55dc | 430 | headerMangler * |
3b07476b CT |
431 | HeaderManglers::track(const char *name) |
432 | { | |
81ab22b6 FC |
433 | if (strcmp(name, "All") == 0) |
434 | return &all; | |
3b07476b | 435 | |
81ab22b6 | 436 | const Http::HdrType id = Http::HeaderLookupTable.lookup(SBuf(name)).id; |
3b07476b | 437 | |
81ab22b6 FC |
438 | if (id != Http::HdrType::BAD_HDR) |
439 | return &known[id]; | |
440 | ||
441 | if (strcmp(name, "Other") == 0) | |
442 | return &known[Http::HdrType::OTHER]; | |
3b07476b | 443 | |
81ab22b6 | 444 | return &custom[name]; |
3b07476b CT |
445 | } |
446 | ||
447 | void | |
448 | HeaderManglers::setReplacement(const char *name, const char *value) | |
449 | { | |
450 | // for backword compatibility, we allow replacements to be configured | |
451 | // for headers w/o access rules, but such replacements are ignored | |
001d55dc | 452 | headerMangler *m = track(name); |
3b07476b CT |
453 | |
454 | safe_free(m->replacement); // overwrite old value if any | |
455 | m->replacement = xstrdup(value); | |
456 | } | |
457 | ||
001d55dc | 458 | const headerMangler * |
3b07476b CT |
459 | HeaderManglers::find(const HttpHeaderEntry &e) const |
460 | { | |
461 | // a known header with a configured ACL list | |
1da82544 | 462 | if (e.id != Http::HdrType::OTHER && Http::any_HdrType_enum_value(e.id) && |
d3abcb0d | 463 | known[e.id].access_list) |
3b07476b CT |
464 | return &known[e.id]; |
465 | ||
466 | // a custom header | |
789217a2 | 467 | if (e.id == Http::HdrType::OTHER) { |
3b07476b CT |
468 | // does it have an ACL list configured? |
469 | // Optimize: use a name type that we do not need to convert to here | |
d5f18517 AJ |
470 | SBuf tmp(e.name); // XXX: performance regression. c_str() reallocates |
471 | const ManglersByName::const_iterator i = custom.find(tmp.c_str()); | |
3b07476b CT |
472 | if (i != custom.end()) |
473 | return &i->second; | |
474 | } | |
475 | ||
476 | // Next-to-last resort: "Other" rules match any custom header | |
789217a2 FC |
477 | if (e.id == Http::HdrType::OTHER && known[Http::HdrType::OTHER].access_list) |
478 | return &known[Http::HdrType::OTHER]; | |
3b07476b CT |
479 | |
480 | // Last resort: "All" rules match any header | |
481 | if (all.access_list) | |
482 | return &all; | |
483 | ||
aee3523a | 484 | return nullptr; |
3b07476b CT |
485 | } |
486 | ||
f4698e0b | 487 | void |
4bf68cfa | 488 | httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd) |
f4698e0b | 489 | { |
aee3523a | 490 | ACLFilledChecklist checklist(nullptr, request, nullptr); |
f4698e0b | 491 | |
b6f3c19a CT |
492 | checklist.al = al; |
493 | if (al && al->reply) { | |
494 | checklist.reply = al->reply.getRaw(); | |
495 | HTTPMSGLOCK(checklist.reply); | |
496 | } | |
497 | ||
f4698e0b | 498 | for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) { |
06bf5384 | 499 | if (!hwa->aclList || checklist.fastCheck(hwa->aclList).allowed()) { |
aee3523a | 500 | const char *fieldValue = nullptr; |
f4698e0b CT |
501 | MemBuf mb; |
502 | if (hwa->quoted) { | |
aee3523a | 503 | if (al != nullptr) { |
f4698e0b | 504 | mb.init(); |
4bf68cfa | 505 | hwa->valueFormat->assemble(mb, al, 0); |
f4698e0b CT |
506 | fieldValue = mb.content(); |
507 | } | |
508 | } else { | |
509 | fieldValue = hwa->fieldValue.c_str(); | |
510 | } | |
511 | ||
512 | if (!fieldValue || fieldValue[0] == '\0') | |
513 | fieldValue = "-"; | |
514 | ||
d5f18517 | 515 | HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, SBuf(hwa->fieldName), fieldValue); |
f4698e0b CT |
516 | heads->addEntry(e); |
517 | } | |
518 | } | |
519 | } | |
f53969cc | 520 |