--- /dev/null
- * A context lifetime extends from directly after a request has been parsed
+ /*
+ * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+ #ifndef SQUID_SRC_HTTP_STREAM_H
+ #define SQUID_SRC_HTTP_STREAM_H
+
+ #include "http/forward.h"
+ #include "mem/forward.h"
+ #include "StoreIOBuffer.h"
+
+ class clientStreamNode;
+ class ClientHttpRequest;
+
+ namespace Http
+ {
+
+ /**
+ * The processing context for a single HTTP transaction (stream).
+ *
- * and reply payload (if any) have been written.
++ * A stream lifetime extends from directly after a request has been parsed
+ * off the client connection buffer, until the last byte of both request
- * Contexts self-register with the Http::Server Pipeline being managed by the
++ * and reply payload (if any) have been written, or it is otherwise
++ * explicitly terminated.
+ *
- * When a context is completed the finished() method needs to be called which
++ * Streams self-register with the Http::Server Pipeline being managed by the
+ * Server for the connection on which the request was received.
+ *
+ * The socket level management and I/O 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 start.
+ *
- * whole client connection and all other pending contexts.
++ * When a stream 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
- * the client connection. Only the back() context may read from the connection,
- * and only the front() context may write to it. A context which needs to read
++ * whole client connection and all other pending streams.
+ *
+ * HTTP/1.x:
+ *
+ * When HTTP/1 pipeline is operating there may be multiple transactions using
++ * the client connection. Only the back() stream may read from the connection,
++ * and only the front() stream may write to it. A stream which needs to read
+ * or write to the connection but does not meet those criteria must be shifted
+ * to the deferred state.
+ *
+ *
+ * XXX: If an async call ends the ClientHttpRequest job, Http::Stream
+ * (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 Http::Stream which pretends to "own" *http.
+ */
+ class Stream : public RefCountable
+ {
+ MEMPROXY_CLASS(Stream);
+
+ public:
+ /// construct with HTTP/1.x details
+ Stream(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq);
+ ~Stream();
+
+ /// register this stream with the Server
+ void registerWithConn();
+
+ /// whether the reply has started being sent
+ bool startOfOutput() const;
+
+ /// update stream state after a write, may initiate more I/O
+ void writeComplete(size_t size);
+
+ /// get more data to send
+ void pullData();
+
+ /// \return true if the HTTP request is for multiple ranges
+ bool multipartRangeRequest() const;
+
+ int64_t getNextRangeOffset() const;
+ bool canPackMoreRanges() const;
+ size_t lengthToSend(Range<int64_t> const &available) const;
+
+ clientStream_status_t socketState();
+
+ /// send an HTTP reply message headers and maybe some initial payload
+ void sendStartOfMessage(HttpReply *, StoreIOBuffer bodyData);
+ /// send some HTTP reply message payload
+ void sendBody(StoreIOBuffer bodyData);
+ /// update stream state when N bytes are being sent.
+ /// NP: Http1Server bytes actually not sent yet, just packed into a MemBuf ready
+ void noteSentBodyBytes(size_t);
+
+ /// add Range headers (if any) to the given HTTP reply message
+ void buildRangeHeader(HttpReply *);
+
+ clientStreamNode * getTail() const;
+ clientStreamNode * getClientReplyContext() const;
+
+ ConnStateData *getConn() const;
+
+ /// update state to reflect I/O error
+ void noteIoError(const int xerrno);
+
+ /// cleanup when the transaction has finished. may destroy 'this'
+ void finished();
+
+ /// terminate due to a send/write error (may continue reading)
+ void initiateClose(const char *reason);
+
+ void deferRecipientForLater(clientStreamNode *, HttpReply *, StoreIOBuffer receivedData);
+
+ public: // HTTP/1.x state data
+
+ 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, "This " << this << " marked " << aBool);
+ }
+
+ class DeferredParams
+ {
+
+ public:
+ clientStreamNode *node;
+ HttpReply *rep;
+ StoreIOBuffer queuedBuffer;
+ };
+
+ DeferredParams deferredparams;
+ int64_t writtenToSocket;
+
+ private:
+ void prepareReply(HttpReply *);
+ void packChunk(const StoreIOBuffer &bodyData, MemBuf &);
+ void packRange(StoreIOBuffer const &, MemBuf *);
+ void doClose();
+
+ bool mayUseConnection_; /* This request may use the connection. Don't read anymore requests for now */
+ bool connRegistered_;
+ };
+
+ } // namespace Http
+
+ #endif /* SQUID_SRC_HTTP_STREAM_H */
+