2 * Copyright (C) 1996-2015 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 /* DEBUG: section 33 Client-side Routines */
11 #ifndef SQUID_CLIENTSIDE_H
12 #define SQUID_CLIENTSIDE_H
14 #include "base/RunnersRegistry.h"
15 #include "clientStreamForward.h"
17 #include "helper/forward.h"
18 #include "http/forward.h"
19 #include "HttpControlMsg.h"
20 #include "ipc/FdNotes.h"
22 #include "servers/Server.h"
24 #include "auth/UserRequest.h"
27 #include "ssl/support.h"
31 class ClientHttpRequest
;
32 class clientStreamNode
;
40 * This is in fact the processing context for a single HTTP request.
42 * Managing what has been done, and what happens next to the data buffer
43 * holding what we hope is an HTTP request.
45 * Parsing is still a mess of global functions done in conjunction with the
46 * real socket controller which generated ClientHttpRequest.
47 * It also generates one of us and passes us control from there based on
48 * the results of the parse.
50 * After that all the request interpretation and adaptation is in our scope.
51 * Then finally the reply fetcher is created by this and we get the result
52 * back. Which we then have to manage writing of it to the ConnStateData.
54 * The socket level management is done by a ConnStateData which owns us.
55 * The scope of this objects control over a socket consists of the data
56 * buffer received from ConnStateData with an initially unknown length.
57 * When that length is known it sets the end bounary of our acces to the
60 * The individual processing actions are done by other Jobs which we
63 * XXX: If an async call ends the ClientHttpRequest job, ClientSocketContext
64 * (and ConnStateData) may not know about it, leading to segfaults and
65 * assertions like areAllContextsForThisConnection(). This is difficult to fix
66 * because ClientHttpRequest lacks a good way to communicate its ongoing
67 * destruction back to the ClientSocketContext which pretends to "own" *http.
69 class ClientSocketContext
: public RefCountable
71 CBDATA_CLASS(ClientSocketContext
);
74 typedef RefCount
<ClientSocketContext
> Pointer
;
75 ClientSocketContext(const Comm::ConnectionPointer
&aConn
, ClientHttpRequest
*aReq
);
76 ~ClientSocketContext();
77 bool startOfOutput() const;
78 void writeComplete(const Comm::ConnectionPointer
&conn
, char *bufnotused
, size_t size
, Comm::Flag errflag
);
79 void keepaliveNextRequest();
81 Comm::ConnectionPointer clientConnection
; /// details about the client connection socket.
82 ClientHttpRequest
*http
; /* we pretend to own that job */
84 char reqbuf
[HTTP_REQBUF_SZ
];
89 unsigned deferred
:1; /* This is a pipelined request waiting for the current object to complete */
91 unsigned parsed_ok
:1; /* Was this parsed correctly? */
93 bool mayUseConnection() const {return mayUseConnection_
;}
95 void mayUseConnection(bool aBool
) {
96 mayUseConnection_
= aBool
;
97 debugs(33,3, HERE
<< "This " << this << " marked " << aBool
);
104 clientStreamNode
*node
;
106 StoreIOBuffer queuedBuffer
;
109 DeferredParams deferredparams
;
110 int64_t writtenToSocket
;
112 int64_t getNextRangeOffset() const;
113 bool canPackMoreRanges() const;
114 clientStream_status_t
socketState();
115 void sendBody(HttpReply
* rep
, StoreIOBuffer bodyData
);
116 void sendStartOfMessage(HttpReply
* rep
, StoreIOBuffer bodyData
);
117 size_t lengthToSend(Range
<int64_t> const &available
);
118 void noteSentBodyBytes(size_t);
119 void buildRangeHeader(HttpReply
* rep
);
120 clientStreamNode
* getTail() const;
121 clientStreamNode
* getClientReplyContext() const;
122 ConnStateData
*getConn() const;
123 void connIsFinished();
124 void removeFromConnectionList(ConnStateData
* conn
);
125 void deferRecipientForLater(clientStreamNode
* node
, HttpReply
* rep
, StoreIOBuffer receivedData
);
126 bool multipartRangeRequest() const;
127 void registerWithConn();
128 void noteIoError(const int xerrno
); ///< update state to reflect I/O error
130 /// starts writing 1xx control message to the client
131 void writeControlMsg(HttpControlMsg
&msg
);
134 static IOCB WroteControlMsg
;
135 void wroteControlMsg(const Comm::ConnectionPointer
&conn
, char *bufnotused
, size_t size
, Comm::Flag errflag
, int xerrno
);
138 void prepareReply(HttpReply
* rep
);
139 void packChunk(const StoreIOBuffer
&bodyData
, MemBuf
&mb
);
140 void packRange(StoreIOBuffer
const &, MemBuf
* mb
);
141 void deRegisterWithConn();
143 void initiateClose(const char *reason
);
145 AsyncCall::Pointer cbControlMsgSent
; ///< notifies HttpControlMsg Source
147 bool mayUseConnection_
; /* This request may use the connection. Don't read anymore requests for now */
148 bool connRegistered_
;
151 class ConnectionDetail
;
159 * Manages a connection to a client.
161 * Multiple requests (up to pipeline_prefetch) can be pipelined. This object is responsible for managing
162 * which one is currently being fulfilled and what happens to the queue if the current one
163 * causes the client connection to be closed early.
165 * Act as a manager for the connection and passes data in buffer to the current parser.
166 * the parser has ambiguous scope at present due to being made from global functions
167 * I believe this object uses the parser to identify boundaries and kick off the
168 * actual HTTP request handling objects (ClientSocketContext, ClientHttpRequest, HttpRequest)
170 * If the above can be confirmed accurate we can call this object PipelineManager or similar
172 class ConnStateData
: public Server
, public HttpControlMsgSink
, public RegisteredRunner
176 explicit ConnStateData(const MasterXaction::Pointer
&xact
);
177 virtual ~ConnStateData();
180 virtual void notifyAllContexts(const int xerrno
);
181 virtual void receivedFirstByte();
182 virtual bool handleReadData();
183 virtual void afterClientRead();
185 bool areAllContextsForThisConnection() const;
186 void freeAllContexts();
188 bool clientParseRequests();
189 void readNextRequest();
190 ClientSocketContext::Pointer
getCurrentContext() const;
191 void addContextToQueue(ClientSocketContext
* context
);
192 int getConcurrentRequestCount() const;
195 // HttpControlMsgSink API
196 virtual void sendControlMsg(HttpControlMsg msg
);
198 Http1::TeChunkedParser
*bodyParser
; ///< parses HTTP/1.1 chunked request body
200 /** number of body bytes we need to comm_read for the "current" request
202 * \retval 0 We do not need to read any [more] body bytes
203 * \retval negative May need more but do not know how many; could be zero!
204 * \retval positive Need to read exactly that many more body bytes
206 int64_t mayNeedToReadMoreBody() const;
210 * Fetch the user details for connection based authentication
211 * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
213 const Auth::UserRequest::Pointer
&getAuth() const { return auth_
; }
216 * Set the user details for connection-based authentication to use from now until connection closure.
218 * Any change to existing credentials shows that something invalid has happened. Such as:
219 * - NTLM/Negotiate auth was violated by the per-request headers missing a revalidation token
220 * - NTLM/Negotiate auth was violated by the per-request headers being for another user
221 * - SSL-Bump CONNECT tunnel with persistent credentials has ended
223 void setAuth(const Auth::UserRequest::Pointer
&aur
, const char *cause
);
227 * used by the owner of the connection, opaque otherwise
228 * TODO: generalise the connection owner concept.
230 ClientSocketContext::Pointer currentobject
;
232 Ip::Address log_addr
;
236 bool readMore
; ///< needs comm_read (for this request or new requests)
237 bool swanSang
; // XXX: temporary flag to check proper cleanup
240 Comm::ConnectionPointer serverConnection
; /* pinned server side connection */
241 char *host
; /* host name of pinned connection */
242 int port
; /* port of pinned connection */
243 bool pinned
; /* this connection was pinned */
244 bool auth
; /* pinned for www authentication */
245 bool reading
; ///< we are monitoring for peer connection closure
246 bool zeroReply
; ///< server closed w/o response (ERR_ZERO_SIZE_OBJECT)
247 CachePeer
*peer
; /* CachePeer the connection goes via */
248 AsyncCall::Pointer readHandler
; ///< detects serverConnection closure
249 AsyncCall::Pointer closeHandler
; /*The close handler for pinned server side connection*/
252 bool transparent() const;
254 /// true if we stopped receiving the request
255 const char *stoppedReceiving() const { return stoppedReceiving_
; }
256 /// true if we stopped sending the response
257 const char *stoppedSending() const { return stoppedSending_
; }
258 /// note request receiving error and close as soon as we write the response
259 void stopReceiving(const char *error
);
260 /// note response sending error and close as soon as we read the request
261 void stopSending(const char *error
);
263 void expectNoForwarding(); ///< cleans up virgin request [body] forwarding state
266 BodyPipe::Pointer
expectRequestBody(int64_t size
);
267 virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer
) = 0;
268 virtual void noteBodyConsumerAborted(BodyPipe::Pointer
) = 0;
270 bool handleRequestBodyData();
272 /// Forward future client requests using the given server connection.
273 /// Optionally, monitor pinned server connection for remote-end closures.
274 void pinConnection(const Comm::ConnectionPointer
&pinServerConn
, HttpRequest
*request
, CachePeer
*peer
, bool auth
, bool monitor
= true);
275 /// Undo pinConnection() and, optionally, close the pinned connection.
276 void unpinConnection(const bool andClose
);
277 /// Returns validated pinnned server connection (and stops its monitoring).
278 Comm::ConnectionPointer
borrowPinnedConnection(HttpRequest
*request
, const CachePeer
*aPeer
);
280 * Checks if there is pinning info if it is valid. It can close the server side connection
281 * if pinned info is not valid.
282 \param request if it is not NULL also checks if the pinning info refers to the request client side HttpRequest
283 \param CachePeer if it is not NULL also check if the CachePeer is the pinning CachePeer
284 \return The details of the server side connection (may be closed if failures were present).
286 const Comm::ConnectionPointer
validatePinnedConnection(HttpRequest
*request
, const CachePeer
*peer
);
288 * returts the pinned CachePeer if exists, NULL otherwise
290 CachePeer
*pinnedPeer() const {return pinning
.peer
;}
291 bool pinnedAuth() const {return pinning
.auth
;}
293 /// called just before a FwdState-dispatched job starts using connection
294 virtual void notePeerConnection(Comm::ConnectionPointer
) {}
296 // pining related comm callbacks
297 virtual void clientPinnedConnectionClosed(const CommCloseCbParams
&io
);
300 void clientReadFtpData(const CommIoCbParams
&io
);
301 void connStateClosed(const CommCloseCbParams
&io
);
302 void requestTimeout(const CommTimeoutCbParams
¶ms
);
305 virtual void start();
306 virtual bool doneAll() const { return BodyProducer::doneAll() && false;}
307 virtual void swanSong();
309 /// Changes state so that we close the connection and quit after serving
310 /// the client-side-detected error response instead of getting stuck.
311 void quitAfterError(HttpRequest
*request
); // meant to be private
313 /// The caller assumes responsibility for connection closure detection.
314 void stopPinnedConnectionMonitoring();
317 /// the second part of old httpsAccept, waiting for future HttpsServer home
318 void postHttpsAccept();
320 /// Initializes and starts a peek-and-splice negotiation with the SSL client
321 void startPeekAndSplice();
322 /// Called when the initialization of peek-and-splice negotiation finidhed
323 void startPeekAndSpliceDone();
324 /// Called when a peek-and-splice step finished. For example after
325 /// server SSL certificates received and fake server SSL certificates
327 void doPeekAndSpliceStep();
328 /// called by FwdState when it is done bumping the server
329 void httpsPeeked(Comm::ConnectionPointer serverConnection
);
331 /// Splice a bumped client connection on peek-and-splice mode
334 /// Check on_unsupported_protocol access list and splice if required
335 /// \retval true on splice
336 /// \retval false otherwise
337 bool spliceOnError(const err_type err
);
339 /// Start to create dynamic SSL_CTX for host or uses static port SSL context.
340 void getSslContextStart();
342 * Done create dynamic ssl certificate.
344 * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
346 void getSslContextDone(Security::ContextPointer sslContext
, bool isNew
= false);
347 /// Callback function. It is called when squid receive message from ssl_crtd.
348 static void sslCrtdHandleReplyWrapper(void *data
, const Helper::Reply
&reply
);
349 /// Proccess response from ssl_crtd.
350 void sslCrtdHandleReply(const Helper::Reply
&reply
);
352 void switchToHttps(HttpRequest
*request
, Ssl::BumpMode bumpServerMode
);
353 bool switchedToHttps() const { return switchedToHttps_
; }
354 Ssl::ServerBump
*serverBump() {return sslServerBump
;}
355 inline void setServerBump(Ssl::ServerBump
*srvBump
) {
357 sslServerBump
= srvBump
;
359 assert(sslServerBump
== srvBump
);
361 const SBuf
&sslCommonName() const {return sslCommonName_
;}
362 void resetSslCommonName(const char *name
) {sslCommonName_
= name
;}
363 /// Fill the certAdaptParams with the required data for certificate adaptation
364 /// and create the key for storing/retrieve the certificate to/from the cache
365 void buildSslCertGenerationParams(Ssl::CertificateProperties
&certProperties
);
366 /// Called when the client sends the first request on a bumped connection.
367 /// Returns false if no [delayed] error should be written to the client.
368 /// Otherwise, writes the error to the client and returns true. Also checks
369 /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
370 bool serveDelayedError(ClientSocketContext
*context
);
372 Ssl::BumpMode sslBumpMode
; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
375 bool switchedToHttps() const { return false; }
378 /* clt_conn_tag=tag annotation access */
379 const SBuf
&connectionTag() const { return connectionTag_
; }
380 void connectionTag(const char *aTag
) { connectionTag_
= aTag
; }
382 /// handle a control message received by context from a peer and call back
383 virtual void writeControlMsgAndCall(ClientSocketContext
*context
, HttpReply
*rep
, AsyncCall::Pointer
&call
) = 0;
385 /// ClientStream calls this to supply response header (once) and data
386 /// for the current ClientSocketContext.
387 virtual void handleReply(HttpReply
*header
, StoreIOBuffer receivedData
) = 0;
389 /// remove no longer needed leading bytes from the input buffer
390 void consumeInput(const size_t byteCount
);
392 /* TODO: Make the methods below (at least) non-public when possible. */
394 /// stop parsing the request and create context for relaying error info
395 ClientSocketContext
*abortRequestParsing(const char *const errUri
);
397 /// generate a fake CONNECT request with the given payload
398 /// at the beginning of the client I/O buffer
399 void fakeAConnectRequest(const char *reason
, const SBuf
&payload
);
401 /// client data which may need to forward as-is to server after an
402 /// on_unsupported_protocol tunnel decision.
403 SBuf preservedClientData
;
405 /* Registered Runner API */
406 virtual void startShutdown();
407 virtual void endingShutdown();
410 void startDechunkingRequest();
411 void finishDechunkingRequest(bool withSuccess
);
412 void abortChunkedRequestBody(const err_type error
);
413 err_type
handleChunkedRequestBody();
415 void startPinnedConnectionMonitoring();
416 void clientPinnedConnectionRead(const CommIoCbParams
&io
);
418 /// Handles a ready-for-reading TLS squid-to-server connection that
419 /// we thought was idle.
420 /// \return false if and only if the connection should be closed.
421 bool handleIdleClientPinnedTlsRead();
424 /// parse input buffer prefix into a single transfer protocol request
425 /// return NULL to request more header bytes (after checking any limits)
426 /// use abortRequestParsing() to handle parsing errors w/o creating request
427 virtual ClientSocketContext
*parseOneRequest() = 0;
429 /// start processing a freshly parsed request
430 virtual void processParsedRequest(ClientSocketContext
*context
) = 0;
432 /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
433 virtual int pipelinePrefetchMax() const;
435 /// timeout to use when waiting for the next request
436 virtual time_t idleTimeout() const = 0;
438 BodyPipe::Pointer bodyPipe
; ///< set when we are reading request body
442 virtual bool connFinishedWithConn(int size
);
444 void clientAfterReadingRequests();
445 bool concurrentRequestQueueFilled() const;
447 void pinNewConnection(const Comm::ConnectionPointer
&pinServer
, HttpRequest
*request
, CachePeer
*aPeer
, bool auth
);
449 /* PROXY protocol functionality */
450 bool proxyProtocolValidateClient();
451 bool parseProxyProtocolHeader();
452 bool parseProxy1p0();
453 bool parseProxy2p0();
454 bool proxyProtocolError(const char *reason
);
456 /// whether PROXY protocol header is still expected
457 bool needProxyProtocolHeader_
;
460 /// some user details that can be used to perform authentication on this connection
461 Auth::UserRequest::Pointer auth_
;
464 /// the parser state for current HTTP/1.x input buffer processing
465 Http1::RequestParserPointer parser_
;
468 bool switchedToHttps_
;
469 /// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
470 String sslConnectHostOrIp
; ///< The SSL server host name as passed in the CONNECT request
471 SBuf sslCommonName_
; ///< CN name for SSL certificate generation
472 String sslBumpCertKey
; ///< Key to use to store/retrieve generated certificate
474 /// HTTPS server cert. fetching state for bump-ssl-server-first
475 Ssl::ServerBump
*sslServerBump
;
476 Ssl::CertSignAlgorithm signAlgorithm
; ///< The signing algorithm to use
479 /// the reason why we no longer write the response or nil
480 const char *stoppedSending_
;
481 /// the reason why we no longer read the request or nil
482 const char *stoppedReceiving_
;
484 SBuf connectionTag_
; ///< clt_conn_tag=Tag annotation for client connection
487 void setLogUri(ClientHttpRequest
* http
, char const *uri
, bool cleanUrl
= false);
489 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion
, const char *end
= NULL
);
491 int varyEvaluateMatch(StoreEntry
* entry
, HttpRequest
* req
);
493 /// accept requests to a given port and inform subCall about them
494 void clientStartListeningOn(AnyP::PortCfgPointer
&port
, const RefCount
< CommCbFunPtrCallT
<CommAcceptCbPtrFun
> > &subCall
, const Ipc::FdNoteId noteId
);
496 void clientOpenListenSockets(void);
497 void clientConnectionsClose(void);
498 void httpRequestFree(void *);
500 /// decide whether to expect multiple requests on the corresponding connection
501 void clientSetKeepaliveFlag(ClientHttpRequest
*http
);
503 /* misplaced declaratrions of Stream callbacks provided/used by client side */
504 SQUIDCEXTERN CSR clientGetMoreData
;
505 SQUIDCEXTERN CSS clientReplyStatus
;
506 SQUIDCEXTERN CSD clientReplyDetach
;
507 CSCB clientSocketRecipient
;
508 CSD clientSocketDetach
;
510 /* TODO: Move to HttpServer. Warning: Move requires large code nonchanges! */
511 ClientSocketContext
*parseHttpRequest(ConnStateData
*, const Http1::RequestParserPointer
&);
512 void clientProcessRequest(ConnStateData
*, const Http1::RequestParserPointer
&, ClientSocketContext
*);
513 void clientPostHttpsAccept(ConnStateData
*);
515 #endif /* SQUID_CLIENTSIDE_H */