/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "http/forward.h"
#include "HttpControlMsg.h"
#include "ipc/FdNotes.h"
-#include "SBuf.h"
+#include "sbuf/SBuf.h"
#include "servers/Server.h"
#if USE_AUTH
#include "auth/UserRequest.h"
#endif
#if USE_OPENSSL
+#include "security/Handshake.h"
#include "ssl/support.h"
#endif
-class ConnStateData;
class ClientHttpRequest;
-class clientStreamNode;
-namespace AnyP
-{
-class PortCfg;
-} // namespace Anyp
-
-/**
- * Badly named.
- * This is in fact the processing context for a single HTTP transaction.
- *
- * A context lifetime extends from directly after a request has been parsed
- * off the client connection buffer, until the last byte of both request
- * and reply payload (if any) have been written.
- *
- * (NOTE: it is not certain yet if an early reply to a POST/PUT is sent by
- * the server whether the context will remain in the pipeline until its
- * request payload has finished being read. It is supposed to, but may not)
- *
- * Contexts self-register with the Pipeline being managed by the Server
- * for the connection on which the request was received.
- *
- * When HTTP/1 pipeline is operating there may be multiple transactions using
- * the clientConnection. Only the back() context may read from the connection,
- * and only the front() context may write to it. A context which needs to read
- * or write to the connection but does not meet those criteria must be shifted
- * to the deferred state.
- *
- * When a context is completed the finished() method needs to be called which
- * will perform all cleanup and deregistration operations. If the reason for
- * finishing is an error, then notifyIoError() needs to be called prior to
- * the finished() method.
- * The caller should follow finished() with a call to ConnStateData::kick()
- * to resume processing of other transactions or I/O on the connection.
- *
- * Alternatively the initiateClose() method can be called to terminate the
- * whole client connection and all other pending contexts.
- *
- * The socket level management is done by a Server which owns us.
- * The scope of this objects control over a socket consists of the data
- * buffer received from the Server with an initially unknown length.
- * When that length is known it sets the end boundary of our access to the
- * buffer.
- *
- * The individual processing actions are done by other Jobs which we
- * kick off as needed.
- *
- * XXX: If an async call ends the ClientHttpRequest job, ClientSocketContext
- * (and ConnStateData) may not know about it, leading to segfaults and
- * assertions. This is difficult to fix
- * because ClientHttpRequest lacks a good way to communicate its ongoing
- * destruction back to the ClientSocketContext which pretends to "own" *http.
- */
-class ClientSocketContext : public RefCountable
-{
- MEMPROXY_CLASS(ClientSocketContext);
-
-public:
- typedef RefCount<ClientSocketContext> Pointer;
- ClientSocketContext(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq);
- ~ClientSocketContext();
- bool startOfOutput() const;
- void writeComplete(size_t size);
-
- Comm::ConnectionPointer clientConnection; /// details about the client connection socket.
- ClientHttpRequest *http; /* we pretend to own that job */
- HttpReply *reply;
- char reqbuf[HTTP_REQBUF_SZ];
-
- struct {
+class HttpHdrRangeSpec;
- unsigned deferred:1; /* This is a pipelined request waiting for the current object to complete */
-
- unsigned parsed_ok:1; /* Was this parsed correctly? */
- } flags;
- bool mayUseConnection() const {return mayUseConnection_;}
-
- void mayUseConnection(bool aBool) {
- mayUseConnection_ = aBool;
- debugs(33,3, HERE << "This " << this << " marked " << aBool);
- }
-
- class DeferredParams
- {
-
- public:
- clientStreamNode *node;
- HttpReply *rep;
- StoreIOBuffer queuedBuffer;
- };
-
- DeferredParams deferredparams;
- int64_t writtenToSocket;
- void pullData();
- int64_t getNextRangeOffset() const;
- bool canPackMoreRanges() const;
- clientStream_status_t socketState();
- void sendBody(HttpReply * rep, StoreIOBuffer bodyData);
- void sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData);
- size_t lengthToSend(Range<int64_t> const &available);
- void noteSentBodyBytes(size_t);
- void buildRangeHeader(HttpReply * rep);
- clientStreamNode * getTail() const;
- clientStreamNode * getClientReplyContext() const;
- ConnStateData *getConn() const;
- void finished(); ///< cleanup when the transaction has finished. may destroy 'this'
- void deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData);
- bool multipartRangeRequest() const;
- void registerWithConn();
- void noteIoError(const int xerrno); ///< update state to reflect I/O error
- void initiateClose(const char *reason); ///< terminate due to a send/write error (may continue reading)
-
-private:
- void prepareReply(HttpReply * rep);
- void packChunk(const StoreIOBuffer &bodyData, MemBuf &mb);
- void packRange(StoreIOBuffer const &, MemBuf * mb);
- void doClose();
-
- bool mayUseConnection_; /* This request may use the connection. Don't read anymore requests for now */
- bool connRegistered_;
-};
-
-class ConnectionDetail;
#if USE_OPENSSL
namespace Ssl
{
* processed.
*
* Performs HTTP message processing to kick off the actual HTTP request
- * handling objects (ClientSocketContext, ClientHttpRequest, HttpRequest).
+ * handling objects (Http::Stream, ClientHttpRequest, HttpRequest).
*
* Performs SSL-Bump processing for switching between HTTP and HTTPS protocols.
*
* managing, or for graceful half-close use the stopReceiving() or
* stopSending() methods.
*/
-class ConnStateData : public Server, public HttpControlMsgSink, public RegisteredRunner
+class ConnStateData : public Server, public HttpControlMsgSink, private IndependentRunner
{
public:
/* HttpControlMsgSink API */
virtual void sendControlMsg(HttpControlMsg);
+ virtual void doneWithControlMsg();
/// Traffic parsing
bool clientParseRequests();
/// Initializes and starts a peek-and-splice negotiation with the SSL client
void startPeekAndSplice();
- /// Called when the initialization of peek-and-splice negotiation finidhed
- void startPeekAndSpliceDone();
+
/// Called when a peek-and-splice step finished. For example after
/// server SSL certificates received and fake server SSL certificates
/// generated
void httpsPeeked(Comm::ConnectionPointer serverConnection);
/// Splice a bumped client connection on peek-and-splice mode
- void splice();
-
- /// Check on_unsupported_protocol access list and splice if required
- /// \retval true on splice
- /// \retval false otherwise
- bool spliceOnError(const err_type err);
+ bool splice();
- /// Start to create dynamic Security::ContextPtr for host or uses static port SSL context.
+ /// Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
void getSslContextStart();
/**
* Done create dynamic ssl certificate.
*
* \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
*/
- void getSslContextDone(Security::ContextPtr sslContext, bool isNew = false);
+ void getSslContextDone(Security::ContextPointer &, bool isNew = false);
/// Callback function. It is called when squid receive message from ssl_crtd.
static void sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply);
/// Proccess response from ssl_crtd.
void sslCrtdHandleReply(const Helper::Reply &reply);
void switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode);
+ void parseTlsHandshake();
bool switchedToHttps() const { return switchedToHttps_; }
Ssl::ServerBump *serverBump() {return sslServerBump;}
inline void setServerBump(Ssl::ServerBump *srvBump) {
/// Returns false if no [delayed] error should be written to the client.
/// Otherwise, writes the error to the client and returns true. Also checks
/// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
- bool serveDelayedError(ClientSocketContext *context);
+ bool serveDelayedError(Http::Stream *);
Ssl::BumpMode sslBumpMode; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
+ /// Tls parser to use for client HELLO messages parsing on bumped
+ /// connections.
+ Security::HandshakeParser tlsParser;
#else
bool switchedToHttps() const { return false; }
#endif
void connectionTag(const char *aTag) { connectionTag_ = aTag; }
/// handle a control message received by context from a peer and call back
- virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
+ virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
/// ClientStream calls this to supply response header (once) and data
- /// for the current ClientSocketContext.
+ /// for the current Http::Stream.
virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData) = 0;
/// remove no longer needed leading bytes from the input buffer
/* TODO: Make the methods below (at least) non-public when possible. */
/// stop parsing the request and create context for relaying error info
- ClientSocketContext *abortRequestParsing(const char *const errUri);
+ Http::Stream *abortRequestParsing(const char *const errUri);
/// generate a fake CONNECT request with the given payload
/// at the beginning of the client I/O buffer
- void fakeAConnectRequest(const char *reason, const SBuf &payload);
+ bool fakeAConnectRequest(const char *reason, const SBuf &payload);
+
+ /// generates and sends to tunnel.cc a fake request with a given payload
+ bool initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload);
+
+ /// whether tunneling of unsupported protocol is allowed for this connection
+ bool mayTunnelUnsupportedProto();
+
+ /// build a fake http request
+ ClientHttpRequest *buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload);
/// client data which may need to forward as-is to server after an
/// on_unsupported_protocol tunnel decision.
/// parse input buffer prefix into a single transfer protocol request
/// return NULL to request more header bytes (after checking any limits)
/// use abortRequestParsing() to handle parsing errors w/o creating request
- virtual ClientSocketContext *parseOneRequest() = 0;
+ virtual Http::Stream *parseOneRequest() = 0;
/// start processing a freshly parsed request
- virtual void processParsedRequest(ClientSocketContext *context) = 0;
+ virtual void processParsedRequest(Http::StreamPointer &) = 0;
/// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
virtual int pipelinePrefetchMax() const;
private:
/* ::Server API */
virtual bool connFinishedWithConn(int size);
+ virtual void checkLogging();
void clientAfterReadingRequests();
bool concurrentRequestQueueFilled() const;
#if USE_OPENSSL
bool switchedToHttps_;
+ bool parsingTlsHandshake; ///< whether we are getting/parsing TLS Hello bytes
+
/// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
String sslConnectHostOrIp; ///< The SSL server host name as passed in the CONNECT request
SBuf sslCommonName_; ///< CN name for SSL certificate generation
/// decide whether to expect multiple requests on the corresponding connection
void clientSetKeepaliveFlag(ClientHttpRequest *http);
+/// append a "part" HTTP header (as in a multi-part/range reply) to the buffer
+void clientPackRangeHdr(const HttpReply *, const HttpHdrRangeSpec *, String boundary, MemBuf *);
+
+/// put terminating boundary for multiparts to the buffer
+void clientPackTermBound(String boundary, MemBuf *);
+
/* misplaced declaratrions of Stream callbacks provided/used by client side */
SQUIDCEXTERN CSR clientGetMoreData;
SQUIDCEXTERN CSS clientReplyStatus;
CSD clientSocketDetach;
/* TODO: Move to HttpServer. Warning: Move requires large code nonchanges! */
-ClientSocketContext *parseHttpRequest(ConnStateData *, const Http1::RequestParserPointer &);
-void clientProcessRequest(ConnStateData *, const Http1::RequestParserPointer &, ClientSocketContext *);
+Http::Stream *parseHttpRequest(ConnStateData *, const Http1::RequestParserPointer &);
+void clientProcessRequest(ConnStateData *, const Http1::RequestParserPointer &, Http::Stream *);
void clientPostHttpsAccept(ConnStateData *);
#endif /* SQUID_CLIENTSIDE_H */