{
// normally we can splice here, because we just got client hello message
- if (fd_table[clientConnection->fd].ssl.get()) {
- // Restore default read methods
- fd_table[clientConnection->fd].read_method = &default_read_method;
- fd_table[clientConnection->fd].write_method = &default_write_method;
- }
+ // fde::ssl/tls_read_method() probably reads from our own inBuf. If so, then
+ // we should not lose any raw bytes when switching to raw I/O here.
+ if (fd_table[clientConnection->fd].ssl.get())
+ fd_table[clientConnection->fd].useDefaultIo();
// XXX: assuming that there was an HTTP/1.1 CONNECT to begin with...
// reset the current protocol to HTTP/1.1 (was "HTTPS" for the bumping process)
HERE << "Calling read handler on FD " << fd
);
PROF_start(comm_read_handler);
- F->flags.read_pending = 0;
F->read_handler = NULL;
hdl(fd, F->read_data);
PROF_stop(comm_read_handler);
if ((hdl = F->read_handler) != NULL) {
debugs(5, DEBUG_EPOLL ? 0 : 8, HERE << "Calling read handler on FD " << fd);
PROF_start(comm_write_handler);
- F->flags.read_pending = 0;
F->read_handler = NULL;
hdl(fd, F->read_data);
PROF_stop(comm_write_handler);
if (ke[i].filter == EVFILT_READ || F->flags.read_pending) {
if ((hdl = F->read_handler) != NULL) {
F->read_handler = NULL;
- F->flags.read_pending = 0;
hdl(fd, F->read_data);
}
}
if ((hdl = F->read_handler)) {
PROF_start(comm_read_handler);
F->read_handler = NULL;
- F->flags.read_pending = false;
hdl(fd, F->read_data);
PROF_stop(comm_read_handler);
++ statCounter.select_fds;
(void) 0;
else {
F->read_handler = NULL;
- F->flags.read_pending = 0;
commUpdateReadBits(fd, NULL);
hdl(fd, F->read_data);
++ statCounter.select_fds;
if ((hdl = F->read_handler)) {
F->read_handler = NULL;
- F->flags.read_pending = 0;
commUpdateReadBits(fd, NULL);
hdl(fd, F->read_data);
++ statCounter.select_fds;
case FD_SOCKET:
case FD_PIPE:
- F->read_method = &socket_read_method;
- F->write_method = &socket_write_method;
+ F->setIo(&socket_read_method, &socket_write_method);
break;
case FD_FILE:
case FD_LOG:
- F->read_method = &file_read_method;
- F->write_method = &file_write_method;
+ F->setIo(&file_read_method, &file_write_method);
break;
default:
switch (type) {
case FD_MSGHDR:
- F->read_method = &msghdr_read_method;
- F->write_method = &msghdr_write_method;
+ F->setIo(&msghdr_read_method, &msghdr_write_method);
break;
default:
- F->read_method = &default_read_method;
- F->write_method = &default_write_method;
+ F->setIo(&default_read_method, &default_write_method);
break;
}
* Please see the COPYING and CONTRIBUTORS files for details.
*/
-/* DEBUG: none FDE */
+/* DEBUG: section 05 Comm */
#include "squid.h"
#include "comm/Read.h"
+#include "Debug.h"
#include "fd.h"
#include "fde.h"
#include "globals.h"
fde *fde::Table = nullptr;
+void
+fde::setIo(READ_HANDLER *reader, WRITE_HANDLER *writer)
+{
+ assert(reader);
+ assert(writer);
+ assert(!flags.read_pending); // this method is only meant for new FDs
+
+ readMethod_ = reader;
+ writeMethod_ = writer;
+}
+
+void
+fde::useDefaultIo()
+{
+ debugs(5, 7, "old read_pending=" << flags.read_pending);
+
+ // Some buffering readers are using external-to-them buffers (e.g., inBuf)
+ // and might leave true flags.read_pending behind without losing data. We
+ // must clear the flag here because default I/O methods do not know about it
+ // and will leave it set forever, resulting in I/O loops.
+ flags.read_pending = false;
+
+ readMethod_ = default_read_method;
+ writeMethod_ = default_write_method;
+}
+
+/// use I/O methods that maintain an internal-to-them buffer
+void
+fde::useBufferedIo(READ_HANDLER *bufferingReader, WRITE_HANDLER *bufferingWriter)
+{
+ debugs(5, 7, "read_pending=" << flags.read_pending);
+
+ assert(bufferingReader);
+ assert(bufferingWriter);
+ // flags.read_pending ought to be false here, but these buffering methods
+ // can handle a stale true flag so we do not check or reset it
+
+ readMethod_ = bufferingReader;
+ writeMethod_ = bufferingWriter;
+}
+
bool
fde::readPending(int fdNumber) const
{
*desc = 0;
read_handler = nullptr;
write_handler = nullptr;
- read_method = nullptr;
- write_method = nullptr;
+ readMethod_ = nullptr;
+ writeMethod_ = nullptr;
}
/// Clear the fde class back to NULL equivalent.
/// True if comm_close for this fd has been called
bool closing() const { return flags.close_request; }
+ /// set I/O methods for a freshly opened descriptor
+ void setIo(READ_HANDLER *, WRITE_HANDLER *);
+
+ /// Use default I/O methods. When called after useBufferedIo(), the caller
+ /// is responsible for any (unread or unwritten) buffered data.
+ void useDefaultIo();
+
+ /// use I/O methods that maintain an internal-to-them buffer
+ void useBufferedIo(READ_HANDLER *, WRITE_HANDLER *);
+
+ int read(int fd, char *buf, int len) { return readMethod_(fd, buf, len); }
+ int write(int fd, const char *buf, int len) { return writeMethod_(fd, buf, len); }
+
/* NOTE: memset is used on fdes today. 20030715 RBC */
static void DumpStats(StoreEntry *);
bool called_connect = false;
bool nodelay = false;
bool close_on_exec = false;
+ /// buffering readMethod_ has data to give (regardless of socket state)
bool read_pending = false;
//bool write_pending; //XXX seems not to be used
bool transparent = false;
void *lifetime_data = nullptr;
AsyncCall::Pointer closeHandler;
AsyncCall::Pointer halfClosedReader; /// read handler for half-closed fds
- READ_HANDLER *read_method;
- WRITE_HANDLER *write_method;
Security::SessionPointer ssl;
Security::ContextPointer dynamicTlsContext; ///< cached and then freed when fd is closed
#if _SQUID_WINDOWS_
nfmarkToServer in that this is the value we *receive* from the,
connection, whereas nfmarkToServer is the value to set on packets
*leaving* Squid. */
+
+private:
+ // I/O methods connect Squid to the device/stack/library fde represents
+ READ_HANDLER *readMethod_ = nullptr; ///< imports bytes into Squid
+ WRITE_HANDLER *writeMethod_ = nullptr; ///< exports Squid bytes
};
#define fd_table fde::Table
int fdNFree(void);
-#define FD_READ_METHOD(fd, buf, len) (*fde::Table[fd].read_method)(fd, buf, len)
-#define FD_WRITE_METHOD(fd, buf, len) (*fde::Table[fd].write_method)(fd, buf, len)
+inline int
+FD_READ_METHOD(int fd, char *buf, int len)
+{
+ return fd_table[fd].read(fd, buf, len);
+}
+
+inline int
+FD_WRITE_METHOD(int fd, const char *buf, int len)
+{
+ return fd_table[fd].write(fd, buf, len);
+}
#endif /* SQUID_FDE_H */
#endif
debugs(83, 5, "link FD " << fd << " to TLS session=" << (void*)session.get());
+
fd_table[fd].ssl = session;
- fd_table[fd].read_method = &tls_read_method;
- fd_table[fd].write_method = &tls_write_method;
+ fd_table[fd].useBufferedIo(&tls_read_method, &tls_write_method);
fd_note(fd, squidCtx);
return true;
}
TunnelStateData *tunnelState = new TunnelStateData(context->http);
- fd_table[clientConn->fd].read_method = &default_read_method;
- fd_table[clientConn->fd].write_method = &default_write_method;
+ // tunnelStartShoveling() drains any buffered from-client data (inBuf)
+ fd_table[clientConn->fd].useDefaultIo();
request->hier.resetPeerNotes(srvConn, tunnelState->getHost());
AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
commSetConnTimeout(srvConn, Config.Timeout.read, timeoutCall);
- fd_table[srvConn->fd].read_method = &default_read_method;
- fd_table[srvConn->fd].write_method = &default_write_method;
+
+ // we drain any already buffered from-server data below (rBufData)
+ fd_table[srvConn->fd].useDefaultIo();
auto ssl = fd_table[srvConn->fd].ssl.get();
assert(ssl);