如何在不下载内容的情况下执行 GET 请求



我正在研究链接检查器,通常我可以执行HEAD请求,但是某些站点似乎禁用了此动词,因此在失败时,我还需要执行GET请求(仔细检查链接是否真的死了)

使用以下代码作为我的链接测试器:

public class ValidateResult
{
  public HttpStatusCode? StatusCode { get; set; }
  public Uri RedirectResult { get; set; }
  public WebExceptionStatus? WebExceptionStatus { get; set; }
}

public ValidateResult Validate(Uri uri, bool useHeadMethod = true, 
            bool enableKeepAlive = false, int timeoutSeconds = 30)
{
  ValidateResult result = new ValidateResult();
  HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
  if (useHeadMethod)
  {
    request.Method = "HEAD";
  }
  else
  {
    request.Method = "GET";
  }
  // always compress, if you get back a 404 from a HEAD it can be quite big.
  request.AutomaticDecompression = DecompressionMethods.GZip;
  request.AllowAutoRedirect = false;
  request.UserAgent = UserAgentString;
  request.Timeout = timeoutSeconds * 1000;
  request.KeepAlive = enableKeepAlive;
  HttpWebResponse response = null;
  try
  {
    response = request.GetResponse() as HttpWebResponse;
    result.StatusCode = response.StatusCode;
    if (response.StatusCode == HttpStatusCode.Redirect ||
      response.StatusCode == HttpStatusCode.MovedPermanently ||
      response.StatusCode == HttpStatusCode.SeeOther)
    {
      try
      {
        Uri targetUri = new Uri(Uri, response.Headers["Location"]);
        var scheme = targetUri.Scheme.ToLower();
        if (scheme == "http" || scheme == "https")
        {
          result.RedirectResult = targetUri;
        }
        else
        {
          // this little gem was born out of http://tinyurl.com/18r 
          // redirecting to about:blank
          result.StatusCode = HttpStatusCode.SwitchingProtocols;
          result.WebExceptionStatus = null;
        }
      }
      catch (UriFormatException)
      {
        // another gem... people sometimes redirect to http://nonsense:port/yay
        result.StatusCode = HttpStatusCode.SwitchingProtocols;
        result.WebExceptionStatus = WebExceptionStatus.NameResolutionFailure;
      }
    }
  }
  catch (WebException ex)
  {
    result.WebExceptionStatus = ex.Status;
    response = ex.Response as HttpWebResponse;
    if (response != null)
    {
      result.StatusCode = response.StatusCode;
    }
  }
  finally
  {
    if (response != null)
    {
      response.Close();
    }
  }
  return result;
}

这一切都很好,花花公子。除了当我执行GET请求时,整个有效载荷都会被下载(我在wireshark中观看了这个)。

有没有办法配置基础ServicePointHttpWebRequest根本不缓冲或预先加载响应正文?

(如果我手动编码,我会将 TCP 接收窗口设置得非常低,然后只获取足够的数据包来获取标头,一旦我有足够的信息就停止接收 TCP 数据包。

对于那些想知道这意味着什么的人,我不想在获得 404 时下载 404k 404,这样做在网络上几十万次是昂贵的

当你执行 GET 时,服务器将开始从文件的开头到结尾发送数据。除非你打断它。当然,以 10 Mb/秒的速度,这将是每秒一兆字节,所以如果文件很小,你会得到整个东西。您可以通过几种方式最小化实际下载的数量。

首先,您可以在获得响应后和呼叫response.close之前呼叫request.Abort。这将确保底层代码在关闭响应之前不会尝试下载整个内容。我不知道这对小文件是否有帮助。我知道它会阻止您的应用程序在尝试下载数 GB 文件时挂起。

您可以做的另一件事是请求一个范围,而不是整个文件。请参阅 AddRange 方法及其重载。例如,您可以编写 request.AddRange(512) ,这将仅下载文件的前 512 个字节。当然,这取决于支持范围查询的服务器。大多数人都这样做。但是,大多数也支持HEAD请求。

您可能最终必须编写一个按顺序尝试的方法:

  • 尝试执行 HEAD 请求。如果这有效(即不返回 500),那么您就完成了
  • 尝试使用范围查询进行 GET。如果这没有返回 500,那么您就完成了。
  • 做一个常规的 GET,在GetResponse返回后进行request.Abort

如果您使用的是 GET 请求,无论您是否愿意,您都将收到消息正文。 无论您是否从套接字读取数据,数据仍将传输到终结点。 数据将只在 RecvQ 中排队等待被选中。

为此,如果可能的话,您确实应该使用"HEAD"请求,这将使您免于消息正文。

您不能使用 WebClient 打开流并只读取所需的几个字节吗?

using (var client = new WebClient())
        {
            using (var stream = client.OpenRead(uri))
            {
                const int chunkSize = 100;
                var buffer = new byte[chunkSize];
                int bytesRead;
                while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    //check response here
                }
            }
        }

我不确定WebClient如何在内部打开流。但它似乎允许部分读取数据。

相关内容

最新更新