]>
Commit | Line | Data |
---|---|---|
774c051c | 1 | /* |
f70aedc4 | 2 | * Copyright (C) 1996-2021 The Squid Software Foundation and contributors |
774c051c | 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. | |
774c051c | 7 | */ |
8 | ||
9 | #ifndef SQUID_ICAPMODXACT_H | |
10 | #define SQUID_ICAPMODXACT_H | |
11 | ||
af0ded40 | 12 | #include "AccessLogEntry.h" |
26cc52cb AR |
13 | #include "adaptation/icap/InOut.h" |
14 | #include "adaptation/icap/Launcher.h" | |
602d9612 A |
15 | #include "adaptation/icap/Xaction.h" |
16 | #include "BodyPipe.h" | |
51c3e7f0 | 17 | #include "http/one/forward.h" |
417da400 | 18 | #include "http/one/TeChunkedParser.h" |
5f8252d2 | 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 | */ | |
774c051c | 29 | |
af6a12ee AJ |
30 | namespace Adaptation |
31 | { | |
e1381638 AJ |
32 | namespace Icap |
33 | { | |
26cc52cb | 34 | |
774c051c | 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 | |
47f6e231 | 42 | void expect(int64_t aSize); // expect with any, even unknown size |
774c051c | 43 | bool expected() const; |
44 | ||
45 | /* other members can be accessed iff expected() */ | |
46 | ||
47 | bool knownSize() const; | |
47f6e231 | 48 | uint64_t size() const; // can be accessed iff knownSize() |
774c051c | 49 | |
50 | private: | |
51 | enum { dtUnexpected = -2, dtUnknown = -1 }; | |
47f6e231 | 52 | int64_t theData; // combines expectation and size info to save RAM |
774c051c | 53 | }; |
54 | ||
9e008dda | 55 | // Virgin body may be used for two activities: (a) writing preview or prime |
5f8252d2 | 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 | |
774c051c | 61 | { |
62 | ||
63 | public: | |
478cfe99 | 64 | VirginBodyAct(); |
774c051c | 65 | |
5f8252d2 | 66 | void plan(); // the activity may happen; do not consume at or above offset |
61beade2 | 67 | void disable(); // the activity will not continue; no consumption restrictions |
478cfe99 | 68 | |
69 | bool active() const { return theState == stActive; } | |
70 | bool disabled() const { return theState == stDisabled; } | |
774c051c | 71 | |
72 | // methods below require active() | |
73 | ||
47f6e231 | 74 | uint64_t offset() const; // the absolute beginning of not-yet-acted-on data |
5f8252d2 | 75 | void progress(size_t size); // note processed body bytes |
774c051c | 76 | |
77 | private: | |
47f6e231 | 78 | int64_t theStart; // unprocessed virgin body data offset |
478cfe99 | 79 | |
80 | typedef enum { stUndecided, stActive, stDisabled } State; | |
81 | State theState; | |
774c051c | 82 | }; |
83 | ||
84 | // maintains preview-related sizes | |
85 | ||
26cc52cb | 86 | class Preview |
774c051c | 87 | { |
88 | ||
89 | public: | |
26cc52cb | 90 | Preview(); // disabled |
774c051c | 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 | ||
c99de607 | 101 | void wrote(size_t size, bool wroteEof); |
774c051c | 102 | |
103 | private: | |
104 | size_t theWritten; | |
105 | size_t theAd; | |
106 | enum State { stDisabled, stWriting, stIeof, stDone } theState; | |
107 | }; | |
108 | ||
69c698a3 EB |
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 | ||
417da400 EB |
124 | /// handles ICAP-specific chunk extensions supported by Squid |
125 | class ChunkExtensionValueParser: public Http1::ChunkExtensionValueParser | |
126 | { | |
127 | public: | |
128 | /* Http1::ChunkExtensionValueParser API */ | |
129 | virtual 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 | ||
26cc52cb | 141 | class ModXact: public Xaction, public BodyProducer, public BodyConsumer |
774c051c | 142 | { |
5c2f68b7 | 143 | CBDATA_CLASS(ModXact); |
774c051c | 144 | |
774c051c | 145 | public: |
63df1d28 | 146 | ModXact(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, ServiceRep::Pointer &s); |
c7d51c86 | 147 | virtual ~ModXact(); |
774c051c | 148 | |
5f8252d2 | 149 | // BodyProducer methods |
bd7f2ede | 150 | virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer); |
151 | virtual void noteBodyConsumerAborted(BodyPipe::Pointer); | |
774c051c | 152 | |
5f8252d2 | 153 | // BodyConsumer methods |
bd7f2ede | 154 | virtual void noteMoreBodyDataAvailable(BodyPipe::Pointer); |
155 | virtual void noteBodyProductionEnded(BodyPipe::Pointer); | |
156 | virtual void noteBodyProducerAborted(BodyPipe::Pointer); | |
774c051c | 157 | |
158 | // comm handlers | |
159 | virtual void handleCommConnected(); | |
160 | virtual void handleCommWrote(size_t size); | |
161 | virtual void handleCommRead(size_t size); | |
162 | void handleCommWroteHeaders(); | |
163 | void handleCommWroteBody(); | |
164 | ||
165 | // service waiting | |
166 | void noteServiceReady(); | |
2dba5b8e | 167 | void noteServiceAvailable(); |
774c051c | 168 | |
5f8252d2 | 169 | public: |
26cc52cb AR |
170 | InOut virgin; |
171 | InOut adapted; | |
5f8252d2 | 172 | |
478cfe99 | 173 | // bypasses exceptions if needed and possible |
0a8bbeeb | 174 | virtual void callException(const std::exception &e); |
478cfe99 | 175 | |
64b66b76 | 176 | /// record error detail in the virgin request if possible |
83b053a0 | 177 | virtual void detailError(const ErrorDetail::Pointer &errDetail); |
7a957a93 | 178 | // Icap::Xaction API |
129fe2a1 | 179 | virtual void clearError(); |
d4ddb3e6 CT |
180 | /// The master transaction log entry |
181 | virtual AccessLogEntry::Pointer masterLogEntry() { return alMaster; } | |
64b66b76 | 182 | |
774c051c | 183 | private: |
5f8252d2 | 184 | virtual void start(); |
185 | ||
3ff65596 AR |
186 | /// locates the request, either as a cause or as a virgin message itself |
187 | const HttpRequest &virginRequest() const; // Must always be available | |
188 | ||
774c051c | 189 | void estimateVirginBody(); |
5f8252d2 | 190 | void makeAdaptedBodyPipe(const char *what); |
774c051c | 191 | |
192 | void waitForService(); | |
193 | ||
194 | // will not send anything [else] on the adapted pipe | |
195 | bool doneSending() const; | |
196 | ||
197 | void startWriting(); | |
198 | void writeMore(); | |
5f8252d2 | 199 | void writePreviewBody(); |
774c051c | 200 | void writePrimeBody(); |
201 | void writeSomeBody(const char *label, size_t size); | |
23e05fb1 | 202 | void decideWritingAfterPreview(const char *previewKind); |
774c051c | 203 | |
204 | void startReading(); | |
205 | void readMore(); | |
206 | virtual bool doneReading() const { return commEof || state.doneParsing(); } | |
c99de607 | 207 | virtual bool doneWriting() const { return state.doneWriting(); } |
774c051c | 208 | |
5f8252d2 | 209 | size_t virginContentSize(const VirginBodyAct &act) const; |
210 | const char *virginContentData(const VirginBodyAct &act) const; | |
211 | bool virginBodyEndReached(const VirginBodyAct &act) const; | |
212 | ||
774c051c | 213 | void makeRequestHeaders(MemBuf &buf); |
83c51da9 | 214 | void makeAllowHeader(MemBuf &buf); |
5f8252d2 | 215 | void makeUsernameHeader(const HttpRequest *request, MemBuf &buf); |
774c051c | 216 | void addLastRequestChunk(MemBuf &buf); |
c99de607 | 217 | void openChunk(MemBuf &buf, size_t chunkSize, bool ieof); |
218 | void closeChunk(MemBuf &buf); | |
774c051c | 219 | void virginConsume(); |
5f8252d2 | 220 | void finishNullOrEmptyBodyPreview(MemBuf &buf); |
774c051c | 221 | |
c824c43b | 222 | void decideOnPreview(); |
223 | void decideOnRetries(); | |
774c051c | 224 | bool shouldAllow204(); |
83c51da9 CT |
225 | bool shouldAllow206any(); |
226 | bool shouldAllow206in(); | |
227 | bool shouldAllow206out(); | |
c824c43b | 228 | bool canBackupEverything() const; |
229 | ||
774c051c | 230 | void prepBackup(size_t expectedSize); |
231 | void backup(const MemBuf &buf); | |
232 | ||
233 | void parseMore(); | |
234 | ||
235 | void parseHeaders(); | |
236 | void parseIcapHead(); | |
237 | void parseHttpHead(); | |
63df1d28 | 238 | bool parseHead(Http::Message *head); |
774c051c | 239 | |
5f8252d2 | 240 | void decideOnParsingBody(); |
774c051c | 241 | void parseBody(); |
69c698a3 | 242 | void parseIcapTrailer(); |
774c051c | 243 | void maybeAllocateHttpMsg(); |
244 | ||
245 | void handle100Continue(); | |
b559db5d | 246 | bool validate200Ok(); |
774c051c | 247 | void handle200Ok(); |
248 | void handle204NoContent(); | |
83c51da9 | 249 | void handle206PartialContent(); |
774c051c | 250 | void handleUnknownScode(); |
251 | ||
478cfe99 | 252 | void bypassFailure(); |
253 | ||
254 | void startSending(); | |
a22e6cd3 | 255 | void disableBypass(const char *reason, bool includeGroupBypass); |
478cfe99 | 256 | |
257 | void prepEchoing(); | |
83c51da9 | 258 | void prepPartialBodyEchoing(uint64_t pos); |
774c051c | 259 | void echoMore(); |
63df1d28 | 260 | void updateSources(); ///< Update the Http::Message sources |
774c051c | 261 | |
262 | virtual bool doneAll() const; | |
5f8252d2 | 263 | virtual void swanSong(); |
774c051c | 264 | |
774c051c | 265 | void stopReceiving(); |
266 | void stopSending(bool nicely); | |
c99de607 | 267 | void stopWriting(bool nicely); |
69c698a3 | 268 | void stopParsing(const bool checkUnparsedData = true); |
774c051c | 269 | void stopBackup(); |
270 | ||
271 | virtual void fillPendingStatus(MemBuf &buf) const; | |
272 | virtual void fillDoneStatus(MemBuf &buf) const; | |
3cfc19b3 | 273 | virtual bool fillVirginHttpHeader(MemBuf&) const; |
774c051c | 274 | |
275 | private: | |
69c698a3 EB |
276 | /// parses a message header or trailer |
277 | /// \returns true on success | |
278 | /// \returns false if more data is needed | |
279 | /// \throw TextException on unrecoverable error | |
280 | template<class Part> | |
281 | bool parsePart(Part *part, const char *description); | |
282 | ||
63df1d28 AJ |
283 | void packHead(MemBuf &httpBuf, const Http::Message *head); |
284 | void encapsulateHead(MemBuf &icapBuf, const char *section, MemBuf &httpBuf, const Http::Message *head); | |
774c051c | 285 | bool gotEncapsulated(const char *section) const; |
69c698a3 EB |
286 | /// whether ICAP response header indicates HTTP header presence |
287 | bool expectHttpHeader() const; | |
288 | /// whether ICAP response header indicates HTTP body presence | |
289 | bool expectHttpBody() const; | |
290 | /// whether ICAP response header indicates ICAP trailers presence | |
291 | bool expectIcapTrailers() const; | |
5f8252d2 | 292 | void checkConsuming(); |
774c051c | 293 | |
3ff65596 | 294 | virtual void finalizeLogInfo(); |
774c051c | 295 | |
296 | SizedEstimate virginBody; | |
5f8252d2 | 297 | VirginBodyAct virginBodyWriting; // virgin body writing state |
298 | VirginBodyAct virginBodySending; // virgin body sending state | |
47f6e231 | 299 | uint64_t virginConsumed; // virgin data consumed so far |
26cc52cb | 300 | Preview preview; // use for creating (writing) the preview |
774c051c | 301 | |
db1720f8 | 302 | Http1::TeChunkedParser *bodyParser; // ICAP response body parser |
774c051c | 303 | |
478cfe99 | 304 | bool canStartBypass; // enables bypass of transaction failures |
a22e6cd3 | 305 | bool protectGroupBypass; // protects ServiceGroup-wide bypass of failures |
478cfe99 | 306 | |
bae917ac CT |
307 | /** |
308 | * size of HTTP header in ICAP reply or -1 if there is not any encapsulated | |
309 | * message data | |
310 | */ | |
311 | int64_t replyHttpHeaderSize; | |
312 | /** | |
dcaab393 | 313 | * size of dechunked HTTP body in ICAP reply or -1 if there is not any |
bae917ac CT |
314 | * encapsulated message data |
315 | */ | |
316 | int64_t replyHttpBodySize; | |
3ff65596 AR |
317 | |
318 | int adaptHistoryId; ///< adaptation history slot reservation | |
319 | ||
69c698a3 EB |
320 | TrailerParser *trailerParser; |
321 | ||
417da400 EB |
322 | ChunkExtensionValueParser extensionParser; |
323 | ||
774c051c | 324 | class State |
325 | { | |
326 | ||
327 | public: | |
328 | State(); | |
329 | ||
330 | public: | |
331 | ||
5f8252d2 | 332 | bool serviceWaiting; // waiting for ICAP service options |
333 | bool allowedPostview204; // mmust handle 204 No Content outside preview | |
83c51da9 CT |
334 | bool allowedPostview206; // must handle 206 Partial Content outside preview |
335 | bool allowedPreview206; // must handle 206 Partial Content inside preview | |
336 | bool readyForUob; ///< got a 206 response and expect a use-origin-body | |
2dba5b8e | 337 | bool waitedForService; ///< true if was queued at least once |
774c051c | 338 | |
339 | // will not write anything [else] to the ICAP server connection | |
c99de607 | 340 | bool doneWriting() const { return writing == writingReallyDone; } |
774c051c | 341 | |
5f8252d2 | 342 | // will not use virgin.body_pipe |
9e008dda AJ |
343 | bool doneConsumingVirgin() const { |
344 | return writing >= writingAlmostDone | |
7ddcfbab A |
345 | && ((sending == sendingAdapted && !readyForUob) || |
346 | sending == sendingDone); | |
9e008dda | 347 | } |
5f8252d2 | 348 | |
774c051c | 349 | // parsed entire ICAP response from the ICAP server |
350 | bool doneParsing() const { return parsing == psDone; } | |
351 | ||
352 | // is parsing ICAP or HTTP headers read from the ICAP server | |
9e008dda | 353 | bool parsingHeaders() const { |
774c051c | 354 | return parsing == psIcapHeader || |
355 | parsing == psHttpHeader; | |
356 | } | |
357 | ||
69c698a3 | 358 | enum Parsing { psIcapHeader, psHttpHeader, psBody, psIcapTrailer, psDone } parsing; |
774c051c | 359 | |
360 | // measures ICAP request writing progress | |
361 | enum Writing { writingInit, writingConnect, writingHeaders, | |
9e008dda AJ |
362 | writingPreview, writingPaused, writingPrime, |
363 | writingAlmostDone, // waiting for the last write() call to finish | |
364 | writingReallyDone | |
365 | } writing; | |
774c051c | 366 | |
367 | enum Sending { sendingUndecided, sendingVirgin, sendingAdapted, | |
9e008dda AJ |
368 | sendingDone |
369 | } sending; | |
2fadd50d | 370 | } state; |
774c051c | 371 | |
af0ded40 | 372 | AccessLogEntry::Pointer alMaster; ///< Master transaction AccessLogEntry |
774c051c | 373 | }; |
374 | ||
26cc52cb AR |
375 | // An Launcher that stores ModXact construction info and |
376 | // creates ModXact when needed | |
377 | class ModXactLauncher: public Launcher | |
c824c43b | 378 | { |
5c2f68b7 AJ |
379 | CBDATA_CLASS(ModXactLauncher); |
380 | ||
c824c43b | 381 | public: |
63df1d28 | 382 | ModXactLauncher(Http::Message *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp, Adaptation::ServicePointer s); |
c824c43b | 383 | |
384 | protected: | |
26cc52cb | 385 | virtual Xaction *createXaction(); |
c824c43b | 386 | |
3ff65596 AR |
387 | virtual void swanSong(); |
388 | ||
389 | /// starts or stops transaction accounting in ICAP history | |
390 | void updateHistory(bool start); | |
391 | ||
26cc52cb | 392 | InOut virgin; |
c824c43b | 393 | |
af0ded40 | 394 | AccessLogEntry::Pointer al; |
c824c43b | 395 | }; |
774c051c | 396 | |
26cc52cb AR |
397 | } // namespace Icap |
398 | } // namespace Adaptation | |
399 | ||
774c051c | 400 | #endif /* SQUID_ICAPMOD_XACT_H */ |
f53969cc | 401 |