/// <summary>Processes the response (possibly updating or inserting) Content-Length and Transfer-Encoding headers. /// </summary> /// <remarks>Processes the response (possibly updating or inserting) Content-Length and Transfer-Encoding headers. /// </remarks> /// <param name="response">The HttpResponse to modify.</param> /// <param name="context">Unused.</param> /// <exception cref="Org.Apache.Http.ProtocolException">If either the Content-Length or Transfer-Encoding headers are found. /// </exception> /// <exception cref="System.ArgumentException">If the response is null.</exception> /// <exception cref="Org.Apache.Http.HttpException"></exception> /// <exception cref="System.IO.IOException"></exception> public virtual void Process(HttpResponse response, HttpContext context) { Args.NotNull(response, "HTTP response"); if (this.overwrite) { response.RemoveHeaders(HTTP.TransferEncoding); response.RemoveHeaders(HTTP.ContentLen); } else { if (response.ContainsHeader(HTTP.TransferEncoding)) { throw new ProtocolException("Transfer-encoding header already present"); } if (response.ContainsHeader(HTTP.ContentLen)) { throw new ProtocolException("Content-Length header already present"); } } ProtocolVersion ver = response.GetStatusLine().GetProtocolVersion(); HttpEntity entity = response.GetEntity(); if (entity != null) { long len = entity.GetContentLength(); if (entity.IsChunked() && !ver.LessEquals(HttpVersion.Http10)) { response.AddHeader(HTTP.TransferEncoding, HTTP.ChunkCoding); } else { if (len >= 0) { response.AddHeader(HTTP.ContentLen, System.Convert.ToString(entity.GetContentLength ())); } } // Specify a content type if known if (entity.GetContentType() != null && !response.ContainsHeader(HTTP.ContentType)) { response.AddHeader(entity.GetContentType()); } // Specify a content encoding if known if (entity.GetContentEncoding() != null && !response.ContainsHeader(HTTP.ContentEncoding )) { response.AddHeader(entity.GetContentEncoding()); } } else { int status = response.GetStatusLine().GetStatusCode(); if (status != HttpStatus.ScNoContent && status != HttpStatus.ScNotModified && status != HttpStatus.ScResetContent) { response.AddHeader(HTTP.ContentLen, "0"); } } }
/// <summary>Send the given request over the given connection.</summary> /// <remarks> /// Send the given request over the given connection. /// <p> /// This method also handles the expect-continue handshake if necessary. /// If it does not have to handle an expect-continue handshake, it will /// not use the connection for reading or anything else that depends on /// data coming in over the connection. /// </remarks> /// <param name="request"> /// the request to send, already /// <see cref="PreProcess(Org.Apache.Http.IHttpRequest, HttpProcessor, HttpContext)">preprocessed /// </see> /// </param> /// <param name="conn"> /// the connection over which to send the request, /// already established /// </param> /// <param name="context">the context for sending the request</param> /// <returns> /// a terminal response received as part of an expect-continue /// handshake, or /// <code>null</code> if the expect-continue handshake is not used /// </returns> /// <exception cref="System.IO.IOException">in case of an I/O error.</exception> /// <exception cref="Org.Apache.Http.HttpException"> /// in case of HTTP protocol violation or a processing /// problem. /// </exception> protected internal virtual HttpResponse DoSendRequest(IHttpRequest request, HttpClientConnection conn, HttpContext context) { Args.NotNull(request, "HTTP request"); Args.NotNull(conn, "Client connection"); Args.NotNull(context, "HTTP context"); HttpResponse response = null; context.SetAttribute(HttpCoreContext.HttpConnection, conn); context.SetAttribute(HttpCoreContext.HttpReqSent, false); conn.SendRequestHeader(request); if (request is HttpEntityEnclosingRequest) { // Check for expect-continue handshake. We have to flush the // headers and wait for an 100-continue response to handle it. // If we get a different response, we must not send the entity. bool sendentity = true; ProtocolVersion ver = request.GetRequestLine().GetProtocolVersion(); if (((HttpEntityEnclosingRequest)request).ExpectContinue() && !ver.LessEquals(HttpVersion .Http10)) { conn.Flush(); // As suggested by RFC 2616 section 8.2.3, we don't wait for a // 100-continue response forever. On timeout, send the entity. if (conn.IsResponseAvailable(this.waitForContinue)) { response = conn.ReceiveResponseHeader(); if (CanResponseHaveBody(request, response)) { conn.ReceiveResponseEntity(response); } int status = response.GetStatusLine().GetStatusCode(); if (status < 200) { if (status != HttpStatus.ScContinue) { throw new ProtocolException("Unexpected response: " + response.GetStatusLine()); } // discard 100-continue response = null; } else { sendentity = false; } } } if (sendentity) { conn.SendRequestEntity((HttpEntityEnclosingRequest)request); } } conn.Flush(); context.SetAttribute(HttpCoreContext.HttpReqSent, true); return(response); }
/// <exception cref="Org.Apache.Http.HttpException"></exception> /// <exception cref="System.IO.IOException"></exception> public virtual void Process(HttpResponse response, HttpContext context) { Args.NotNull(response, "HTTP response"); HttpCoreContext corecontext = HttpCoreContext.Adapt(context); // Always drop connection after certain type of responses int status = response.GetStatusLine().GetStatusCode(); if (status == HttpStatus.ScBadRequest || status == HttpStatus.ScRequestTimeout || status == HttpStatus.ScLengthRequired || status == HttpStatus.ScRequestTooLong || status == HttpStatus.ScRequestUriTooLong || status == HttpStatus.ScServiceUnavailable || status == HttpStatus.ScNotImplemented) { response.SetHeader(HTTP.ConnDirective, HTTP.ConnClose); return; } Header _explicit = response.GetFirstHeader(HTTP.ConnDirective); if (_explicit != null && Sharpen.Runtime.EqualsIgnoreCase(HTTP.ConnClose, _explicit .GetValue())) { // Connection persistence _explicitly disabled return; } // Always drop connection for HTTP/1.0 responses and below // if the content body cannot be correctly delimited HttpEntity entity = response.GetEntity(); if (entity != null) { ProtocolVersion ver = response.GetStatusLine().GetProtocolVersion(); if (entity.GetContentLength() < 0 && (!entity.IsChunked() || ver.LessEquals(HttpVersion .Http10))) { response.SetHeader(HTTP.ConnDirective, HTTP.ConnClose); return; } } // Drop connection if requested by the client or request was <= 1.0 IHttpRequest request = corecontext.GetRequest(); if (request != null) { Header header = request.GetFirstHeader(HTTP.ConnDirective); if (header != null) { response.SetHeader(HTTP.ConnDirective, header.GetValue()); } else { if (request.GetProtocolVersion().LessEquals(HttpVersion.Http10)) { response.SetHeader(HTTP.ConnDirective, HTTP.ConnClose); } } } }
/// <exception cref="Org.Apache.Http.HttpException"></exception> /// <exception cref="System.IO.IOException"></exception> public virtual void Process(IHttpRequest request, HttpContext context) { Args.NotNull(request, "HTTP request"); HttpCoreContext corecontext = HttpCoreContext.Adapt(context); ProtocolVersion ver = request.GetRequestLine().GetProtocolVersion(); string method = request.GetRequestLine().GetMethod(); if (Sharpen.Runtime.EqualsIgnoreCase(method, "CONNECT") && ver.LessEquals(HttpVersion .Http10)) { return; } if (!request.ContainsHeader(HTTP.TargetHost)) { HttpHost targethost = corecontext.GetTargetHost(); if (targethost == null) { HttpConnection conn = corecontext.GetConnection(); if (conn is HttpInetConnection) { // Populate the context with a default HTTP host based on the // inet address of the target host IPAddress address = ((HttpInetConnection)conn).GetRemoteAddress(); int port = ((HttpInetConnection)conn).GetRemotePort(); if (address != null) { targethost = new HttpHost(address.GetHostName(), port); } } if (targethost == null) { if (ver.LessEquals(HttpVersion.Http10)) { return; } else { throw new ProtocolException("Target host missing"); } } } request.AddHeader(HTTP.TargetHost, targethost.ToHostString()); } }
/// <exception cref="Org.Apache.Http.HttpException"></exception> /// <exception cref="System.IO.IOException"></exception> public virtual void Process(IHttpRequest request, HttpContext context) { Args.NotNull(request, "HTTP request"); if (request is HttpEntityEnclosingRequest) { if (this.overwrite) { request.RemoveHeaders(HTTP.TransferEncoding); request.RemoveHeaders(HTTP.ContentLen); } else { if (request.ContainsHeader(HTTP.TransferEncoding)) { throw new ProtocolException("Transfer-encoding header already present"); } if (request.ContainsHeader(HTTP.ContentLen)) { throw new ProtocolException("Content-Length header already present"); } } ProtocolVersion ver = request.GetRequestLine().GetProtocolVersion(); HttpEntity entity = ((HttpEntityEnclosingRequest)request).GetEntity(); if (entity == null) { request.AddHeader(HTTP.ContentLen, "0"); return; } // Must specify a transfer encoding or a content length if (entity.IsChunked() || entity.GetContentLength() < 0) { if (ver.LessEquals(HttpVersion.Http10)) { throw new ProtocolException("Chunked transfer encoding not allowed for " + ver); } request.AddHeader(HTTP.TransferEncoding, HTTP.ChunkCoding); } else { request.AddHeader(HTTP.ContentLen, System.Convert.ToString(entity.GetContentLength ())); } // Specify a content type if known if (entity.GetContentType() != null && !request.ContainsHeader(HTTP.ContentType)) { request.AddHeader(entity.GetContentType()); } // Specify a content encoding if known if (entity.GetContentEncoding() != null && !request.ContainsHeader(HTTP.ContentEncoding )) { request.AddHeader(entity.GetContentEncoding()); } } }
/// <exception cref="Org.Apache.Http.HttpException"></exception> /// <exception cref="System.IO.IOException"></exception> public virtual void Process(IHttpRequest request, HttpContext context) { Args.NotNull(request, "HTTP request"); if (!request.ContainsHeader(HTTP.ExpectDirective)) { if (request is HttpEntityEnclosingRequest) { ProtocolVersion ver = request.GetRequestLine().GetProtocolVersion(); HttpEntity entity = ((HttpEntityEnclosingRequest)request).GetEntity(); // Do not send the expect header if request body is known to be empty if (entity != null && entity.GetContentLength() != 0 && !ver.LessEquals(HttpVersion .Http10)) { bool active = request.GetParams().GetBooleanParameter(CoreProtocolPNames.UseExpectContinue , this.activeByDefault); if (active) { request.AddHeader(HTTP.ExpectDirective, HTTP.ExpectContinue); } } } } }
/// <exception cref="Apache.Http.HttpException"></exception> /// <exception cref="System.IO.IOException"></exception> public virtual void Process(IHttpRequest request, HttpContext context) { Args.NotNull(request, "HTTP request"); if (!request.ContainsHeader(HTTP.ExpectDirective)) { if (request is HttpEntityEnclosingRequest) { ProtocolVersion ver = request.GetRequestLine().GetProtocolVersion(); HttpEntity entity = ((HttpEntityEnclosingRequest)request).GetEntity(); // Do not send the expect header if request body is known to be empty if (entity != null && entity.GetContentLength() != 0 && !ver.LessEquals(HttpVersion .Http10)) { HttpClientContext clientContext = ((HttpClientContext)HttpClientContext.Adapt(context )); RequestConfig config = clientContext.GetRequestConfig(); if (config.IsExpectContinueEnabled()) { request.AddHeader(HTTP.ExpectDirective, HTTP.ExpectContinue); } } } } }
// see interface ConnectionReuseStrategy public virtual bool KeepAlive(HttpResponse response, HttpContext context) { Args.NotNull(response, "HTTP response"); Args.NotNull(context, "HTTP context"); // Check for a self-terminating entity. If the end of the entity will // be indicated by closing the connection, there is no keep-alive. ProtocolVersion ver = response.GetStatusLine().GetProtocolVersion(); Header teh = response.GetFirstHeader(HTTP.TransferEncoding); if (teh != null) { if (!Sharpen.Runtime.EqualsIgnoreCase(HTTP.ChunkCoding, teh.GetValue())) { return(false); } } else { if (CanResponseHaveBody(response)) { Header[] clhs = response.GetHeaders(HTTP.ContentLen); // Do not reuse if not properly content-length delimited if (clhs.Length == 1) { Header clh = clhs[0]; try { int contentLen = System.Convert.ToInt32(clh.GetValue()); if (contentLen < 0) { return(false); } } catch (FormatException) { return(false); } } else { return(false); } } } // Check for the "Connection" header. If that is absent, check for // the "Proxy-Connection" header. The latter is an unspecified and // broken but unfortunately common extension of HTTP. HeaderIterator hit = response.HeaderIterator(HTTP.ConnDirective); if (!hit.HasNext()) { hit = response.HeaderIterator("Proxy-Connection"); } // Experimental usage of the "Connection" header in HTTP/1.0 is // documented in RFC 2068, section 19.7.1. A token "keep-alive" is // used to indicate that the connection should be persistent. // Note that the final specification of HTTP/1.1 in RFC 2616 does not // include this information. Neither is the "Connection" header // mentioned in RFC 1945, which informally describes HTTP/1.0. // // RFC 2616 specifies "close" as the only connection token with a // specific meaning: it disables persistent connections. // // The "Proxy-Connection" header is not formally specified anywhere, // but is commonly used to carry one token, "close" or "keep-alive". // The "Connection" header, on the other hand, is defined as a // sequence of tokens, where each token is a header name, and the // token "close" has the above-mentioned additional meaning. // // To get through this mess, we treat the "Proxy-Connection" header // in exactly the same way as the "Connection" header, but only if // the latter is missing. We scan the sequence of tokens for both // "close" and "keep-alive". As "close" is specified by RFC 2068, // it takes precedence and indicates a non-persistent connection. // If there is no "close" but a "keep-alive", we take the hint. if (hit.HasNext()) { try { TokenIterator ti = CreateTokenIterator(hit); bool keepalive = false; while (ti.HasNext()) { string token = ti.NextToken(); if (Sharpen.Runtime.EqualsIgnoreCase(HTTP.ConnClose, token)) { return(false); } else { if (Sharpen.Runtime.EqualsIgnoreCase(HTTP.ConnKeepAlive, token)) { // continue the loop, there may be a "close" afterwards keepalive = true; } } } if (keepalive) { return(true); } } catch (ParseException) { // neither "close" nor "keep-alive", use default policy // invalid connection header means no persistent connection // we don't have logging in HttpCore, so the exception is lost return(false); } } // default since HTTP/1.1 is persistent, before it was non-persistent return(!ver.LessEquals(HttpVersion.Http10)); }