2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
9 #ifndef SQUID_ICAPMODXACT_H
10 #define SQUID_ICAPMODXACT_H
12 #include "AccessLogEntry.h"
13 #include "adaptation/icap/InOut.h"
14 #include "adaptation/icap/Launcher.h"
15 #include "adaptation/icap/Xaction.h"
17 #include "http/one/forward.h"
18 #include "http/one/TeChunkedParser.h"
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.
35 // estimated future presence and size of something (e.g., HTTP body)
41 SizedEstimate(); // not expected by default
42 void expect(int64_t aSize
); // expect with any, even unknown size
43 bool expected() const;
45 /* other members can be accessed iff expected() */
47 bool knownSize() const;
48 uint64_t size() const; // can be accessed iff knownSize()
51 enum { dtUnexpected
= -2, dtUnknown
= -1 };
52 int64_t theData
; // combines expectation and size info to save RAM
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.
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
69 bool active() const { return theState
== stActive
; }
70 bool disabled() const { return theState
== stDisabled
; }
72 // methods below require active()
74 uint64_t offset() const; // the absolute beginning of not-yet-acted-on data
75 void progress(size_t size
); // note processed body bytes
78 int64_t theStart
; // unprocessed virgin body data offset
80 typedef enum { stUndecided
, stActive
, stDisabled
} State
;
84 // maintains preview-related sizes
90 Preview(); // disabled
91 void enable(size_t anAd
); // enabled with advertised size
94 /* other members can be accessed iff enabled() */
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
101 void wrote(size_t size
, bool wroteEof
);
106 enum State
{ stDisabled
, stWriting
, stIeof
, stDone
} theState
;
109 /// Parses and stores ICAP trailer header block.
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
);
120 /// parsed trailer size if parse() was successful
121 size_t hdr_sz
; // pedantic XXX: wrong type dictated by HttpHeader::parse() API
124 /// handles ICAP-specific chunk extensions supported by Squid
125 class ChunkExtensionValueParser
: public Http1::ChunkExtensionValueParser
128 /* Http1::ChunkExtensionValueParser API */
129 virtual void parse(Tokenizer
&tok
, const SBuf
&extName
) override
;
131 bool sawUseOriginalBody() const { return useOriginalBody_
>= 0; }
132 uint64_t useOriginalBody() const { assert(sawUseOriginalBody()); return static_cast<uint64_t>(useOriginalBody_
); }
135 static const SBuf UseOriginalBodyName
;
137 /// the value of the parsed use-original-body chunk extension (or -1)
138 int64_t useOriginalBody_
= -1;
141 class ModXact
: public Xaction
, public BodyProducer
, public BodyConsumer
143 CBDATA_CLASS(ModXact
);
146 ModXact(Http::Message
*virginHeader
, HttpRequest
*virginCause
, AccessLogEntry::Pointer
&alp
, ServiceRep::Pointer
&s
);
149 // BodyProducer methods
150 virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer
);
151 virtual void noteBodyConsumerAborted(BodyPipe::Pointer
);
153 // BodyConsumer methods
154 virtual void noteMoreBodyDataAvailable(BodyPipe::Pointer
);
155 virtual void noteBodyProductionEnded(BodyPipe::Pointer
);
156 virtual void noteBodyProducerAborted(BodyPipe::Pointer
);
159 virtual void handleCommConnected();
160 virtual void handleCommWrote(size_t size
);
161 virtual void handleCommRead(size_t size
);
162 void handleCommWroteHeaders();
163 void handleCommWroteBody();
166 void noteServiceReady();
167 void noteServiceAvailable();
173 // bypasses exceptions if needed and possible
174 virtual void callException(const std::exception
&e
);
176 /// record error detail in the virgin request if possible
177 virtual void detailError(const ErrorDetail::Pointer
&errDetail
);
179 virtual void clearError();
180 /// The master transaction log entry
181 virtual AccessLogEntry::Pointer
masterLogEntry() { return alMaster
; }
184 virtual void start();
186 /// locates the request, either as a cause or as a virgin message itself
187 const HttpRequest
&virginRequest() const; // Must always be available
189 void estimateVirginBody();
190 void makeAdaptedBodyPipe(const char *what
);
192 void waitForService();
194 // will not send anything [else] on the adapted pipe
195 bool doneSending() const;
199 void writePreviewBody();
200 void writePrimeBody();
201 void writeSomeBody(const char *label
, size_t size
);
202 void decideWritingAfterPreview(const char *previewKind
);
206 virtual bool doneReading() const { return commEof
|| state
.doneParsing(); }
207 virtual bool doneWriting() const { return state
.doneWriting(); }
209 size_t virginContentSize(const VirginBodyAct
&act
) const;
210 const char *virginContentData(const VirginBodyAct
&act
) const;
211 bool virginBodyEndReached(const VirginBodyAct
&act
) const;
213 void makeRequestHeaders(MemBuf
&buf
);
214 void makeAllowHeader(MemBuf
&buf
);
215 void makeUsernameHeader(const HttpRequest
*request
, MemBuf
&buf
);
216 void addLastRequestChunk(MemBuf
&buf
);
217 void openChunk(MemBuf
&buf
, size_t chunkSize
, bool ieof
);
218 void closeChunk(MemBuf
&buf
);
219 void virginConsume();
220 void finishNullOrEmptyBodyPreview(MemBuf
&buf
);
222 void decideOnPreview();
223 void decideOnRetries();
224 bool shouldAllow204();
225 bool shouldAllow206any();
226 bool shouldAllow206in();
227 bool shouldAllow206out();
228 bool canBackupEverything() const;
230 void prepBackup(size_t expectedSize
);
231 void backup(const MemBuf
&buf
);
236 void parseIcapHead();
237 void parseHttpHead();
238 bool parseHead(Http::Message
*head
);
240 void decideOnParsingBody();
242 void parseIcapTrailer();
243 void maybeAllocateHttpMsg();
245 void handle100Continue();
246 bool validate200Ok();
248 void handle204NoContent();
249 void handle206PartialContent();
250 void handleUnknownScode();
252 void bypassFailure();
255 void disableBypass(const char *reason
, bool includeGroupBypass
);
258 void prepPartialBodyEchoing(uint64_t pos
);
260 void updateSources(); ///< Update the Http::Message sources
262 virtual bool doneAll() const;
263 virtual void swanSong();
265 void stopReceiving();
266 void stopSending(bool nicely
);
267 void stopWriting(bool nicely
);
268 void stopParsing(const bool checkUnparsedData
= true);
271 virtual void fillPendingStatus(MemBuf
&buf
) const;
272 virtual void fillDoneStatus(MemBuf
&buf
) const;
273 virtual bool fillVirginHttpHeader(MemBuf
&) const;
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
281 bool parsePart(Part
*part
, const char *description
);
283 void packHead(MemBuf
&httpBuf
, const Http::Message
*head
);
284 void encapsulateHead(MemBuf
&icapBuf
, const char *section
, MemBuf
&httpBuf
, const Http::Message
*head
);
285 bool gotEncapsulated(const char *section
) const;
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;
292 void checkConsuming();
294 virtual void finalizeLogInfo();
296 SizedEstimate virginBody
;
297 VirginBodyAct virginBodyWriting
; // virgin body writing state
298 VirginBodyAct virginBodySending
; // virgin body sending state
299 uint64_t virginConsumed
; // virgin data consumed so far
300 Preview preview
; // use for creating (writing) the preview
302 Http1::TeChunkedParser
*bodyParser
; // ICAP response body parser
304 bool canStartBypass
; // enables bypass of transaction failures
305 bool protectGroupBypass
; // protects ServiceGroup-wide bypass of failures
308 * size of HTTP header in ICAP reply or -1 if there is not any encapsulated
311 int64_t replyHttpHeaderSize
;
313 * size of dechunked HTTP body in ICAP reply or -1 if there is not any
314 * encapsulated message data
316 int64_t replyHttpBodySize
;
318 int adaptHistoryId
; ///< adaptation history slot reservation
320 TrailerParser
*trailerParser
;
322 ChunkExtensionValueParser extensionParser
;
332 bool serviceWaiting
; // waiting for ICAP service options
333 bool allowedPostview204
; // mmust handle 204 No Content outside preview
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
337 bool waitedForService
; ///< true if was queued at least once
339 // will not write anything [else] to the ICAP server connection
340 bool doneWriting() const { return writing
== writingReallyDone
; }
342 // will not use virgin.body_pipe
343 bool doneConsumingVirgin() const {
344 return writing
>= writingAlmostDone
345 && ((sending
== sendingAdapted
&& !readyForUob
) ||
346 sending
== sendingDone
);
349 // parsed entire ICAP response from the ICAP server
350 bool doneParsing() const { return parsing
== psDone
; }
352 // is parsing ICAP or HTTP headers read from the ICAP server
353 bool parsingHeaders() const {
354 return parsing
== psIcapHeader
||
355 parsing
== psHttpHeader
;
358 enum Parsing
{ psIcapHeader
, psHttpHeader
, psBody
, psIcapTrailer
, psDone
} parsing
;
360 // measures ICAP request writing progress
361 enum Writing
{ writingInit
, writingConnect
, writingHeaders
,
362 writingPreview
, writingPaused
, writingPrime
,
363 writingAlmostDone
, // waiting for the last write() call to finish
367 enum Sending
{ sendingUndecided
, sendingVirgin
, sendingAdapted
,
372 AccessLogEntry::Pointer alMaster
; ///< Master transaction AccessLogEntry
375 // An Launcher that stores ModXact construction info and
376 // creates ModXact when needed
377 class ModXactLauncher
: public Launcher
379 CBDATA_CLASS(ModXactLauncher
);
382 ModXactLauncher(Http::Message
*virginHeader
, HttpRequest
*virginCause
, AccessLogEntry::Pointer
&alp
, Adaptation::ServicePointer s
);
385 virtual Xaction
*createXaction();
387 virtual void swanSong();
389 /// starts or stops transaction accounting in ICAP history
390 void updateHistory(bool start
);
394 AccessLogEntry::Pointer al
;
398 } // namespace Adaptation
400 #endif /* SQUID_ICAPMOD_XACT_H */