]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.h
Merged from trunk (r13356).
[thirdparty/squid.git] / src / client_side.h
1 /*
2 * DEBUG: section 33 Client-side Routines
3 * AUTHOR: Duane Wessels
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
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.
16 *
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.
21 *
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.
26 *
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.
30 *
31 */
32
33 #ifndef SQUID_CLIENTSIDE_H
34 #define SQUID_CLIENTSIDE_H
35
36 #include "comm.h"
37 #include "HttpControlMsg.h"
38 #include "HttpParser.h"
39 #include "SBuf.h"
40 #if USE_AUTH
41 #include "auth/UserRequest.h"
42 #endif
43 #if USE_OPENSSL
44 #include "ssl/support.h"
45 #endif
46
47 class ConnStateData;
48 class ClientHttpRequest;
49 class clientStreamNode;
50 class ChunkedCodingParser;
51 class HelperReply;
52 namespace AnyP
53 {
54 class PortCfg;
55 } // namespace Anyp
56
57 /**
58 * Badly named.
59 * This is in fact the processing context for a single HTTP request.
60 *
61 * Managing what has been done, and what happens next to the data buffer
62 * holding what we hope is an HTTP request.
63 *
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.
68 *
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.
72 *
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
77 * buffer.
78 *
79 * The individual processing actions are done by other Jobs which we
80 * kick off as needed.
81 */
82 class ClientSocketContext : public RefCountable
83 {
84
85 public:
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();
92
93 Comm::ConnectionPointer clientConnection; /// details about the client connection socket.
94 ClientHttpRequest *http; /* we own this */
95 HttpReply *reply;
96 char reqbuf[HTTP_REQBUF_SZ];
97 Pointer next;
98
99 struct {
100
101 unsigned deferred:1; /* This is a pipelined request waiting for the current object to complete */
102
103 unsigned parsed_ok:1; /* Was this parsed correctly? */
104 } flags;
105 bool mayUseConnection() const {return mayUseConnection_;}
106
107 void mayUseConnection(bool aBool) {
108 mayUseConnection_ = aBool;
109 debugs(33,3, HERE << "This " << this << " marked " << aBool);
110 }
111
112 class DeferredParams
113 {
114
115 public:
116 clientStreamNode *node;
117 HttpReply *rep;
118 StoreIOBuffer queuedBuffer;
119 };
120
121 DeferredParams deferredparams;
122 int64_t writtenToSocket;
123 void pullData();
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
141
142 /// starts writing 1xx control message to the client
143 void writeControlMsg(HttpControlMsg &msg);
144
145 protected:
146 static IOCB WroteControlMsg;
147 void wroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag, int xerrno);
148
149 private:
150 void prepareReply(HttpReply * rep);
151 void packChunk(const StoreIOBuffer &bodyData, MemBuf &mb);
152 void packRange(StoreIOBuffer const &, MemBuf * mb);
153 void deRegisterWithConn();
154 void doClose();
155 void initiateClose(const char *reason);
156
157 AsyncCall::Pointer cbControlMsgSent; ///< notifies HttpControlMsg Source
158
159 bool mayUseConnection_; /* This request may use the connection. Don't read anymore requests for now */
160 bool connRegistered_;
161
162 CBDATA_CLASS2(ClientSocketContext);
163 };
164
165 class ConnectionDetail;
166 #if USE_OPENSSL
167 namespace Ssl
168 {
169 class ServerBump;
170 }
171 #endif
172 /**
173 * Manages a connection to a client.
174 *
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.
178 *
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)
183 *
184 * If the above can be confirmed accurate we can call this object PipelineManager or similar
185 */
186 class ConnStateData : public BodyProducer, public HttpControlMsgSink
187 {
188
189 public:
190 explicit ConnStateData(const MasterXaction::Pointer &xact);
191 ~ConnStateData();
192
193 void readSomeData();
194 void readSomeFtpData();
195 bool areAllContextsForThisConnection() const;
196 void freeAllContexts();
197 void notifyAllContexts(const int xerrno); ///< tell everybody about the err
198 /// Traffic parsing
199 bool clientParseRequests();
200 void readNextRequest();
201 ClientSocketContext::Pointer getCurrentContext() const;
202 void addContextToQueue(ClientSocketContext * context);
203 int getConcurrentRequestCount() const;
204 bool isOpen() const;
205 void checkHeaderLimits();
206
207 // HttpControlMsgSink API
208 virtual void sendControlMsg(HttpControlMsg msg);
209
210 // Client TCP connection details from comm layer.
211 Comm::ConnectionPointer clientConnection;
212
213 struct In {
214 In();
215 ~In();
216 bool maybeMakeSpaceAvailable();
217
218 ChunkedCodingParser *bodyParser; ///< parses chunked request body
219 SBuf buf;
220 } in;
221
222 /** number of body bytes we need to comm_read for the "current" request
223 *
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
227 */
228 int64_t mayNeedToReadMoreBody() const;
229
230 #if USE_AUTH
231 /**
232 * Fetch the user details for connection based authentication
233 * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
234 */
235 const Auth::UserRequest::Pointer &getAuth() const { return auth_; }
236
237 /**
238 * Set the user details for connection-based authentication to use from now until connection closure.
239 *
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
244 */
245 void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause);
246 #endif
247
248 /**
249 * used by the owner of the connection, opaque otherwise
250 * TODO: generalise the connection owner concept.
251 */
252 ClientSocketContext::Pointer currentobject;
253
254 Ip::Address log_addr;
255 int nrequests;
256
257 struct {
258 bool readMore; ///< needs comm_read (for this request or new requests)
259 bool swanSang; // XXX: temporary flag to check proper cleanup
260 } flags;
261 struct {
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*/
272 } pinning;
273
274 /// Squid listening port details where this connection arrived.
275 AnyP::PortCfg *port;
276
277 bool transparent() const;
278 bool reading() const;
279 void stopReading(); ///< cancels comm_read if it is scheduled
280
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);
289
290 void expectNoForwarding(); ///< cleans up virgin request [body] forwarding state
291
292 BodyPipe::Pointer expectRequestBody(int64_t size);
293 virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer);
294 virtual void noteBodyConsumerAborted(BodyPipe::Pointer);
295
296 bool handleReadData(SBuf *buf);
297 bool handleRequestBodyData();
298
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);
306 /**
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).
312 */
313 const Comm::ConnectionPointer validatePinnedConnection(HttpRequest *request, const CachePeer *peer);
314 /**
315 * returts the pinned CachePeer if exists, NULL otherwise
316 */
317 CachePeer *pinnedPeer() const {return pinning.peer;}
318 bool pinnedAuth() const {return pinning.auth;}
319
320 // pining related comm callbacks
321 void clientPinnedConnectionClosed(const CommCloseCbParams &io);
322
323 // comm callbacks
324 void clientReadRequest(const CommIoCbParams &io);
325 void clientReadFtpData(const CommIoCbParams &io);
326 void connStateClosed(const CommCloseCbParams &io);
327 void requestTimeout(const CommTimeoutCbParams &params);
328
329 // AsyncJob API
330 virtual bool doneAll() const { return BodyProducer::doneAll() && false;}
331 virtual void swanSong();
332
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
336
337 /// The caller assumes responsibility for connection closure detection.
338 void stopPinnedConnectionMonitoring();
339
340 const bool isFtp;
341 enum FtpState {
342 FTP_BEGIN,
343 FTP_CONNECTED,
344 FTP_HANDLE_FEAT,
345 FTP_HANDLE_PASV,
346 FTP_HANDLE_PORT,
347 FTP_HANDLE_DATA_REQUEST,
348 FTP_HANDLE_UPLOAD_REQUEST,
349 FTP_HANDLE_EPRT,
350 FTP_HANDLE_EPSV,
351 FTP_HANDLE_CWD,
352 FTP_HANDLE_PASS,
353 FTP_HANDLE_CDUP,
354 FTP_ERROR
355 };
356 struct {
357 String uri;
358 String host;
359 String workingDir;
360 FtpState state;
361 bool readGreeting;
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
372 } ftp;
373 const char *ftpBuildUri(const char *file = NULL);
374 void ftpSetWorkingDir(const char *dir);
375
376 #if USE_OPENSSL
377 /// called by FwdState when it is done bumping the server
378 void httpsPeeked(Comm::ConnectionPointer serverConnection);
379
380 /// Start to create dynamic SSL_CTX for host or uses static port SSL context.
381 void getSslContextStart();
382 /**
383 * Done create dynamic ssl certificate.
384 *
385 * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
386 */
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);
392
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) {
397 if (!sslServerBump)
398 sslServerBump = srvBump;
399 else
400 assert(sslServerBump == srvBump);
401 }
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);
410
411 Ssl::BumpMode sslBumpMode; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
412
413 #else
414 bool switchedToHttps() const { return false; }
415 #endif
416
417 void finishDechunkingRequest(bool withSuccess);
418
419 void resumeFtpRequest(ClientSocketContext *const context);
420
421 protected:
422 void startDechunkingRequest();
423 void abortChunkedRequestBody(const err_type error);
424 err_type handleChunkedRequestBody(size_t &putSize);
425
426 void startPinnedConnectionMonitoring();
427 void clientPinnedConnectionRead(const CommIoCbParams &io);
428
429 private:
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();
435
436 bool concurrentRequestQueueFilled() const;
437
438 void pinNewConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth);
439
440 #if USE_AUTH
441 /// some user details that can be used to perform authentication on this connection
442 Auth::UserRequest::Pointer auth_;
443 #endif
444
445 HttpParser parser_;
446
447 // XXX: CBDATA plays with public/private and leaves the following 'private' fields all public... :(
448
449 #if USE_OPENSSL
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
455
456 /// HTTPS server cert. fetching state for bump-ssl-server-first
457 Ssl::ServerBump *sslServerBump;
458 Ssl::CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
459 #endif
460
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_;
465
466 AsyncCall::Pointer reader; ///< set when we are reading
467 BodyPipe::Pointer bodyPipe; // set when we are reading request body
468
469 CBDATA_CLASS2(ConnStateData);
470 };
471
472 void setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl = false);
473
474 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end = NULL);
475
476 int varyEvaluateMatch(StoreEntry * entry, HttpRequest * req);
477
478 void clientOpenListenSockets(void);
479 void clientConnectionsClose(void);
480 void httpRequestFree(void *);
481
482 #endif /* SQUID_CLIENTSIDE_H */