/FluentFTP/Proxy/FtpClientHttp11Proxy.cs

https://github.com/AliShahbazi124/FluentFTP · C# · 214 lines · 145 code · 36 blank · 33 comment · 21 complexity · df624f7248cbf7bfddc89b373278aa79 MD5 · raw file

  1. using System;
  2. using System.IO;
  3. using System.Text.RegularExpressions;
  4. using System.Threading;
  5. #if ASYNC
  6. using System.Threading.Tasks;
  7. #endif
  8. namespace FluentFTP.Proxy {
  9. /// <summary> A FTP client with a HTTP 1.1 proxy implementation. </summary>
  10. public class FtpClientHttp11Proxy : FtpClientProxy {
  11. /// <summary> A FTP client with a HTTP 1.1 proxy implementation </summary>
  12. /// <param name="proxy">Proxy information</param>
  13. public FtpClientHttp11Proxy(ProxyInfo proxy)
  14. : base(proxy) {
  15. ConnectionType = "HTTP 1.1 Proxy";
  16. }
  17. /// <summary> Redefine the first dialog: HTTP Frame for the HTTP 1.1 Proxy </summary>
  18. protected override void Handshake() {
  19. var proxyConnectionReply = GetReply();
  20. if (!proxyConnectionReply.Success) {
  21. throw new FtpException("Can't connect " + Host + " via proxy " + Proxy.Host + ".\nMessage : " +
  22. proxyConnectionReply.ErrorMessage);
  23. }
  24. // TO TEST: if we are able to detect the actual FTP server software from this reply
  25. HandshakeReply = proxyConnectionReply;
  26. }
  27. /// <summary>
  28. /// Creates a new instance of this class. Useful in FTP proxy classes.
  29. /// </summary>
  30. protected override FtpClient Create() {
  31. return new FtpClientHttp11Proxy(Proxy);
  32. }
  33. /// <summary>
  34. /// Connects to the server using an existing <see cref="FtpSocketStream"/>
  35. /// </summary>
  36. /// <param name="stream">The existing socket stream</param>
  37. protected override void Connect(FtpSocketStream stream) {
  38. Connect(stream, Host, Port, FtpIpVersion.ANY);
  39. }
  40. #if ASYNC
  41. /// <summary>
  42. /// Connects to the server using an existing <see cref="FtpSocketStream"/>
  43. /// </summary>
  44. /// <param name="stream">The existing socket stream</param>
  45. protected override Task ConnectAsync(FtpSocketStream stream, CancellationToken token)
  46. {
  47. return ConnectAsync(stream, Host, Port, FtpIpVersion.ANY, token);
  48. }
  49. #endif
  50. /// <summary>
  51. /// Connects to the server using an existing <see cref="FtpSocketStream"/>
  52. /// </summary>
  53. /// <param name="stream">The existing socket stream</param>
  54. /// <param name="host">Host name</param>
  55. /// <param name="port">Port number</param>
  56. /// <param name="ipVersions">IP version to use</param>
  57. protected override void Connect(FtpSocketStream stream, string host, int port, FtpIpVersion ipVersions) {
  58. base.Connect(stream);
  59. var writer = new StreamWriter(stream);
  60. writer.WriteLine("CONNECT {0}:{1} HTTP/1.1", host, port);
  61. writer.WriteLine("Host: {0}:{1}", host, port);
  62. if (Proxy.Credentials != null) {
  63. var credentialsHash = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(Proxy.Credentials.UserName + ":" + Proxy.Credentials.Password));
  64. writer.WriteLine("Proxy-Authorization: Basic " + credentialsHash);
  65. }
  66. writer.WriteLine("User-Agent: custom-ftp-client");
  67. writer.WriteLine();
  68. writer.Flush();
  69. ProxyHandshake(stream);
  70. }
  71. #if ASYNC
  72. /// <summary>
  73. /// Connects to the server using an existing <see cref="FtpSocketStream"/>
  74. /// </summary>
  75. /// <param name="stream">The existing socket stream</param>
  76. /// <param name="host">Host name</param>
  77. /// <param name="port">Port number</param>
  78. /// <param name="ipVersions">IP version to use</param>
  79. /// <param name="token">IP version to use</param>
  80. protected override async Task ConnectAsync(FtpSocketStream stream, string host, int port, FtpIpVersion ipVersions, CancellationToken token)
  81. {
  82. await base.ConnectAsync(stream,token);
  83. var writer = new StreamWriter(stream);
  84. await writer.WriteLineAsync(string.Format("CONNECT {0}:{1} HTTP/1.1", host, port));
  85. await writer.WriteLineAsync(string.Format("Host: {0}:{1}", host, port));
  86. if (Proxy.Credentials != null)
  87. {
  88. var credentialsHash = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(Proxy.Credentials.UserName + ":" + Proxy.Credentials.Password));
  89. await writer.WriteLineAsync("Proxy-Authorization: Basic " + credentialsHash);
  90. }
  91. await writer.WriteLineAsync("User-Agent: custom-ftp-client");
  92. await writer.WriteLineAsync();
  93. await writer.FlushAsync();
  94. await ProxyHandshakeAsync(stream, token);
  95. }
  96. #endif
  97. private void ProxyHandshake(FtpSocketStream stream) {
  98. var proxyConnectionReply = GetProxyReply(stream);
  99. if (!proxyConnectionReply.Success)
  100. throw new FtpException("Can't connect " + Host + " via proxy " + Proxy.Host + ".\nMessage : " + proxyConnectionReply.ErrorMessage);
  101. }
  102. #if ASYNC
  103. private async Task ProxyHandshakeAsync(FtpSocketStream stream, CancellationToken token = default(CancellationToken))
  104. {
  105. var proxyConnectionReply = await GetProxyReplyAsync(stream, token);
  106. if (!proxyConnectionReply.Success)
  107. throw new FtpException("Can't connect " + Host + " via proxy " + Proxy.Host + ".\nMessage : " + proxyConnectionReply.ErrorMessage);
  108. }
  109. #endif
  110. private FtpReply GetProxyReply(FtpSocketStream stream) {
  111. FtpReply reply = new FtpReply();
  112. string buf;
  113. #if !CORE14
  114. lock (Lock) {
  115. #endif
  116. if (!IsConnected) {
  117. throw new InvalidOperationException("No connection to the server has been established.");
  118. }
  119. stream.ReadTimeout = ReadTimeout;
  120. while ((buf = stream.ReadLine(Encoding)) != null) {
  121. Match m;
  122. this.LogLine(FtpTraceLevel.Info, buf);
  123. if ((m = Regex.Match(buf, @"^HTTP/.*\s(?<code>[0-9]{3}) (?<message>.*)$")).Success) {
  124. reply.Code = m.Groups["code"].Value;
  125. reply.Message = m.Groups["message"].Value;
  126. break;
  127. }
  128. reply.InfoMessages += (buf + "\n");
  129. }
  130. // fixes #84 (missing bytes when downloading/uploading files through proxy)
  131. while ((buf = stream.ReadLine(Encoding)) != null) {
  132. this.LogLine(FtpTraceLevel.Info, buf);
  133. if (FtpExtensions.IsNullOrWhiteSpace(buf)) {
  134. break;
  135. }
  136. reply.InfoMessages += (buf + "\n");
  137. }
  138. #if !CORE14
  139. }
  140. #endif
  141. return reply;
  142. }
  143. #if ASYNC
  144. private async Task<FtpReply> GetProxyReplyAsync(FtpSocketStream stream, CancellationToken token = default(CancellationToken))
  145. {
  146. FtpReply reply = new FtpReply();
  147. string buf;
  148. if (!IsConnected){
  149. throw new InvalidOperationException("No connection to the server has been established.");
  150. }
  151. stream.ReadTimeout = ReadTimeout;
  152. while ((buf = await stream.ReadLineAsync(Encoding, token)) != null)
  153. {
  154. Match m;
  155. this.LogLine(FtpTraceLevel.Info, buf);
  156. if ((m = Regex.Match(buf, @"^HTTP/.*\s(?<code>[0-9]{3}) (?<message>.*)$")).Success)
  157. {
  158. reply.Code = m.Groups["code"].Value;
  159. reply.Message = m.Groups["message"].Value;
  160. break;
  161. }
  162. reply.InfoMessages += (buf + "\n");
  163. }
  164. // fixes #84 (missing bytes when downloading/uploading files through proxy)
  165. while ((buf = await stream.ReadLineAsync(Encoding, token)) != null)
  166. {
  167. this.LogLine(FtpTraceLevel.Info, buf);
  168. if (FtpExtensions.IsNullOrWhiteSpace(buf))
  169. {
  170. break;
  171. }
  172. reply.InfoMessages += (buf + "\n");
  173. }
  174. return reply;
  175. }
  176. #endif
  177. }
  178. }