]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/icap/ModXact.h
Use ERR_ACCESS_DENIED for HTTP 403 (Forbidden) errors (#1899)
[thirdparty/squid.git] / src / adaptation / icap / ModXact.h
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 #ifndef SQUID_SRC_ADAPTATION_ICAP_MODXACT_H
10 #define SQUID_SRC_ADAPTATION_ICAP_MODXACT_H
11
12 #include "AccessLogEntry.h"
13 #include "adaptation/icap/InOut.h"
14 #include "adaptation/icap/Launcher.h"
15 #include "adaptation/icap/Xaction.h"
16 #include "BodyPipe.h"
17 #include "http/one/forward.h"
18 #include "http/one/TeChunkedParser.h"
19
20 /*
21 * ICAPModXact implements ICAP REQMOD and RESPMOD transaction using
22 * ICAPXaction as the base. The ICAPModXact receives a virgin HTTP message
23 * from an ICAP vecoring point, (a.k.a., initiator), communicates with the
24 * ICAP server, and sends the adapted HTTP message headers back.
25 * Virgin/adapted HTTP message body is reveived/sent using BodyPipe
26 * interface. The initiator (or its associate) is expected to send and/or
27 * receive the HTTP body.
28 */
29
30 namespace Adaptation
31 {
32 namespace Icap
33 {
34
35 // estimated future presence and size of something (e.g., HTTP body)
36
37 class SizedEstimate
38 {
39
40 public:
41 SizedEstimate(); // not expected by default
42 void expect(int64_t aSize); // expect with any, even unknown size
43 bool expected() const;
44
45 /* other members can be accessed iff expected() */
46
47 bool knownSize() const;
48 uint64_t size() const; // can be accessed iff knownSize()
49
50 private:
51 enum { dtUnexpected = -2, dtUnknown = -1 };
52 int64_t theData; // combines expectation and size info to save RAM
53 };
54
55 // Virgin body may be used for two activities: (a) writing preview or prime
56 // body to the ICAP server and (b) sending the body back in the echo mode.
57 // Both activities use the same BodyPipe and may be active at the same time.
58 // This class is used to maintain the state of body writing or sending
59 // activity and to coordinate consumption of the shared virgin body buffer.
60 class VirginBodyAct
61 {
62
63 public:
64 VirginBodyAct();
65
66 void plan(); // the activity may happen; do not consume at or above offset
67 void disable(); // the activity will not continue; no consumption restrictions
68
69 bool active() const { return theState == stActive; }
70 bool disabled() const { return theState == stDisabled; }
71
72 // methods below require active()
73
74 uint64_t offset() const; // the absolute beginning of not-yet-acted-on data
75 void progress(size_t size); // note processed body bytes
76
77 private:
78 int64_t theStart; // unprocessed virgin body data offset
79
80 typedef enum { stUndecided, stActive, stDisabled } State;
81 State theState;
82 };
83
84 // maintains preview-related sizes
85
86 class Preview
87 {
88
89 public:
90 Preview(); // disabled
91 void enable(size_t anAd); // enabled with advertised size
92 bool enabled() const;
93
94 /* other members can be accessed iff enabled() */
95
96 size_t ad() const; // advertised preview size
97 size_t debt() const; // remains to write
98 bool done() const; // wrote everything
99 bool ieof() const; // premature EOF
100
101 void wrote(size_t size, bool wroteEof);
102
103 private:
104 size_t theWritten;
105 size_t theAd;
106 enum State { stDisabled, stWriting, stIeof, stDone } theState;
107 };
108
109 /// Parses and stores ICAP trailer header block.
110 class TrailerParser
111 {
112 public:
113 TrailerParser() : trailer(hoReply), hdr_sz(0) {}
114 /// Parses trailers stored in a buffer.
115 /// \returns true and sets hdr_sz on success
116 /// \returns false and sets *error to zero when needs more data
117 /// \returns false and sets *error to a positive Http::StatusCode on error
118 bool parse(const char *buf, int len, int atEnd, Http::StatusCode *error);
119 HttpHeader trailer;
120 /// parsed trailer size if parse() was successful
121 size_t hdr_sz; // pedantic XXX: wrong type dictated by HttpHeader::parse() API
122 };
123
124 /// handles ICAP-specific chunk extensions supported by Squid
125 class ChunkExtensionValueParser: public Http1::ChunkExtensionValueParser
126 {
127 public:
128 /* Http1::ChunkExtensionValueParser API */
129 void parse(Tokenizer &tok, const SBuf &extName) override;
130
131 bool sawUseOriginalBody() const { return useOriginalBody_ >= 0; }
132 uint64_t useOriginalBody() const { assert(sawUseOriginalBody()); return static_cast<uint64_t>(useOriginalBody_); }
133
134 private:
135 static const SBuf UseOriginalBodyName;
136
137 /// the value of the parsed use-original-body chunk extension (or -1)
138 int64_t useOriginalBody_ = -1;
139 };
140
141 class ModXact: public Xaction, public BodyProducer, public BodyConsumer
142 {
143 CBDATA_CHILD(ModXact);
144
145 public:
146 ModXact(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, ServiceRep::Pointer &s);
147 ~ModXact() override;
148
149 // BodyProducer methods
150 void noteMoreBodySpaceAvailable(BodyPipe::Pointer) override;
151 void noteBodyConsumerAborted(BodyPipe::Pointer) override;
152
153 // BodyConsumer methods
154 void noteMoreBodyDataAvailable(BodyPipe::Pointer) override;
155 void noteBodyProductionEnded(BodyPipe::Pointer) override;
156 void noteBodyProducerAborted(BodyPipe::Pointer) override;
157
158 /* Xaction API */
159 void startShoveling() override;
160 void handleCommWrote(size_t size) override;
161 void handleCommRead(size_t size) override;
162
163 void handleCommWroteHeaders();
164 void handleCommWroteBody();
165
166 // service waiting
167 void noteServiceReady();
168 void noteServiceAvailable();
169
170 public:
171 InOut virgin;
172 InOut adapted;
173
174 // bypasses exceptions if needed and possible
175 void callException(const std::exception &e) override;
176
177 /// record error detail in the virgin request if possible
178 void detailError(const ErrorDetail::Pointer &errDetail) override;
179 // Icap::Xaction API
180 void clearError() override;
181 /// The master transaction log entry
182 AccessLogEntry::Pointer masterLogEntry() override { return alMaster; }
183
184 private:
185 void start() override;
186
187 /// locates the request, either as a cause or as a virgin message itself
188 const HttpRequest &virginRequest() const; // Must always be available
189
190 void estimateVirginBody();
191 void makeAdaptedBodyPipe(const char *what);
192
193 void waitForService();
194
195 // will not send anything [else] on the adapted pipe
196 bool doneSending() const;
197
198 void startWriting();
199 void writeMore();
200 void writePreviewBody();
201 void writePrimeBody();
202 void writeSomeBody(const char *label, size_t size);
203 void decideWritingAfterPreview(const char *previewKind);
204
205 void startReading();
206 void readMore();
207 bool doneReading() const override { return commEof || state.doneParsing(); }
208 bool doneWriting() const override { return state.doneWriting(); }
209
210 size_t virginContentSize(const VirginBodyAct &act) const;
211 const char *virginContentData(const VirginBodyAct &act) const;
212 bool virginBodyEndReached(const VirginBodyAct &act) const;
213
214 void makeRequestHeaders(MemBuf &buf);
215 void makeAllowHeader(MemBuf &buf);
216 void makeUsernameHeader(const HttpRequest *request, MemBuf &buf);
217 void addLastRequestChunk(MemBuf &buf);
218 void openChunk(MemBuf &buf, size_t chunkSize, bool ieof);
219 void closeChunk(MemBuf &buf);
220 void virginConsume();
221 void finishNullOrEmptyBodyPreview(MemBuf &buf);
222
223 void decideOnPreview();
224 void decideOnRetries();
225 bool shouldAllow204();
226 bool shouldAllow206any();
227 bool shouldAllow206in();
228 bool shouldAllow206out();
229 bool canBackupEverything() const;
230
231 void prepBackup(size_t expectedSize);
232 void backup(const MemBuf &buf);
233
234 void parseMore();
235
236 void parseHeaders();
237 void parseIcapHead();
238 void parseHttpHead();
239 bool parseHead(Http::Message *head);
240
241 void decideOnParsingBody();
242 void parseBody();
243 void parseIcapTrailer();
244 void maybeAllocateHttpMsg();
245
246 void handle100Continue();
247 bool validate200Ok();
248 void handle200Ok();
249 void handle204NoContent();
250 void handle206PartialContent();
251 void handleUnknownScode();
252
253 void bypassFailure();
254
255 void startSending();
256 void disableBypass(const char *reason, bool includeGroupBypass);
257
258 void prepEchoing();
259 void prepPartialBodyEchoing(uint64_t pos);
260 void echoMore();
261 void updateSources(); ///< Update the Http::Message sources
262
263 bool doneAll() const override;
264 void swanSong() override;
265
266 void stopReceiving();
267 void stopSending(bool nicely);
268 void stopWriting(bool nicely);
269 void stopParsing(const bool checkUnparsedData = true);
270 void stopBackup();
271
272 void fillPendingStatus(MemBuf &buf) const override;
273 void fillDoneStatus(MemBuf &buf) const override;
274 bool fillVirginHttpHeader(MemBuf&) const override;
275
276 private:
277 /// parses a message header or trailer
278 /// \returns true on success
279 /// \returns false if more data is needed
280 /// \throw TextException on unrecoverable error
281 template<class Part>
282 bool parsePart(Part *part, const char *description);
283
284 void packHead(MemBuf &httpBuf, const Http::Message *head);
285 void encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const Http::Message *head);
286 bool gotEncapsulated(const char *section) const;
287 /// whether ICAP response header indicates HTTP header presence
288 bool expectHttpHeader() const;
289 /// whether ICAP response header indicates HTTP body presence
290 bool expectHttpBody() const;
291 /// whether ICAP response header indicates ICAP trailers presence
292 bool expectIcapTrailers() const;
293 void checkConsuming();
294
295 void finalizeLogInfo() override;
296
297 SizedEstimate virginBody;
298 VirginBodyAct virginBodyWriting; // virgin body writing state
299 VirginBodyAct virginBodySending; // virgin body sending state
300 uint64_t virginConsumed; // virgin data consumed so far
301 Preview preview; // use for creating (writing) the preview
302
303 Http1::TeChunkedParser *bodyParser; // ICAP response body parser
304
305 bool canStartBypass; // enables bypass of transaction failures
306 bool protectGroupBypass; // protects ServiceGroup-wide bypass of failures
307
308 /**
309 * size of HTTP header in ICAP reply or -1 if there is not any encapsulated
310 * message data
311 */
312 int64_t replyHttpHeaderSize;
313 /**
314 * size of dechunked HTTP body in ICAP reply or -1 if there is not any
315 * encapsulated message data
316 */
317 int64_t replyHttpBodySize;
318
319 int adaptHistoryId; ///< adaptation history slot reservation
320
321 TrailerParser *trailerParser;
322
323 ChunkExtensionValueParser extensionParser;
324
325 class State
326 {
327
328 public:
329 State();
330
331 public:
332
333 bool serviceWaiting; // waiting for ICAP service options
334 bool allowedPostview204; // mmust handle 204 No Content outside preview
335 bool allowedPostview206; // must handle 206 Partial Content outside preview
336 bool allowedPreview206; // must handle 206 Partial Content inside preview
337 bool readyForUob; ///< got a 206 response and expect a use-origin-body
338 bool waitedForService; ///< true if was queued at least once
339
340 // will not write anything [else] to the ICAP server connection
341 bool doneWriting() const { return writing == writingReallyDone; }
342
343 // will not use virgin.body_pipe
344 bool doneConsumingVirgin() const {
345 return writing >= writingAlmostDone
346 && ((sending == sendingAdapted && !readyForUob) ||
347 sending == sendingDone);
348 }
349
350 // parsed entire ICAP response from the ICAP server
351 bool doneParsing() const { return parsing == psDone; }
352
353 // is parsing ICAP or HTTP headers read from the ICAP server
354 bool parsingHeaders() const {
355 return parsing == psIcapHeader ||
356 parsing == psHttpHeader;
357 }
358
359 enum Parsing { psIcapHeader, psHttpHeader, psBody, psIcapTrailer, psDone } parsing;
360
361 // measures ICAP request writing progress
362 enum Writing { writingInit, writingConnect, writingHeaders,
363 writingPreview, writingPaused, writingPrime,
364 writingAlmostDone, // waiting for the last write() call to finish
365 writingReallyDone
366 } writing;
367
368 enum Sending { sendingUndecided, sendingVirgin, sendingAdapted,
369 sendingDone
370 } sending;
371 } state;
372
373 AccessLogEntry::Pointer alMaster; ///< Master transaction AccessLogEntry
374 };
375
376 // An Launcher that stores ModXact construction info and
377 // creates ModXact when needed
378 class ModXactLauncher: public Launcher
379 {
380 CBDATA_CHILD(ModXactLauncher);
381
382 public:
383 ModXactLauncher(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, Adaptation::ServicePointer s);
384
385 protected:
386 Xaction *createXaction() override;
387
388 void swanSong() override;
389
390 /// starts or stops transaction accounting in ICAP history
391 void updateHistory(bool start);
392
393 InOut virgin;
394
395 AccessLogEntry::Pointer al;
396 };
397
398 } // namespace Icap
399 } // namespace Adaptation
400
401 #endif /* SQUID_SRC_ADAPTATION_ICAP_MODXACT_H */
402