c# .net httpwebrequest servicepoint

c# - ¿Cómo puedo realizar una solicitud GET sin descargar el contenido?



.net httpwebrequest (3)

¿No podría utilizar un WebClient para abrir un flujo y leer solo los pocos bytes que necesita?

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 } } }

No estoy seguro de cómo WebClient abre la transmisión internamente. Pero parece permitir la lectura parcial de los datos.

Estoy trabajando en un verificador de enlaces, en general puedo realizar solicitudes HEAD , sin embargo, algunos sitios parecen deshabilitar este verbo, por lo que si falla también debo realizar una solicitud GET (para verificar que el enlace está realmente muerto)

Uso el siguiente código como comprobador de enlaces:

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; }

Todo esto funciona bien y dandy. Excepto que cuando realizo una solicitud GET , toda la carga útil se descarga (lo vi en Wirehark).

¿Hay alguna forma de configurar el ServicePoint subyacente o el HttpWebRequest para que no HttpWebRequest en búfer o esté ansioso por cargar el cuerpo de la respuesta?

(Si estuviera codificando manualmente esto, establecería la ventana de recepción de TCP realmente baja, y luego solo obtendré los paquetes suficientes para obtener los encabezados, dejar de activar los paquetes de TCP tan pronto como tenga suficiente información).

para aquellos que se preguntan qué se pretende lograr con esto, no quiero descargar un 40k 404 cuando obtengo un 404, hacer esto unos cientos de miles de veces es caro en la red


Cuando realice un GET, el servidor comenzará a enviar datos desde el principio del archivo hasta el final. A menos que lo interrumpas. Por supuesto, a 10 Mb / s, será un megabyte por segundo, por lo que si el archivo es pequeño obtendrás todo. Puede minimizar la cantidad que realmente descarga de un par de maneras.

Primero, puede llamar a request.Abort después de recibir la respuesta y antes de llamar a response.close . Eso asegurará que el código subyacente no intente descargar todo antes de cerrar la respuesta. Si esto ayuda en archivos pequeños, no lo sé. Sé que evitará que su aplicación se bloquee cuando intente descargar un archivo de varios gigabytes.

La otra cosa que puede hacer es solicitar un rango, en lugar de todo el archivo. Ver el método AddRange y sus sobrecargas. Podría, por ejemplo, escribir request.AddRange(512) , que descargaría solo los primeros 512 bytes del archivo. Esto depende, por supuesto, del servidor que admita las consultas de rango. Debe hacerse. Pero entonces, la mayoría de las solicitudes HEAD de soporte, también.

Probablemente terminarás teniendo que escribir un método que pruebe cosas en secuencia:

  • tratar de hacer una solicitud HEAD. Si eso funciona (es decir, no devuelve un 500), entonces has terminado
  • intente GET con una consulta de rango. Si eso no devuelve un 500, entonces has terminado.
  • realice un GET normal, con una request.Abort GetResponse después de que GetResponse devuelva.

Si está utilizando una solicitud GET, recibirá el cuerpo del mensaje si desea o no. Los datos se seguirán transmitiendo a su punto final independientemente de si los leyó desde el socket o no. Los datos se mantendrán en cola en el RecvQ a la espera de ser seleccionados.

Para esto, realmente debería utilizar una solicitud "HEAD" si es posible, lo que le ahorrará el cuerpo del mensaje.