I just tested this on one of our development servers and there really is a CWD released by .NET FtpWebRequest:
new connection from 172.16.3.210 on 172.16.3.210:21 (Explicit SSL)
hostname resolved: devpc
sending welcome message.
220 Gene6 FTP Server v3.10.0 (Build 2) ready ...
USER testuser
testuser, 331 Password required for testuser.
testuser, PASS ****
testuser, logged in as "testuser".
testuser, 230 User testuser logged in.
testuser, OPTS utf8 on
testuser, 501 Please CLNT first.
testuser, PWD
testuser, 257 "/" is current directory.
testuser, CWD /
testuser, change directory '/' -> 'D: \ testfolder' -> Access allowed.
testuser, 250 CWD command successful. "/" is current directory.
testuser, type I
testuser, 200 Type set to I.
testuser, PORT 172,16,3,210,4,127
testuser, 200 Port command successful.
testuser, NLST
testuser, 150 Opening data connection for directory list.
testuser, 226 Transfer ok.
testuser, 421 Connection closed, timed out.
testuser, disconnected. (00d00: 05: 01)
This was even without specifying "/" in the uri when creating the FtpWebRequest object.
If you are debugging or viewing source code, the "FtpControlStream" class comes into play. See Call Stack:
System.dll! System.Net.FtpControlStream.BuildCommandsList (System.Net.WebRequest req) Line 555 C #
System.dll! System.Net.CommandStream.SubmitRequest (System.Net.WebRequest request =
{System.Net.FtpWebRequest}, bool async = false, bool readInitalResponseOnConnect = true) Line 143 C #
System.dll! System.Net.FtpWebRequest.TimedSubmitRequestHelper (bool async) Line 1122 + 0x13 bytes C #
System.dll! System.Net.FtpWebRequest.SubmitRequest (bool async = false) Line 1042 + 0xc bytes C #
System.dll! System.Net.FtpWebRequest.GetResponse () Line 649 C #
The BuildCommandsList () method is called here. BuildCommandsList () creates a list of commands to send to the FTP server. This method has the following code snippet:
if (m_PreviousServerPath != newServerPath) { if (!m_IsRootPath && m_LoginState == FtpLoginState.LoggedIn && m_LoginDirectory != null) { newServerPath = m_LoginDirectory+newServerPath; } m_NewServerPath = newServerPath; commandList.Add(new PipelineEntry(FormatFtpCommand("CWD", newServerPath), PipelineEntryFlags.UserCommand)); }
When connecting to the server for the first time, m_PreviousServerPath is always null, the value of newServerPath is "/" and calculated by a function named GetPathAndFileName () (several lines are called before this code block). GetPathAndFileName () evaluates newServerPath as "/" if no path is specified, or if "/" is explicitly attached to the end of "ftp: // ...." uri.
Thus, this ultimately leads to the addition of the CWD command to the command pipeline, because null! = "/".
In a nutshell, unfortunately, you cannot override this behavior because it is burned in the source.