/// \ingroup ServerProtocolFTPInternal
typedef void (FTPSM) (FtpStateData *);
+/// common code for FTP control and data channels
+class FtpChannel {
+public:
+ FtpChannel(): fd(-1) {}
+
+ /// called after the socket is opened, sets up close handler
+ void opened(int aFd, const AsyncCall::Pointer &aCloser);
+
+ void close(); /// clears the close handler and calls comm_close
+ void clear(); /// just resets fd and close handler
+
+ int fd; /// channel descriptor; \todo: remove because the closer has it
+
+private:
+ AsyncCall::Pointer closer; /// Comm close handler callback
+};
+
/// \ingroup ServerProtocolFTPInternal
class FtpStateData : public ServerStateData
{
char *old_filepath;
char typecode;
- struct
+ // \todo: optimize ctrl and data structs member order, to minimize size
+ /// FTP control channel info; the channel is opened once per transaction
+ struct CtrlChannel: public FtpChannel
{
- int fd;
char *buf;
size_t size;
size_t offset;
int replycode;
} ctrl;
- struct
+ /// FTP data channel info; the channel may be opened/closed a few times
+ struct DataChannel: public FtpChannel
{
- int fd;
MemBuf *readBuf;
char *host;
u_short port;
struct _ftp_flags flags;
private:
- AsyncCall::Pointer closeHandler;
CBDATA_CLASS(FtpStateData);
public:
static CNCB ftpPasvCallback;
static PF ftpDataWrite;
void ftpTimeout(const CommTimeoutCbParams &io);
- void ftpSocketClosed(const CommCloseCbParams &io);
+ void ctrlClosed(const CommCloseCbParams &io);
+ void dataClosed(const CommCloseCbParams &io);
void ftpReadControlReply(const CommIoCbParams &io);
void ftpWriteCommandCallback(const CommIoCbParams &io);
void ftpAcceptDataConnection(const CommAcceptCbParams &io);
virtual bool doneWithServer() const;
virtual bool haveControlChannel(const char *caller_name) const;
+ AsyncCall::Pointer dataCloser(); /// creates a Comm close callback
private:
// BodyConsumer for HTTP: consume request body.
ftpReadMkdir /* SENT_MKDIR */
};
+/// close handler called by Comm when FTP control channel is closed prematurely
+void
+FtpStateData::ctrlClosed(const CommCloseCbParams &io)
+{
+ ctrl.clear();
+ deleteThis("FtpStateData::ctrlClosed");
+}
+
+/// close handler called by Comm when FTP data channel is closed prematurely
void
-FtpStateData::ftpSocketClosed(const CommCloseCbParams &io)
+FtpStateData::dataClosed(const CommCloseCbParams &io)
{
- ctrl.fd = -1;
- deleteThis("FtpStateData::ftpSocketClosed");
+ data.clear();
+ deleteThis("FtpStateData::dataClosed");
}
FtpStateData::FtpStateData(FwdState *theFwdState) : AsyncJob("FtpStateData"), ServerStateData(theFwdState)
debugs(9, 3, HERE << "'" << url << "'" );
statCounter.server.all.requests++;
statCounter.server.ftp.requests++;
- ctrl.fd = theFwdState->server_fd;
- data.fd = -1;
theSize = -1;
mdtm = -1;
flags.rest_supported = 1;
typedef CommCbMemFunT<FtpStateData, CommCloseCbParams> Dialer;
- closeHandler = asyncCall(9, 5, "FtpStateData::ftpSocketClosed",
- Dialer(this,&FtpStateData::ftpSocketClosed));
- comm_add_close_handler(ctrl.fd, closeHandler);
+ AsyncCall::Pointer closer = asyncCall(9, 5, "FtpStateData::ctrlClosed",
+ Dialer(this, &FtpStateData::ctrlClosed));
+ ctrl.opened(theFwdState->server_fd, closer);
if (request->method == METHOD_PUT)
flags.put = 1;
reply_hdr = NULL;
}
- if (data.fd > -1) {
- int fd = data.fd;
- data.fd = -1;
- comm_close(fd);
- }
+ data.close();
if (ctrl.buf) {
memFreeBuf(ctrl.size, ctrl.buf);
debugs(9, 3,HERE);
/* Connection closed; transfer done. */
- if (data.fd > -1) {
- /**
- * Close data socket so it does not occupy resources while
- * we wait.
- */
- comm_close(data.fd);
- data.fd = -1;
- }
+
+ /// Close data channel, if any, to conserve resources while we wait.
+ data.close();
/* expect the "transfer complete" message on the control socket */
/*
return;
}
- /** \par
- * Closes any old FTP-Data connection which may exist. */
- if (ftpState->data.fd >= 0) {
- comm_close(ftpState->data.fd);
- ftpState->data.fd = -1;
- }
+ /// Closes any old FTP-Data connection which may exist. */
+ ftpState->data.close();
/** \par
* Checks for previous EPSV/PASV failures on this server/session.
return;
}
- /*
- * No comm_add_close_handler() here. If we have both ctrl and
- * data FD's call ftpSocketClosed() upon close, then we have
- * to delete the close handler which did NOT get called
- * to prevent ftpSocketClosed() getting called twice.
- * Instead we'll always call comm_close() on the ctrl FD.
- *
- * XXX this should not actually matter if the ftpState is cbdata
- * managed correctly and comm close handlers are cbdata fenced
- */
- ftpState->data.fd = fd;
+ ftpState->data.opened(fd, ftpState->dataCloser());
/** \par
* Send EPSV (ALL,2,1) or PASV on the control channel.
struct addrinfo *AI = NULL;
int on = 1;
int x = 0;
- /*
- * Tear down any old data connection if any. We are about to
- * establish a new one.
- */
- if (ftpState->data.fd > 0) {
- comm_close(ftpState->data.fd);
- ftpState->data.fd = -1;
- }
+ /// Close old data channel, if any. We may open a new one below.
+ ftpState->data.close();
/*
* Set up a listen socket on the same local address as the
return -1;
}
- ftpState->data.fd = fd;
+ ftpState->data.opened(fd, ftpState->dataCloser());
ftpState->data.port = comm_local_port(fd);
ftpState->data.host = NULL;
return fd;
/**\par
* Replace the Listen socket with the accepted data socket */
- comm_close(data.fd);
-
- data.fd = io.nfd;
+ data.close();
+ data.opened(io.nfd, dataCloser());
data.port = io.details.peer.GetPort();
io.details.peer.NtoA(data.host,SQUIDHOSTNAMELEN);
if (ctrl.fd > -1) {
fwd->unregister(ctrl.fd);
- comm_remove_close_handler(ctrl.fd, closeHandler);
- closeHandler = NULL;
- comm_close(ctrl.fd);
- ctrl.fd = -1;
+ ctrl.close();
}
- if (data.fd > -1) {
- comm_close(data.fd);
- data.fd = -1;
- }
+ data.close();
}
/**
fwd->handleUnregisteredServerEnd();
deleteThis("FtpStateData::abortTransaction");
}
+
+/// creates a data channel Comm close callback
+AsyncCall::Pointer
+FtpStateData::dataCloser()
+{
+ typedef CommCbMemFunT<FtpStateData, CommCloseCbParams> Dialer;
+ return asyncCall(9, 5, "FtpStateData::dataClosed",
+ Dialer(this, &FtpStateData::dataClosed));
+}
+
+/// configures the channel with a descriptor and registers a close handler
+void
+FtpChannel::opened(int aFd, const AsyncCall::Pointer &aCloser)
+{
+ assert(fd < 0);
+ assert(closer == NULL);
+
+ assert(aFd >= 0);
+ assert(aCloser != NULL);
+
+ fd = aFd;
+ closer = aCloser;
+ comm_add_close_handler(fd, closer);
+}
+
+/// planned close: removes the close handler and calls comm_close
+void
+FtpChannel::close()
+{
+ if (fd >= 0) {
+ comm_remove_close_handler(fd, closer);
+ closer = NULL;
+ comm_close(fd); // we do not expect to be called back
+ fd = -1;
+ }
+}
+
+/// just resets fd and close handler
+void
+FtpChannel::clear()
+{
+ fd = -1;
+ closer = NULL;
+}