]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/client_side.h
SourceFormat Enforcement
[thirdparty/squid.git] / src / client_side.h
index 2e8d36357005f9d55f389c35a95b7d6695ad2ae5..74797884d812fd463690087dba62d48bd6d81db8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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
-{
-    CBDATA_CLASS(ClientSocketContext);
-
-public:
-    typedef RefCount<ClientSocketContext> Pointer;
-    ClientSocketContext(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq);
-    ~ClientSocketContext();
-    bool startOfOutput() const;
-    void writeComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag);
-
-    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 {
-
-        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
-
-    /// starts writing 1xx control message to the client
-    void writeControlMsg(HttpControlMsg &msg);
-
-protected:
-    static IOCB WroteControlMsg;
-    void wroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag, int xerrno);
-
-private:
-    void prepareReply(HttpReply * rep);
-    void packChunk(const StoreIOBuffer &bodyData, MemBuf &mb);
-    void packRange(StoreIOBuffer const &, MemBuf * mb);
-    void doClose();
-    void initiateClose(const char *reason);
-
-    AsyncCall::Pointer cbControlMsgSent; ///< notifies HttpControlMsg Source
-
-    bool mayUseConnection_; /* This request may use the connection. Don't read anymore requests for now */
-    bool connRegistered_;
-};
+class HttpHdrRangeSpec;
 
-class ConnectionDetail;
 #if USE_OPENSSL
 namespace Ssl
 {
@@ -183,7 +54,7 @@ class ServerBump;
  * 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.
  *
@@ -191,7 +62,7 @@ class ServerBump;
  * 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:
@@ -202,6 +73,11 @@ public:
     virtual void receivedFirstByte();
     virtual bool handleReadData();
     virtual void afterClientRead();
+    virtual void afterClientWrite(size_t);
+
+    /* HttpControlMsgSink API */
+    virtual void sendControlMsg(HttpControlMsg);
+    virtual void doneWithControlMsg();
 
     /// Traffic parsing
     bool clientParseRequests();
@@ -212,9 +88,6 @@ public:
 
     bool isOpen() const;
 
-    // HttpControlMsgSink API
-    virtual void sendControlMsg(HttpControlMsg msg);
-
     Http1::TeChunkedParser *bodyParser; ///< parses HTTP/1.1 chunked request body
 
     /** number of body bytes we need to comm_read for the "current" request
@@ -332,8 +205,7 @@ public:
 
     /// 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
@@ -342,27 +214,23 @@ public:
     void httpsPeeked(Comm::ConnectionPointer serverConnection);
 
     /// Splice a bumped client connection on peek-and-splice mode
-    void splice();
+    bool splice();
 
-    /// Check on_unsupported_protocol access list and splice if required
-    /// \retval true on splice
-    /// \retval false otherwise
-    bool spliceOnError(const err_type err);
-
-    /// Start to create dynamic SSL_CTX 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::ContextPointer 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) {
@@ -380,10 +248,13 @@ public:
     /// 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
@@ -393,10 +264,10 @@ public:
     void connectionTag(const char *aTag) { connectionTag_ = aTag; }
 
     /// handle a control message received by context from a peer and call back
-    virtual void writeControlMsgAndCall(ClientSocketContext *context, 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
@@ -405,11 +276,20 @@ public:
     /* 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.
@@ -437,10 +317,10 @@ protected:
     /// 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;
@@ -453,6 +333,7 @@ protected:
 private:
     /* ::Server API */
     virtual bool connFinishedWithConn(int size);
+    virtual void checkLogging();
 
     void clientAfterReadingRequests();
     bool concurrentRequestQueueFilled() const;
@@ -479,6 +360,8 @@ private:
 
 #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
@@ -513,6 +396,12 @@ void httpRequestFree(void *);
 /// 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;
@@ -521,8 +410,8 @@ CSCB clientSocketRecipient;
 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 */