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 "clientStreamForward.h"
16 #include "helper/forward.h"
17 #include "http/forward.h"
18 #include "HttpControlMsg.h"
19 #include "ipc/FdNotes.h"
22 #include "auth/UserRequest.h"
25 #include "ssl/support.h"
29 class ClientHttpRequest
;
30 class clientStreamNode
;
38 * This is in fact the processing context for a single HTTP request.
40 * Managing what has been done, and what happens next to the data buffer
41 * holding what we hope is an HTTP request.
43 * Parsing is still a mess of global functions done in conjunction with the
44 * real socket controller which generated ClientHttpRequest.
45 * It also generates one of us and passes us control from there based on
46 * the results of the parse.
48 * After that all the request interpretation and adaptation is in our scope.
49 * Then finally the reply fetcher is created by this and we get the result
50 * back. Which we then have to manage writing of it to the ConnStateData.
52 * The socket level management is done by a ConnStateData which owns us.
53 * The scope of this objects control over a socket consists of the data
54 * buffer received from ConnStateData with an initially unknown length.
55 * When that length is known it sets the end bounary of our acces to the
58 * The individual processing actions are done by other Jobs which we
61 * XXX: If an async call ends the ClientHttpRequest job, ClientSocketContext
62 * (and ConnStateData) may not know about it, leading to segfaults and
63 * assertions like areAllContextsForThisConnection(). This is difficult to fix
64 * because ClientHttpRequest lacks a good way to communicate its ongoing
65 * destruction back to the ClientSocketContext which pretends to "own" *http.
67 class ClientSocketContext
: public RefCountable
69 CBDATA_CLASS(ClientSocketContext
);
72 typedef RefCount
<ClientSocketContext
> Pointer
;
73 ClientSocketContext(const Comm::ConnectionPointer
&aConn
, ClientHttpRequest
*aReq
);
74 ~ClientSocketContext();
75 bool startOfOutput() const;
76 void writeComplete(const Comm::ConnectionPointer
&conn
, char *bufnotused
, size_t size
, Comm::Flag errflag
);
77 void keepaliveNextRequest();
79 Comm::ConnectionPointer clientConnection
; /// details about the client connection socket.
80 ClientHttpRequest
*http
; /* we pretend to own that job */
82 char reqbuf
[HTTP_REQBUF_SZ
];
87 unsigned deferred
:1; /* This is a pipelined request waiting for the current object to complete */
89 unsigned parsed_ok
:1; /* Was this parsed correctly? */
91 bool mayUseConnection() const {return mayUseConnection_
;}
93 void mayUseConnection(bool aBool
) {
94 mayUseConnection_
= aBool
;
95 debugs(33,3, HERE
<< "This " << this << " marked " << aBool
);
102 clientStreamNode
*node
;
104 StoreIOBuffer queuedBuffer
;
107 DeferredParams deferredparams
;
108 int64_t writtenToSocket
;
110 int64_t getNextRangeOffset() const;
111 bool canPackMoreRanges() const;
112 clientStream_status_t
socketState();
113 void sendBody(HttpReply
* rep
, StoreIOBuffer bodyData
);
114 void sendStartOfMessage(HttpReply
* rep
, StoreIOBuffer bodyData
);
115 size_t lengthToSend(Range
<int64_t> const &available
);
116 void noteSentBodyBytes(size_t);
117 void buildRangeHeader(HttpReply
* rep
);
118 clientStreamNode
* getTail() const;
119 clientStreamNode
* getClientReplyContext() const;
120 ConnStateData
*getConn() const;
121 void connIsFinished();
122 void removeFromConnectionList(ConnStateData
* conn
);
123 void deferRecipientForLater(clientStreamNode
* node
, HttpReply
* rep
, StoreIOBuffer receivedData
);
124 bool multipartRangeRequest() const;
125 void registerWithConn();
126 void noteIoError(const int xerrno
); ///< update state to reflect I/O error
128 /// starts writing 1xx control message to the client
129 void writeControlMsg(HttpControlMsg
&msg
);
132 static IOCB WroteControlMsg
;
133 void wroteControlMsg(const Comm::ConnectionPointer
&conn
, char *bufnotused
, size_t size
, Comm::Flag errflag
, int xerrno
);
136 void prepareReply(HttpReply
* rep
);
137 void packChunk(const StoreIOBuffer
&bodyData
, MemBuf
&mb
);
138 void packRange(StoreIOBuffer
const &, MemBuf
* mb
);
139 void deRegisterWithConn();
141 void initiateClose(const char *reason
);
143 AsyncCall::Pointer cbControlMsgSent
; ///< notifies HttpControlMsg Source
145 bool mayUseConnection_
; /* This request may use the connection. Don't read anymore requests for now */
146 bool connRegistered_
;
149 class ConnectionDetail
;
157 * Manages a connection to a client.
159 * Multiple requests (up to pipeline_prefetch) can be pipelined. This object is responsible for managing
160 * which one is currently being fulfilled and what happens to the queue if the current one
161 * causes the client connection to be closed early.
163 * Act as a manager for the connection and passes data in buffer to the current parser.
164 * the parser has ambiguous scope at present due to being made from global functions
165 * I believe this object uses the parser to identify boundaries and kick off the
166 * actual HTTP request handling objects (ClientSocketContext, ClientHttpRequest, HttpRequest)
168 * If the above can be confirmed accurate we can call this object PipelineManager or similar
170 class ConnStateData
: public BodyProducer
, public HttpControlMsgSink
174 explicit ConnStateData(const MasterXaction::Pointer
&xact
);
175 virtual ~ConnStateData();
178 bool areAllContextsForThisConnection() const;
179 void freeAllContexts();
180 void notifyAllContexts(const int xerrno
); ///< tell everybody about the err
182 bool clientParseRequests();
183 void readNextRequest();
184 ClientSocketContext::Pointer
getCurrentContext() const;
185 void addContextToQueue(ClientSocketContext
* context
);
186 int getConcurrentRequestCount() const;
189 /// Update flags and timeout after the first byte received
190 void receivedFirstByte();
192 // HttpControlMsgSink API
193 virtual void sendControlMsg(HttpControlMsg msg
);
195 // Client TCP connection details from comm layer.
196 Comm::ConnectionPointer clientConnection
;
199 * The transfer protocol currently being spoken on this connection.
200 * HTTP/1 CONNECT and HTTP/2 SETTINGS offers the ability to change
201 * protocols on the fly.
203 AnyP::ProtocolVersion transferProtocol
;
208 bool maybeMakeSpaceAvailable();
210 Http1::TeChunkedParser
*bodyParser
; ///< parses chunked request body
214 /** number of body bytes we need to comm_read for the "current" request
216 * \retval 0 We do not need to read any [more] body bytes
217 * \retval negative May need more but do not know how many; could be zero!
218 * \retval positive Need to read exactly that many more body bytes
220 int64_t mayNeedToReadMoreBody() const;
224 * Fetch the user details for connection based authentication
225 * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
227 const Auth::UserRequest::Pointer
&getAuth() const { return auth_
; }
230 * Set the user details for connection-based authentication to use from now until connection closure.
232 * Any change to existing credentials shows that something invalid has happened. Such as:
233 * - NTLM/Negotiate auth was violated by the per-request headers missing a revalidation token
234 * - NTLM/Negotiate auth was violated by the per-request headers being for another user
235 * - SSL-Bump CONNECT tunnel with persistent credentials has ended
237 void setAuth(const Auth::UserRequest::Pointer
&aur
, const char *cause
);
241 * used by the owner of the connection, opaque otherwise
242 * TODO: generalise the connection owner concept.
244 ClientSocketContext::Pointer currentobject
;
246 Ip::Address log_addr
;
250 bool readMore
; ///< needs comm_read (for this request or new requests)
251 bool swanSang
; // XXX: temporary flag to check proper cleanup
254 Comm::ConnectionPointer serverConnection
; /* pinned server side connection */
255 char *host
; /* host name of pinned connection */
256 int port
; /* port of pinned connection */
257 bool pinned
; /* this connection was pinned */
258 bool auth
; /* pinned for www authentication */
259 bool reading
; ///< we are monitoring for peer connection closure
260 bool zeroReply
; ///< server closed w/o response (ERR_ZERO_SIZE_OBJECT)
261 CachePeer
*peer
; /* CachePeer the connection goes via */
262 AsyncCall::Pointer readHandler
; ///< detects serverConnection closure
263 AsyncCall::Pointer closeHandler
; /*The close handler for pinned server side connection*/
266 /// Squid listening port details where this connection arrived.
267 AnyP::PortCfgPointer port
;
269 bool transparent() const;
270 bool reading() const;
271 void stopReading(); ///< cancels comm_read if it is scheduled
273 /// true if we stopped receiving the request
274 const char *stoppedReceiving() const { return stoppedReceiving_
; }
275 /// true if we stopped sending the response
276 const char *stoppedSending() const { return stoppedSending_
; }
277 /// note request receiving error and close as soon as we write the response
278 void stopReceiving(const char *error
);
279 /// note response sending error and close as soon as we read the request
280 void stopSending(const char *error
);
282 void expectNoForwarding(); ///< cleans up virgin request [body] forwarding state
285 BodyPipe::Pointer
expectRequestBody(int64_t size
);
286 virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer
) = 0;
287 virtual void noteBodyConsumerAborted(BodyPipe::Pointer
) = 0;
289 bool handleReadData();
290 bool handleRequestBodyData();
292 /// Forward future client requests using the given server connection.
293 /// Optionally, monitor pinned server connection for remote-end closures.
294 void pinConnection(const Comm::ConnectionPointer
&pinServerConn
, HttpRequest
*request
, CachePeer
*peer
, bool auth
, bool monitor
= true);
295 /// Undo pinConnection() and, optionally, close the pinned connection.
296 void unpinConnection(const bool andClose
);
297 /// Returns validated pinnned server connection (and stops its monitoring).
298 Comm::ConnectionPointer
borrowPinnedConnection(HttpRequest
*request
, const CachePeer
*aPeer
);
300 * Checks if there is pinning info if it is valid. It can close the server side connection
301 * if pinned info is not valid.
302 \param request if it is not NULL also checks if the pinning info refers to the request client side HttpRequest
303 \param CachePeer if it is not NULL also check if the CachePeer is the pinning CachePeer
304 \return The details of the server side connection (may be closed if failures were present).
306 const Comm::ConnectionPointer
validatePinnedConnection(HttpRequest
*request
, const CachePeer
*peer
);
308 * returts the pinned CachePeer if exists, NULL otherwise
310 CachePeer
*pinnedPeer() const {return pinning
.peer
;}
311 bool pinnedAuth() const {return pinning
.auth
;}
313 /// called just before a FwdState-dispatched job starts using connection
314 virtual void notePeerConnection(Comm::ConnectionPointer
) {}
316 // pining related comm callbacks
317 virtual void clientPinnedConnectionClosed(const CommCloseCbParams
&io
);
320 void clientReadRequest(const CommIoCbParams
&io
);
321 void clientReadFtpData(const CommIoCbParams
&io
);
322 void connStateClosed(const CommCloseCbParams
&io
);
323 void requestTimeout(const CommTimeoutCbParams
¶ms
);
326 virtual void start();
327 virtual bool doneAll() const { return BodyProducer::doneAll() && false;}
328 virtual void swanSong();
330 /// Changes state so that we close the connection and quit after serving
331 /// the client-side-detected error response instead of getting stuck.
332 void quitAfterError(HttpRequest
*request
); // meant to be private
334 /// The caller assumes responsibility for connection closure detection.
335 void stopPinnedConnectionMonitoring();
338 /// the second part of old httpsAccept, waiting for future HttpsServer home
339 void postHttpsAccept();
341 /// Initializes and starts a peek-and-splice negotiation with the SSL client
342 void startPeekAndSplice();
343 /// Called when the initialization of peek-and-splice negotiation finidhed
344 void startPeekAndSpliceDone();
345 /// Called when a peek-and-splice step finished. For example after
346 /// server SSL certificates received and fake server SSL certificates
348 void doPeekAndSpliceStep();
349 /// called by FwdState when it is done bumping the server
350 void httpsPeeked(Comm::ConnectionPointer serverConnection
);
352 /// Splice a bumped client connection on peek-and-splice mode
355 /// Check on_unsupported_protocol access list and splice if required
356 /// \retval true on splice
357 /// \retval false otherwise
358 bool spliceOnError(const err_type err
);
360 /// Start to create dynamic SSL_CTX for host or uses static port SSL context.
361 void getSslContextStart();
363 * Done create dynamic ssl certificate.
365 * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
367 void getSslContextDone(Security::ContextPointer sslContext
, bool isNew
= false);
368 /// Callback function. It is called when squid receive message from ssl_crtd.
369 static void sslCrtdHandleReplyWrapper(void *data
, const Helper::Reply
&reply
);
370 /// Proccess response from ssl_crtd.
371 void sslCrtdHandleReply(const Helper::Reply
&reply
);
373 void switchToHttps(HttpRequest
*request
, Ssl::BumpMode bumpServerMode
);
374 bool switchedToHttps() const { return switchedToHttps_
; }
375 Ssl::ServerBump
*serverBump() {return sslServerBump
;}
376 inline void setServerBump(Ssl::ServerBump
*srvBump
) {
378 sslServerBump
= srvBump
;
380 assert(sslServerBump
== srvBump
);
382 const SBuf
&sslCommonName() const {return sslCommonName_
;}
383 void resetSslCommonName(const char *name
) {sslCommonName_
= name
;}
384 /// Fill the certAdaptParams with the required data for certificate adaptation
385 /// and create the key for storing/retrieve the certificate to/from the cache
386 void buildSslCertGenerationParams(Ssl::CertificateProperties
&certProperties
);
387 /// Called when the client sends the first request on a bumped connection.
388 /// Returns false if no [delayed] error should be written to the client.
389 /// Otherwise, writes the error to the client and returns true. Also checks
390 /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
391 bool serveDelayedError(ClientSocketContext
*context
);
393 Ssl::BumpMode sslBumpMode
; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
396 bool switchedToHttps() const { return false; }
399 /* clt_conn_tag=tag annotation access */
400 const SBuf
&connectionTag() const { return connectionTag_
; }
401 void connectionTag(const char *aTag
) { connectionTag_
= aTag
; }
403 /// handle a control message received by context from a peer and call back
404 virtual void writeControlMsgAndCall(ClientSocketContext
*context
, HttpReply
*rep
, AsyncCall::Pointer
&call
) = 0;
406 /// ClientStream calls this to supply response header (once) and data
407 /// for the current ClientSocketContext.
408 virtual void handleReply(HttpReply
*header
, StoreIOBuffer receivedData
) = 0;
410 /// remove no longer needed leading bytes from the input buffer
411 void consumeInput(const size_t byteCount
);
413 /* TODO: Make the methods below (at least) non-public when possible. */
415 /// stop parsing the request and create context for relaying error info
416 ClientSocketContext
*abortRequestParsing(const char *const errUri
);
418 /// client data which may need to forward as-is to server after an
419 /// on_unsupported_protocol tunnel decision.
420 SBuf preservedClientData
;
422 void startDechunkingRequest();
423 void finishDechunkingRequest(bool withSuccess
);
424 void abortChunkedRequestBody(const err_type error
);
425 err_type
handleChunkedRequestBody();
427 void startPinnedConnectionMonitoring();
428 void clientPinnedConnectionRead(const CommIoCbParams
&io
);
430 /// parse input buffer prefix into a single transfer protocol request
431 /// return NULL to request more header bytes (after checking any limits)
432 /// use abortRequestParsing() to handle parsing errors w/o creating request
433 virtual ClientSocketContext
*parseOneRequest() = 0;
435 /// start processing a freshly parsed request
436 virtual void processParsedRequest(ClientSocketContext
*context
) = 0;
438 /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
439 virtual int pipelinePrefetchMax() const;
441 /// timeout to use when waiting for the next request
442 virtual time_t idleTimeout() const = 0;
444 BodyPipe::Pointer bodyPipe
; ///< set when we are reading request body
447 int connFinishedWithConn(int size
);
448 void clientAfterReadingRequests();
449 bool concurrentRequestQueueFilled() const;
451 void pinNewConnection(const Comm::ConnectionPointer
&pinServer
, HttpRequest
*request
, CachePeer
*aPeer
, bool auth
);
453 /* PROXY protocol functionality */
454 bool proxyProtocolValidateClient();
455 bool parseProxyProtocolHeader();
456 bool parseProxy1p0();
457 bool parseProxy2p0();
458 bool proxyProtocolError(const char *reason
);
460 /// whether PROXY protocol header is still expected
461 bool needProxyProtocolHeader_
;
464 /// some user details that can be used to perform authentication on this connection
465 Auth::UserRequest::Pointer auth_
;
468 /// the parser state for current HTTP/1.x input buffer processing
469 Http1::RequestParserPointer parser_
;
472 bool switchedToHttps_
;
473 /// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
474 String sslConnectHostOrIp
; ///< The SSL server host name as passed in the CONNECT request
475 SBuf sslCommonName_
; ///< CN name for SSL certificate generation
476 String sslBumpCertKey
; ///< Key to use to store/retrieve generated certificate
478 /// HTTPS server cert. fetching state for bump-ssl-server-first
479 Ssl::ServerBump
*sslServerBump
;
480 Ssl::CertSignAlgorithm signAlgorithm
; ///< The signing algorithm to use
483 /// the reason why we no longer write the response or nil
484 const char *stoppedSending_
;
485 /// the reason why we no longer read the request or nil
486 const char *stoppedReceiving_
;
488 AsyncCall::Pointer reader
; ///< set when we are reading
490 bool receivedFirstByte_
; ///< true if at least one byte received on this connection
491 SBuf connectionTag_
; ///< clt_conn_tag=Tag annotation for client connection
494 void setLogUri(ClientHttpRequest
* http
, char const *uri
, bool cleanUrl
= false);
496 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion
, const char *end
= NULL
);
498 int varyEvaluateMatch(StoreEntry
* entry
, HttpRequest
* req
);
500 /// accept requests to a given port and inform subCall about them
501 void clientStartListeningOn(AnyP::PortCfgPointer
&port
, const RefCount
< CommCbFunPtrCallT
<CommAcceptCbPtrFun
> > &subCall
, const Ipc::FdNoteId noteId
);
503 void clientOpenListenSockets(void);
504 void clientConnectionsClose(void);
505 void httpRequestFree(void *);
507 /// decide whether to expect multiple requests on the corresponding connection
508 void clientSetKeepaliveFlag(ClientHttpRequest
*http
);
510 /* misplaced declaratrions of Stream callbacks provided/used by client side */
511 SQUIDCEXTERN CSR clientGetMoreData
;
512 SQUIDCEXTERN CSS clientReplyStatus
;
513 SQUIDCEXTERN CSD clientReplyDetach
;
514 CSCB clientSocketRecipient
;
515 CSD clientSocketDetach
;
517 /* TODO: Move to HttpServer. Warning: Move requires large code nonchanges! */
518 ClientSocketContext
*parseHttpRequest(ConnStateData
*, const Http1::RequestParserPointer
&);
519 void clientProcessRequest(ConnStateData
*, const Http1::RequestParserPointer
&, ClientSocketContext
*);
520 void clientPostHttpsAccept(ConnStateData
*);
522 #endif /* SQUID_CLIENTSIDE_H */