2 * DEBUG: section 33 Client-side Routines
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 #ifndef SQUID_CLIENTSIDE_H
34 #define SQUID_CLIENTSIDE_H
37 #include "HttpControlMsg.h"
38 #include "HttpParser.h"
41 #include "auth/UserRequest.h"
44 #include "ssl/support.h"
48 class ClientHttpRequest
;
49 class clientStreamNode
;
50 class ChunkedCodingParser
;
59 * This is in fact the processing context for a single HTTP request.
61 * Managing what has been done, and what happens next to the data buffer
62 * holding what we hope is an HTTP request.
64 * Parsing is still a mess of global functions done in conjunction with the
65 * real socket controller which generated ClientHttpRequest.
66 * It also generates one of us and passes us control from there based on
67 * the results of the parse.
69 * After that all the request interpretation and adaptation is in our scope.
70 * Then finally the reply fetcher is created by this and we get the result
71 * back. Which we then have to manage writing of it to the ConnStateData.
73 * The socket level management is done by a ConnStateData which owns us.
74 * The scope of this objects control over a socket consists of the data
75 * buffer received from ConnStateData with an initially unknown length.
76 * When that length is known it sets the end bounary of our acces to the
79 * The individual processing actions are done by other Jobs which we
82 class ClientSocketContext
: public RefCountable
86 typedef RefCount
<ClientSocketContext
> Pointer
;
87 ClientSocketContext(const Comm::ConnectionPointer
&aConn
, ClientHttpRequest
*aReq
);
88 ~ClientSocketContext();
89 bool startOfOutput() const;
90 void writeComplete(const Comm::ConnectionPointer
&conn
, char *bufnotused
, size_t size
, comm_err_t errflag
);
91 void keepaliveNextRequest();
93 Comm::ConnectionPointer clientConnection
; /// details about the client connection socket.
94 ClientHttpRequest
*http
; /* we own this */
96 char reqbuf
[HTTP_REQBUF_SZ
];
101 unsigned deferred
:1; /* This is a pipelined request waiting for the current object to complete */
103 unsigned parsed_ok
:1; /* Was this parsed correctly? */
105 bool mayUseConnection() const {return mayUseConnection_
;}
107 void mayUseConnection(bool aBool
) {
108 mayUseConnection_
= aBool
;
109 debugs(33,3, HERE
<< "This " << this << " marked " << aBool
);
116 clientStreamNode
*node
;
118 StoreIOBuffer queuedBuffer
;
121 DeferredParams deferredparams
;
122 int64_t writtenToSocket
;
124 int64_t getNextRangeOffset() const;
125 bool canPackMoreRanges() const;
126 clientStream_status_t
socketState();
127 void sendBody(HttpReply
* rep
, StoreIOBuffer bodyData
);
128 void sendStartOfMessage(HttpReply
* rep
, StoreIOBuffer bodyData
);
129 size_t lengthToSend(Range
<int64_t> const &available
);
130 void noteSentBodyBytes(size_t);
131 void buildRangeHeader(HttpReply
* rep
);
132 clientStreamNode
* getTail() const;
133 clientStreamNode
* getClientReplyContext() const;
134 ConnStateData
*getConn() const;
135 void connIsFinished();
136 void removeFromConnectionList(ConnStateData
* conn
);
137 void deferRecipientForLater(clientStreamNode
* node
, HttpReply
* rep
, StoreIOBuffer receivedData
);
138 bool multipartRangeRequest() const;
139 void registerWithConn();
140 void noteIoError(const int xerrno
); ///< update state to reflect I/O error
142 /// starts writing 1xx control message to the client
143 void writeControlMsg(HttpControlMsg
&msg
);
146 static IOCB WroteControlMsg
;
147 void wroteControlMsg(const Comm::ConnectionPointer
&conn
, char *bufnotused
, size_t size
, comm_err_t errflag
, int xerrno
);
150 void prepareReply(HttpReply
* rep
);
151 void packChunk(const StoreIOBuffer
&bodyData
, MemBuf
&mb
);
152 void packRange(StoreIOBuffer
const &, MemBuf
* mb
);
153 void deRegisterWithConn();
155 void initiateClose(const char *reason
);
157 AsyncCall::Pointer cbControlMsgSent
; ///< notifies HttpControlMsg Source
159 bool mayUseConnection_
; /* This request may use the connection. Don't read anymore requests for now */
160 bool connRegistered_
;
162 CBDATA_CLASS2(ClientSocketContext
);
165 class ConnectionDetail
;
173 * Manages a connection to a client.
175 * Multiple requests (up to pipeline_prefetch) can be pipelined. This object is responsible for managing
176 * which one is currently being fulfilled and what happens to the queue if the current one
177 * causes the client connection to be closed early.
179 * Act as a manager for the connection and passes data in buffer to the current parser.
180 * the parser has ambiguous scope at present due to being made from global functions
181 * I believe this object uses the parser to identify boundaries and kick off the
182 * actual HTTP request handling objects (ClientSocketContext, ClientHttpRequest, HttpRequest)
184 * If the above can be confirmed accurate we can call this object PipelineManager or similar
186 class ConnStateData
: public BodyProducer
, public HttpControlMsgSink
190 explicit ConnStateData(const MasterXaction::Pointer
&xact
);
194 void readSomeFtpData();
195 bool areAllContextsForThisConnection() const;
196 void freeAllContexts();
197 void notifyAllContexts(const int xerrno
); ///< tell everybody about the err
199 bool clientParseRequests();
200 void readNextRequest();
201 ClientSocketContext::Pointer
getCurrentContext() const;
202 void addContextToQueue(ClientSocketContext
* context
);
203 int getConcurrentRequestCount() const;
205 void checkHeaderLimits();
207 // HttpControlMsgSink API
208 virtual void sendControlMsg(HttpControlMsg msg
);
210 // Client TCP connection details from comm layer.
211 Comm::ConnectionPointer clientConnection
;
216 bool maybeMakeSpaceAvailable();
218 ChunkedCodingParser
*bodyParser
; ///< parses chunked request body
222 /** number of body bytes we need to comm_read for the "current" request
224 * \retval 0 We do not need to read any [more] body bytes
225 * \retval negative May need more but do not know how many; could be zero!
226 * \retval positive Need to read exactly that many more body bytes
228 int64_t mayNeedToReadMoreBody() const;
232 * Fetch the user details for connection based authentication
233 * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
235 const Auth::UserRequest::Pointer
&getAuth() const { return auth_
; }
238 * Set the user details for connection-based authentication to use from now until connection closure.
240 * Any change to existing credentials shows that something invalid has happened. Such as:
241 * - NTLM/Negotiate auth was violated by the per-request headers missing a revalidation token
242 * - NTLM/Negotiate auth was violated by the per-request headers being for another user
243 * - SSL-Bump CONNECT tunnel with persistent credentials has ended
245 void setAuth(const Auth::UserRequest::Pointer
&aur
, const char *cause
);
249 * used by the owner of the connection, opaque otherwise
250 * TODO: generalise the connection owner concept.
252 ClientSocketContext::Pointer currentobject
;
254 Ip::Address log_addr
;
258 bool readMore
; ///< needs comm_read (for this request or new requests)
259 bool swanSang
; // XXX: temporary flag to check proper cleanup
262 Comm::ConnectionPointer serverConnection
; /* pinned server side connection */
263 char *host
; /* host name of pinned connection */
264 int port
; /* port of pinned connection */
265 bool pinned
; /* this connection was pinned */
266 bool auth
; /* pinned for www authentication */
267 bool reading
; ///< we are monitoring for server connection closure
268 bool zeroReply
; ///< server closed w/o response (ERR_ZERO_SIZE_OBJECT)
269 CachePeer
*peer
; /* CachePeer the connection goes via */
270 AsyncCall::Pointer readHandler
; ///< detects serverConnection closure
271 AsyncCall::Pointer closeHandler
; /*The close handler for pinned server side connection*/
274 /// Squid listening port details where this connection arrived.
277 bool transparent() const;
278 bool reading() const;
279 void stopReading(); ///< cancels comm_read if it is scheduled
281 /// true if we stopped receiving the request
282 const char *stoppedReceiving() const { return stoppedReceiving_
; }
283 /// true if we stopped sending the response
284 const char *stoppedSending() const { return stoppedSending_
; }
285 /// note request receiving error and close as soon as we write the response
286 void stopReceiving(const char *error
);
287 /// note response sending error and close as soon as we read the request
288 void stopSending(const char *error
);
290 void expectNoForwarding(); ///< cleans up virgin request [body] forwarding state
292 BodyPipe::Pointer
expectRequestBody(int64_t size
);
293 virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer
);
294 virtual void noteBodyConsumerAborted(BodyPipe::Pointer
);
296 bool handleReadData(SBuf
*buf
);
297 bool handleRequestBodyData();
299 /// forward future client requests using the given server connection
300 /// optionally, monitor pinned server connection for server-side closures
301 void pinConnection(const Comm::ConnectionPointer
&pinServerConn
, HttpRequest
*request
, CachePeer
*peer
, bool auth
, bool monitor
= true);
302 /// undo pinConnection() and, optionally, close the pinned connection
303 void unpinConnection(const bool andClose
);
304 /// returns validated pinnned server connection (and stops its monitoring)
305 Comm::ConnectionPointer
borrowPinnedConnection(HttpRequest
*request
, const CachePeer
*aPeer
);
307 * Checks if there is pinning info if it is valid. It can close the server side connection
308 * if pinned info is not valid.
309 \param request if it is not NULL also checks if the pinning info refers to the request client side HttpRequest
310 \param CachePeer if it is not NULL also check if the CachePeer is the pinning CachePeer
311 \return The details of the server side connection (may be closed if failures were present).
313 const Comm::ConnectionPointer
validatePinnedConnection(HttpRequest
*request
, const CachePeer
*peer
);
315 * returts the pinned CachePeer if exists, NULL otherwise
317 CachePeer
*pinnedPeer() const {return pinning
.peer
;}
318 bool pinnedAuth() const {return pinning
.auth
;}
320 // pining related comm callbacks
321 void clientPinnedConnectionClosed(const CommCloseCbParams
&io
);
324 void clientReadRequest(const CommIoCbParams
&io
);
325 void clientReadFtpData(const CommIoCbParams
&io
);
326 void connStateClosed(const CommCloseCbParams
&io
);
327 void requestTimeout(const CommTimeoutCbParams
¶ms
);
330 virtual bool doneAll() const { return BodyProducer::doneAll() && false;}
331 virtual void swanSong();
333 /// Changes state so that we close the connection and quit after serving
334 /// the client-side-detected error response instead of getting stuck.
335 void quitAfterError(HttpRequest
*request
); // meant to be private
337 /// The caller assumes responsibility for connection closure detection.
338 void stopPinnedConnectionMonitoring();
347 FTP_HANDLE_DATA_REQUEST
,
348 FTP_HANDLE_UPLOAD_REQUEST
,
362 bool gotEpsvAll
; ///< restrict data conn setup commands to just EPSV
363 AsyncCall::Pointer onDataAcceptCall
; ///< who to call upon data connection acceptance
364 Comm::ConnectionPointer dataListenConn
;
365 Comm::ConnectionPointer dataConn
;
366 Ip::Address serverDataAddr
;
367 char uploadBuf
[CLIENT_REQ_BUF_SZ
];
368 size_t uploadAvailSize
;
369 AsyncCall::Pointer listener
; ///< set when we are passively listening
370 AsyncCall::Pointer connector
; ///< set when we are actively connecting
371 AsyncCall::Pointer reader
; ///< set when we are reading FTP data
373 const char *ftpBuildUri(const char *file
= NULL
);
374 void ftpSetWorkingDir(const char *dir
);
377 /// called by FwdState when it is done bumping the server
378 void httpsPeeked(Comm::ConnectionPointer serverConnection
);
380 /// Start to create dynamic SSL_CTX for host or uses static port SSL context.
381 void getSslContextStart();
383 * Done create dynamic ssl certificate.
385 * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
387 void getSslContextDone(SSL_CTX
* sslContext
, bool isNew
= false);
388 /// Callback function. It is called when squid receive message from ssl_crtd.
389 static void sslCrtdHandleReplyWrapper(void *data
, const HelperReply
&reply
);
390 /// Proccess response from ssl_crtd.
391 void sslCrtdHandleReply(const HelperReply
&reply
);
393 void switchToHttps(HttpRequest
*request
, Ssl::BumpMode bumpServerMode
);
394 bool switchedToHttps() const { return switchedToHttps_
; }
395 Ssl::ServerBump
*serverBump() {return sslServerBump
;}
396 inline void setServerBump(Ssl::ServerBump
*srvBump
) {
398 sslServerBump
= srvBump
;
400 assert(sslServerBump
== srvBump
);
402 /// Fill the certAdaptParams with the required data for certificate adaptation
403 /// and create the key for storing/retrieve the certificate to/from the cache
404 void buildSslCertGenerationParams(Ssl::CertificateProperties
&certProperties
);
405 /// Called when the client sends the first request on a bumped connection.
406 /// Returns false if no [delayed] error should be written to the client.
407 /// Otherwise, writes the error to the client and returns true. Also checks
408 /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
409 bool serveDelayedError(ClientSocketContext
*context
);
411 Ssl::BumpMode sslBumpMode
; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
414 bool switchedToHttps() const { return false; }
417 void finishDechunkingRequest(bool withSuccess
);
419 void resumeFtpRequest(ClientSocketContext
*const context
);
422 void startDechunkingRequest();
423 void abortChunkedRequestBody(const err_type error
);
424 err_type
handleChunkedRequestBody(size_t &putSize
);
426 void startPinnedConnectionMonitoring();
427 void clientPinnedConnectionRead(const CommIoCbParams
&io
);
430 int connReadWasError(comm_err_t flag
, int size
, int xerrno
);
431 int connFinishedWithConn(int size
);
432 void clientAfterReadingRequests();
433 void processFtpRequest(ClientSocketContext
*const context
);
434 void handleFtpRequestData();
436 bool concurrentRequestQueueFilled() const;
438 void pinNewConnection(const Comm::ConnectionPointer
&pinServer
, HttpRequest
*request
, CachePeer
*aPeer
, bool auth
);
441 /// some user details that can be used to perform authentication on this connection
442 Auth::UserRequest::Pointer auth_
;
447 // XXX: CBDATA plays with public/private and leaves the following 'private' fields all public... :(
450 bool switchedToHttps_
;
451 /// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
452 String sslConnectHostOrIp
; ///< The SSL server host name as passed in the CONNECT request
453 String sslCommonName
; ///< CN name for SSL certificate generation
454 String sslBumpCertKey
; ///< Key to use to store/retrieve generated certificate
456 /// HTTPS server cert. fetching state for bump-ssl-server-first
457 Ssl::ServerBump
*sslServerBump
;
458 Ssl::CertSignAlgorithm signAlgorithm
; ///< The signing algorithm to use
461 /// the reason why we no longer write the response or nil
462 const char *stoppedSending_
;
463 /// the reason why we no longer read the request or nil
464 const char *stoppedReceiving_
;
466 AsyncCall::Pointer reader
; ///< set when we are reading
467 BodyPipe::Pointer bodyPipe
; // set when we are reading request body
469 CBDATA_CLASS2(ConnStateData
);
472 void setLogUri(ClientHttpRequest
* http
, char const *uri
, bool cleanUrl
= false);
474 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion
, const char *end
= NULL
);
476 int varyEvaluateMatch(StoreEntry
* entry
, HttpRequest
* req
);
478 void clientOpenListenSockets(void);
479 void clientConnectionsClose(void);
480 void httpRequestFree(void *);
482 #endif /* SQUID_CLIENTSIDE_H */