OData Web API 2 PATCH not working

lvmcastro's Avatar

lvmcastro

17 Jul, 2016 06:32 PM

Hi,

When running the Web Application in visual studios IIS everything works as expected

When deployed to appharbour, GET with Token authentication continues to work, Patch fails with

"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 176.34.122.158:15737"

 and DB is not updated

\o/ help!

  1. 1 Posted by lvmcastro on 18 Jul, 2016 09:30 PM

    lvmcastro's Avatar

    Not exactly fixed because I still don't know what was telling the client to change ports, but I forced the client back into the right port during header insertion

    I feel dirty T_T

  2. lvmcastro closed this discussion on 18 Jul, 2016 09:31 PM.

  3. rune re-opened this discussion on 18 Jul, 2016 09:37 PM

  4. Support Staff 2 Posted by rune on 18 Jul, 2016 09:37 PM

    rune's Avatar

    Hi,

    Sounds like the problem is that the wrong URL is being used -- the app isn't listening publicly on other ports than 80 and 443.

    What do you mean by forcing the client back during header insertion?

    Best,
    Rune

  5. 3 Posted by lvmcastro on 19 Jul, 2016 07:58 PM

    lvmcastro's Avatar
  6. Support Staff 4 Posted by rune on 20 Jul, 2016 01:56 AM

    rune's Avatar

    Hi,

    Ah ok, makes sense -- the reason you're getting a URL with the port number is simply that the application runs on a unique, dedicated port. The load balancer listens to requests on default ports and acts as a reverse proxy to the private IP and port for your worker.

    The protocol (http/https) can be determined from the X-Forwarded-Proto header. You can find an example of using it here, and a couple of workarounds for generating public URLs in this KB article.

    However, I don't entirely understand why you need to do this when making requests from the application -- based on your Stack Overflow post it appears that you have to modify the port prior to making a request. Is the application making a request to itself or what is the workflow/use case for this?

    Best,
    Rune

  7. 5 Posted by lvmcastro on 20 Jul, 2016 07:03 PM

    lvmcastro's Avatar

    Hi Rune,

    Thanks for looking into it, I edited the SO question to try and explain the workflow.

    Do you still think it's the load balancer telling my desktop application "don't send your request to me, send them over there instead"?

  8. Support Staff 6 Posted by rune on 23 Jul, 2016 07:53 AM

    rune's Avatar

    Hi,

    Nope I never thought that the load balancer was telling your app to send requests elsewhere :-)

    I tried this for myself and I can see that the problem indeed is that the server is giving the client the wrong URL. It seems that OData/WebAPI doesn't support the X-Forwarded-Proto header out of the box. That causes issues since the OData client software doesn't continue to use the URL you specify when setting up the OData context -- it reads the metadata, which will contain the wrong scheme, hostname and port.

    The solution to this can be found in the article I linked to earlier. In particular, you need to take two step to make sure that the OData base URL returned in the service metadata is accurate:

    1. Set the aspnet:UseHostHeaderForRequestUrl appSetting to true. You can simply add this as an configuration variable for your AppHarbor app so it'll be injected when deploying the app. This will ensure that IIS respects the Host header rather than building a URL based on the server's IP address.
    2. Make sure the OData service respects the X-Forwarded-Proto header and sets the scheme and port when generating URLs.

    There's a sample in the KB article I linked to earlier which shows a solution for Owin. Sounds like you're usign WebAPI so here's a quick example of a handler implementation that'll work with that:

    public class ForwardedProtocolHeadersHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var forwardedProtocolHeader = request.Headers.FirstOrDefault(x =>
                string.Equals(x.Key, "X-Forwarded-Proto", StringComparison.InvariantCultureIgnoreCase));
    
            if (!string.IsNullOrEmpty(forwardedProtocolHeader.Key))
            {
                var isHttps = string.Equals(forwardedProtocolHeader.Value.Single(), Uri.UriSchemeHttps, StringComparison.InvariantCultureIgnoreCase);
    
                var urlBuilder = new UriBuilder(request.RequestUri)
                {
                    Scheme = isHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp,
                    Port = isHttps ? 443 : 80,
                };
    
                request.RequestUri = urlBuilder.Uri;
            }
    
            return base.SendAsync(request, cancellationToken);
        }
    }
    

    Also remember to register the handler in your WebApi configuration. Something like this should work:

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MessageHandlers.Add(new ForwardedProtocolHeadersHandler());
            // Other WebApi configuration
        }
    }
    

    I hope this helps!

    Best,
    Rune

  9. 7 Posted by lvmcastro on 24 Jul, 2016 12:27 PM

    lvmcastro's Avatar

    Hi Rune,

    Implemented it and It works, thanks a bunch.

    So... before, the webservice was telling the client to use the uri and port the server uses when communicate with the load balancer and now the 1st step makes the webserver send no port info so it defaults to default and the second step makes the webservice send the correct uri and port?

    I'll put reading up on X-Forwarded-Proto header and DelegatingHandler on my todo list T_T

  10. Support Staff 8 Posted by rune on 24 Jul, 2016 09:17 PM

    rune's Avatar

    Hi,

    Yes pretty much -- actually the first step is just there to ensure that the ASP.NET service to use the value of the HOST header rather than the IP address (e.g. `example.apphb.com rather than. I'm not sure whether this is necessary for your use case, but it seemed like it may be based on the Stack Overflow thread (which had the IP and port in the error message).

    The 2nd step involving the X-Forwarded-Proto header is responsible for setting the proper protocol scheme (https/http) and uses this to set the port to the corresponding standard port values (443/80).

    In the end this is a more elegant solution -- it fixes the root cause rather than being a workaround on the client side.

    Support for these types of headers (in particular, X-Forwarded-Proto and X-Forwarded-For) has been lacking in .NET libraries, but fortunately newer versions of ASP.NET Core, MVC, OWin etc have better support for it.

    Best,
    Rune

Discussions are closed to public comments.
If you need help with AppHarbor please start a new discussion.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac