コード例 #1
0
        /// <summary>
        /// Sends the response to client.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="response">The response.</param>
        private void SendResponseToClient(HttpContext context, WebResponse response)
        {
            context.Response.ClearHeaders();
            context.Response.ClearContent();

            /* You cannot set any cache headers through the API, because it will prevent the use of the cache headers
             * added through the AppendHeader method.
             */

            // add all the headers from the other proxied session to this request
            for (int i = 0; i < response.Headers.Count; i++)
            {
                string name = response.Headers.GetKey(i);

                // don't add any of these headers
                // don't check for restricted response headers because HttpContext doesn't seem to care
                if (name == "Server" ||
                    name == "X-Powered-By" ||
                    name == "Date" ||
                    name == "Host")
                {
                    continue;
                }

                string[] values = response.Headers.GetValues(i);
                if (values.Length == 0)
                {
                    continue;
                }

                if (name == "Location")
                {
                    try
                    {
                        string location = values[0];

                        // make sure location is not empty before creating the URL
                        if (!String.IsNullOrEmpty(location))
                        {
                            // reminder: If location is an absolute URL, the Uri instance is created using only location.
                            var requestLocationUrl  = new Uri(RequestUrl, location);
                            var responseLocationUrl = new UriBuilder(requestLocationUrl);

                            // if the requested location for the host and port is the same as the requested URL we need to update them to the response
                            if (Uri.Compare(requestLocationUrl, RequestUrl, UriComponents.SchemeAndServer, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                responseLocationUrl.Port   = ResponseUrl.Port;
                                responseLocationUrl.Host   = ResponseUrl.Host;
                                responseLocationUrl.Scheme = ResponseUrl.Scheme;

                                string path      = responseLocationUrl.Path;
                                int    pathIndex = path.IndexOf(RequestUrl.AbsolutePath, StringComparison.OrdinalIgnoreCase);

                                // since this is not redirecting to a different server try to replease the path
                                if (pathIndex > -1)
                                {
                                    path = path.Remove(pathIndex, RequestUrl.AbsolutePath.Length);
                                    path = path.Insert(pathIndex, ResponseUrl.AbsolutePath);

                                    responseLocationUrl.Path = path;
                                }
                            }

                            context.Response.AppendHeader(name, responseLocationUrl.Uri.OriginalString);
                        }
                    }
                    catch { /* do nothing on purpose */ }

                    // nothing else can occure just continue processing from next header
                    continue;
                }

                // if this is a chuncked response then we should send it correctly
                if (name == "Transfer-Encoding")
                {
                    /* http://www.go-mono.com/docs/index.aspx?link=P%3aSystem.Web.HttpResponse.Buffer
                     *
                     * This controls whether HttpResponse should buffer the output before it is delivered to a
                     * client. The default is true.
                     *
                     * The buffering can be changed during the execution back and forth if needed. Notice that
                     * changing the buffering state will not flush the current contents held in the output buffer,
                     * the contents will only be flushed out on the next write operation or by manually calling
                     * System.Web.HttpResponse.Flush
                     */
                    context.Response.BufferOutput = false;
                    continue;
                }

                if (name == "Content-Type")
                {
                    /* http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
                     * http://en.wikipedia.org/wiki/Motion_JPEG#M-JPEG_over_HTTP
                     *
                     * The multipart/x-mixed-replace content-type should be treated as a streaming content and shouldn't be buffered.
                     */
                    if (values[0].StartsWith("multipart/x-mixed-replace"))
                    {
                        context.Response.BufferOutput = false;
                    }
                }

                // it is nessisary to get the values for headers that are allowed to specifiy
                // multiple values in an instance (i.e. Set-Cookie)
                foreach (string value in values)
                {
                    context.Response.AppendHeader(name, value);
                }
            }

            Manager.Log(String.Format("Response is {0}being buffered", (context.Response.BufferOutput ? "" : "not ")), "Proxy");

            // add the vanity url to the header
            Manager.TryToAddVanityHeader(new HttpContextWrapper(context));

            // set all HTTP specific protocol stuff
            if (response is HttpWebResponse)
            {
                var httpResponse = response as HttpWebResponse;
                context.Response.StatusCode        = (int)httpResponse.StatusCode;
                context.Response.StatusDescription = httpResponse.StatusDescription;

                Manager.Log(String.Format("Responding '{0} {1}'", ((int)httpResponse.StatusCode), httpResponse.StatusDescription), "Proxy");
            }

            OnResponseToClient(context, response);

            int bufferSize = Manager.Configuration.Rewriter.Proxy.ResponseSize;

            // push the content out to through the stream
            using (var responseStream = response.GetResponseStream())
                using (var bufferStream = new BufferedStream(responseStream, Manager.Configuration.Rewriter.Proxy.BufferSize))
                {
                    byte[] buffer = new byte[bufferSize];

                    try
                    {
                        while (true)
                        {
                            // make sure that the stream can be read from
                            if (!bufferStream.CanRead)
                            {
                                break;
                            }

                            int bytesReturned = bufferStream.Read(buffer, 0, bufferSize);

                            // if not bytes were returned the end of the stream has been reached
                            // and the loop should exit
                            if (bytesReturned == 0)
                            {
                                break;
                            }

                            // write bytes to the response
                            context.Response.OutputStream.Write(buffer, 0, bytesReturned);
                        }
                    }
                    catch (Exception exc)
                    {
                        Manager.Log("Error on response: " + exc.Message, "Proxy");
                    }
                }
        }
コード例 #2
0
        /// <summary>
        /// Sends the response to client.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="response">The response.</param>
        private async Task SendResponseToClient(HttpContext context, HttpResponseMessage response)
        {
            var hostHeader = context.Request.Headers.Get("Host");

            context.Response.ClearHeaders();
            context.Response.ClearContent();

            /* You cannot set any cache headers through the API, because it will prevent the use of the cache headers
             * added through the AppendHeader method.
             */

            // add all the headers from the other proxied session to this request
            foreach (var header in response.Headers)
            {
                var name = header.Key;

                // don't add any of these headers
                // don't check for restricted response headers because HttpContext doesn't seem to care
                if (name == "Server" ||
                    name == "X-Powered-By" ||
                    name == "Date" ||
                    name == "Host")
                {
                    continue;
                }

                string[] values = header.Value.ToArray();
                if (values.Length == 0)
                {
                    continue;
                }

                if (name == "Location")
                {
                    try
                    {
                        string location = values[0];

                        // make sure location is not empty before creating the URL
                        if (!String.IsNullOrEmpty(location))
                        {
                            // reminder: If location is an absolute URL, the Uri instance is created using only location.
                            var requestLocationUrl  = new Uri(RequestUrl, location);
                            var responseLocationUrl = new UriBuilder(requestLocationUrl);

                            // if the requested location for the host and port is the same as the requested URL we need to update them to the response
                            if (Uri.Compare(requestLocationUrl, RequestUrl, UriComponents.SchemeAndServer, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0)
                            {
                                responseLocationUrl.Port   = ResponseUrl.Port;
                                responseLocationUrl.Host   = ResponseUrl.Host;
                                responseLocationUrl.Scheme = ResponseUrl.Scheme;

                                string path      = responseLocationUrl.Path;
                                int    pathIndex = path.IndexOf(RequestUrl.AbsolutePath, StringComparison.OrdinalIgnoreCase);

                                // since this is not redirecting to a different server try to replease the path
                                if (pathIndex > -1)
                                {
                                    path = path.Remove(pathIndex, RequestUrl.AbsolutePath.Length);
                                    path = path.Insert(pathIndex, ResponseUrl.AbsolutePath);

                                    responseLocationUrl.Path = path;
                                }
                            }

                            context.Response.AppendHeader(name, responseLocationUrl.Uri.OriginalString);
                        }
                    }
                    catch { /* do nothing on purpose */ }

                    // nothing else can occure just continue processing from next header
                    continue;
                }

                // if this is a chuncked response then we should send it correctly
                if (name == "Transfer-Encoding")
                {
                    /* http://www.go-mono.com/docs/index.aspx?link=P%3aSystem.Web.HttpResponse.Buffer
                     *
                     * This controls whether HttpResponse should buffer the output before it is delivered to a
                     * client. The default is true.
                     *
                     * The buffering can be changed during the execution back and forth if needed. Notice that
                     * changing the buffering state will not flush the current contents held in the output buffer,
                     * the contents will only be flushed out on the next write operation or by manually calling
                     * System.Web.HttpResponse.Flush
                     */
                    context.Response.BufferOutput = false;
                    continue;
                }

                if (name == "Content-Type")
                {
                    /* http://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
                     * http://en.wikipedia.org/wiki/Motion_JPEG#M-JPEG_over_HTTP
                     *
                     * The multipart/x-mixed-replace content-type should be treated as a streaming content and shouldn't be buffered.
                     */
                    if (values[0].StartsWith("multipart/x-mixed-replace"))
                    {
                        context.Response.BufferOutput = false;
                    }
                }

                // it is nessisary to get the values for headers that are allowed to specifiy
                // multiple values in an instance (i.e. Set-Cookie)
                foreach (string value in values)
                {
                    context.Response.AppendHeader(name, value);
                }
            }

            context.Response.AppendHeader("Host", hostHeader);

            Manager.Log(String.Format("Response is {0}being buffered", (context.Response.BufferOutput ? "" : "not ")), "Proxy");

            // add the vanity url to the header
            Manager.TryToAddVanityHeader(new HttpContextWrapper(context));

            // set all HTTP specific protocol stuff
            context.Response.StatusCode = (int)response.StatusCode;

            // for not-modified responses the content type will not be returned so don't try and set it.
            if (response.Content.Headers.ContentType != null)
            {
                context.Response.ContentType = response.Content.Headers.ContentType.MediaType;
            }

            Manager.Log(String.Format("Responding '{0}'", ((int)response.StatusCode)), "Proxy");

            await OnResponseToClient(context, response);

            await response.Content.CopyToAsync(context.Response.OutputStream);
        }
コード例 #3
0
        /// <summary>
        /// Runs the rules.
        /// </summary>
        /// <param name="httpContext">The HTTP context.</param>
        /// <param name="url">The URL.</param>
        /// <returns>
        /// Returns a rewritten <see cref="System.Uri"/>, or a value of <see langword="null"/> if no rewriting was done to <paramref name="url"/>.
        /// </returns>
        public Uri RunRules(HttpContextBase httpContext, Uri url)
        {
            var context    = new RuleSetContext(this, url, httpContext);
            var currentUrl = url;

            if (!EngineEnabled)
            {
                Manager.LogIf(!EngineEnabled && LogLevel >= 9, "Rewrite Engine Is DISABLED", "Rewrite");
            }

            if (_rules.Count > 0 && EngineEnabled)
            {
                Manager.LogIf(LogLevel >= 1, "**********************************************************************************");
                Manager.LogIf(LogLevel >= 1, "Input: " + currentUrl, "Rewrite");

                // check if max number of internal transfers have been exceeded
                if (InternalTransferCount(httpContext) > MaxInternalTransfers)
                {
                    string message = "Exceeded the max number of internal transfers.";
                    Manager.LogIf(LogLevel >= 1, message, "Error");
                    throw new HttpException(500, message);
                }

                var temporyFlags  = (IRuleFlagProcessor)null;
                var skipNextChain = false;
                var initialUrl    = currentUrl;

                if (!String.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
                {
                    currentUrl = RemoveBase(VirtualBase, currentUrl);
                }

                // process rules according to their settings
                for (int i = 0; i < _rules.Count; i++)
                {
                    var ruleContext = new RuleContext(i, context, currentUrl, _rules[i]);
                    temporyFlags = _rules[i].Flags;

                    // continue if this rule shouldn't be processed because it doesn't allow internal transfer requests
                    if (RuleFlagsProcessor.HasNotForInternalSubRequests(temporyFlags) && IsInternalTransfer(httpContext))
                    {
                        continue;
                    }

                    bool containsChain         = RuleFlagsProcessor.HasChain(_rules[i].Flags);
                    bool previousContainsChain = RuleFlagsProcessor.HasChain(_rules[Math.Max(0, i - 1)].Flags);

                    // if the previous rule doesn't contain a chain flag then set the initial URL
                    // this will be used to reset a chain if one of the chain rules fail
                    if (!previousContainsChain)
                    {
                        initialUrl = currentUrl;
                    }

                    // skip if the current rule or the last rule has a chain flag
                    // and if the skip next chain is set
                    if (skipNextChain && (previousContainsChain || containsChain))
                    {
                        continue;
                    }
                    else
                    {
                        skipNextChain = false;
                    }

                    if (_rules[i].TryExecute(ruleContext))
                    {
                        var flagResponse = temporyFlags.Apply(ruleContext);
                        currentUrl = ruleContext.SubstitutedUrl;
                        i          = ruleContext.RuleIndex ?? -1;
                        bool breakLoop = false;

                        // apply the flags to the rules, and only do special processing
                        // for the flag responses listed in the switch statement below
                        switch (flagResponse)
                        {
                        case RuleFlagProcessorResponse.ExitRuleSet:
                            return(null);

                        case RuleFlagProcessorResponse.LastRule:
                            breakLoop = true;
                            break;
                        }

                        // break the loop because we have reached the last rule as indicated by a flag
                        if (breakLoop)
                        {
                            break;
                        }
                    }
                    else if (containsChain)
                    {
                        skipNextChain = true;

                        // reset the current URL back to the initial URL from the start of the chain
                        currentUrl = initialUrl;
                    }
                    else if (previousContainsChain)
                    {
                        // reset the current URL back to the initial URL from the start of the chain
                        currentUrl = initialUrl;
                    }
                }

                // if the scheme, host, and ports do not match on the request vs the rewrite a redirect needs to be performed instead of a rewrite
                if (Uri.Compare(currentUrl, context.RequestedUrl, UriComponents.SchemeAndServer, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) != 0)
                {
                    Manager.LogIf(LogLevel >= 1, "Output: 302 Redirect to " + currentUrl, "Rewrite");
                    Manager.Redirect(httpContext, "found", currentUrl);
                }

                if (!String.IsNullOrEmpty(VirtualBase) && VirtualBase != "/")
                {
                    currentUrl = AddBase(VirtualBase, currentUrl);
                }

                Manager.LogIf(LogLevel >= 1, "Output: " + currentUrl, "Rewrite");
                Manager.LogIf(LogLevel >= 1, "**********************************************************************************");

                Manager.TryToAddXRewriteUrlHeader(httpContext);
                Manager.TryToAddVanityHeader(httpContext);
            }

            // if the http request url matches for both the request and the rewrite no work was done so the url should be null
            if (Uri.Compare(currentUrl, url, UriComponents.HttpRequestUrl, UriFormat.SafeUnescaped, StringComparison.OrdinalIgnoreCase) == 0)
            {
                currentUrl = null;
            }

            return(currentUrl);
        }