URL rewrite issue with WCF services - c#

I am at the tail end of a project that rewrites our existing public endpoints to be hosted in IIS (our current implementation was written before IIS7 and is a home-grown hosting application).
I'm also at my wits end with trying to get the URL Rewrite functionality to work properly so that we can seamlessly move our existing customers over to the new endpoints. I'm having a couple of issues with running the new endpoints alongside our legacy app.
I thought this would work fine:
Legacy URL:
https://mydevserver:443/2.0.22/ServiceA
New URL:
https://mydevserver:9995/2.1.22/ServiceB.svc
Rule in web.config:
<rule name="Test">
<match url="2.0.22/ServiceA" />
<action type="Rewrite" url="2.1.22/ServiceB.svc" />
</rule>
So I shut down the legacy service, fired up my client and pointed it to the legacy service URL, but I get an error that there is no endpoint listening. Which makes sense to me, as that URL is registered by our legacy app and it's not available:
There was no endpoint listening at
https://mydevserver:443/2.0.22/ServiceA that could accept the message.
This is often caused by an incorrect address or SOAP action. See
InnerException, if present, for more details.
So I thought if I changed the binding port in IIS for my new endpoints to be the https default, it would work -
New URL:
[same as legacy]/2.1.22/ServiceB.svc
This prompts IIS to give me a warning that the binding is already being used by a product other than IIS, and I might overwrite the existing cert for the address/port combination. So I say OK, and rebind the cert to the 443 port (for good measure), but when I point my client to the old URL, I get sort of the same error but it's worded a little differently:
The HTTP service located at https://mydevserver:443/2.0.22/ServiceA is
unavailable. This could be because the service is too busy or because
no endpoint was found listening at the specified address. Please
ensure that the address is correct and try accessing the service again
later.
I feel like I've tried every combination of everything (wildcard matches, paths, etc.) but I am clearly missing something. I would appreciate any help on this issue.
*Also, is it even possible to host the IIS endpoints on a different server and use URL rewrites?

From further research, I have determined that this is not possible:
You can only rewrite the URL in the same site and same application
pool.
http://blogs.msdn.com/b/asiatech/archive/2011/08/25/return-404-4-not-found-when-url-rewrite.aspx

Related

Is there a better way to debug a SOAP request?

At the moment I'm confronted with the following situation:
I use a web app in my browser that runs on an app server (appsrv).
Depending on configurations I set in the web app (XML and XSLT) appsrv sends a SOAP request to a web service (web_service) on an other server (websvcsrv).
If something goes wrong and I want to check what's sent in the SOAP request I have to do the following:
Change a line in web.config on appsrv from:
<add key="WebServiceUrl_{web_service name}"
value="https://websvcsrv/path/to/web/service.asmx" />
to
<add key="WebServiceUrl_{web_service name}"
value=http://{server which exists and accepts http but has otherwise
absolutely nothing to with the web app or the web service (dummy) }" />
This is only to change from https to the unencrypted http. See below for why this is necessary.
Run Wireshark on appsrv and start capturing.
Run the web app in my browser to a point where the SOAP request is sent from appsrv to the web_service on websvcsrv, where the latter two refer to dummy now.
Ignore the error that's shown by appsrv in the browser (since just dummy is behind now).
Switch to Wireshark on appsrv, filter for xml, find the SOAP request and look at its XML. (This is where http comes into play, since with encrypted https one can't see anything useful for human eyes.)
Correct possible errors in the web app config.
Undo 1., i.e. change line in web.config from dummy to websvcsrv.
Try the corrections with the real web_service on the real websvcsrv.
If something still goes wrong re-start at 1. If not, continue at 10.
Enhance the configuration.
Run the web app in my browser to a point where the–now enhanced–SOAP request is sent from appsrv to the web_service on websvcsrv.
If something goes wrong start at 1.
Needless to say that this is amongst the craziest things I've ever experienced in my decades of developing.
Is there a better way to debug this?

How can I discover what kind of endpoints a service offers with ServicePartitionResolver?

I am trying to write a service resolver and for that I am currently using, as mentioned in https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication, the ServicePartitionResolver.
This all works fairly well so far. Given my service information, I am able to resolve a service like this:
var resolvedService = await ServicePartitionResolver.GetDefault().ResolveAsync(service.ServiceName, key, CancellationToken.None);
Now, in order to allow my services to communicate with each other, I am reading the endpoint address like this for the first one e.g.:
resolvedService.Endpoints.First().Address
This properly returns the required endpoint I returned in the OpenAsync method of my ICommunicationListener implementation.
Basically, this is all doing a fine job as long as the protocol is http.
As soon as I switch the protocol to something like for example tcp in my ServiceManifest.xml of the service where the request should go to:
<Endpoints>
<!-- This endpoint is used by the communication listener to obtain the port on which to
listen. Please note that if your service is partitioned, this port is shared with
replicas of different partitions that are placed in your code. -->
<Endpoint Protocol="tcp" Name="ServiceEndpoint" Type="Input" Port="3245" />
</Endpoints>
The ServiceResolver still resolves my endpoint to an address starting with http.
So, now my question - Am I simply doing something wrong? Because to me it seems like I can't really know what endpoint I am dealing with exactly. Even if this is not directly reflected in the Address, I could still just trim the http part from the endpoint string, but there is no information what kind of endpoint it is as well. So, technically, I can replace the http:// with blank, but it would be preferable to do this based on something I get back from Service Fabric instead of "because I know about the endpoint".
Any ideas?
The protocol definition from the manifest is not used define the endpoint. The communication listener implementation returns the endpoint from OpenAsync. So I'd recommend starting the search in there.
More info here and here.

How do people test their [RequireHttps] attribute in a Web API?

Quick version
I'm sure many people have implemented a [RequireHttps] SSL check of some description (message handler, attribute, whatever) at some point in their Web API development. How do you guys and gals test that it works correctly both in terms of success and failure?
Not so quick version
I'm developing a REST service in a OWIN self-hosted ASP.NET Web API 2. I have already successfully secured the service with SSL and have implemented a custom [RequireHttps] attribute (derived from the answers to this SO question).
In the case when the client is calling the correct URL (e.g. https://my.server.com/api/values), if I add a breakpoint in the attribute definition, the debugger correctly breaks in the code (just calls the base and all is well, as expected).
The question is: how can I exercise the failure scenario for this attribute, such that the attribute code will return an error response without interfering with other server processes?
My Web API service listens on base address https://+:9443/. I've tried removing the s such that I connect to http://my.server.com:9443/api/values, but I get an error response status 502 (connection failed) after about a minute's timeout. Fair enough I suppose, but I was actually hoping to return a response ("SSL required") from my [RequireHttps] attribute.
Then I've tried creating the following StartOptions object:
var options = new StartOptions();
options.Urls.Add("https://+:9443/"); // listen on port 9443 with SSL
options.Urls.Add("http://+:80/"); // listen to standard HTTP port 80
and passing it to the WebApp like this:
WebApp.Start<Startup>(options)
Again, this didn't work when I connected to http://my.server.com:9443/api/values, but it worked when I connected to http://my.server.com:80/api/values.
However, this is not what I want to do. My production server hosts both secure (HTTPS) and insecure (HTTP on port 80) resources so my code will intercept legitimate calls to other processes that rely on port 80 and tell them to reconnect via https, which is wrong.
Can someone please advise on what options I have? Is there even a point to have [RequireHttps] given my situation, as it never seems to do anything useful?
What you are trying to do can't be done. Basically, you're trying to do the same thing as typing
http:443//www.google.com
Notice how that doesn't work either
The problem is that you're trying to access an http protocol over an SSL protocol port, and that is what's failing. Your code for the RequireHttps doesn't even get to execute because the request can't even be processed through IIS.

IIS7 URL Rewrite returns 404 for WCF requests (reverse proxy)

I am using IIS7.5, .net 4.0. I am working locally.
I have installed Application Request Routing, Web Farm Framework, WebDeploy and UrlRewrite to set up a reverse proxy. This works fine for the most part.
I have two websites:
DefaultWebSite (port 80, app pool: Default App Pool (.net 4)) and
Target (port 8085, app pool: TargetAppPool(my identity, .net 4)).
I have a rewrite rule on DefaultWebSite (created as directed on IIS.net) which redirects all localhost (port 80) traffic to localhost:8085 just as detailed in the above link. This works fine for most document types (.aspx, .xap, .htm, .ico) but a request to MyService.svc fails. It returns a 404.
To be clear:
When I paste localhost:8085/MyService.svc into a browser I get the requested WCF page.
When I paste localhost/MyService.svc into a browser I get a 404.
When I paste localhost:8085/MyIcon.ico into a browser I get the requested resource.
When I paste localhost/MyIcon.ico into a browser I get the requested resource.
.svc is the only document type that I've found that returns a 404.
I've got two pieces of info that might be of relevance.
App Pools. When I change the DefaultWebSite's app pool to TargetAppPool then the 404 becomes a 500 ("Failed to map the path '/'"). All other requests are successful when this change is made. Not sure if this relevant or not.
FREB (Failed Request Tracing) Log. I found a page (http://blogs.msdn.com/b/asiatech/archive/2011/08/25/return-404-4-not-found-when-url-rewrite.aspx) which details the steps in a FREB log when a URL rewrite is more successful than mine (it fails later on). I've not been able to find out how to generate a FREB log for a successful rewrite (if that's possible) so I can only compare my FREB log to the one on that blog. I can see that their step 21 (URL_CHANGED) in my FREB log but not 22 (URL_REWRITE_END). I've not got enough experience with these logs to notice anything more significant than that (suggestions welcomed).
My main question is: does anyone know why just URLs requesting .svc resources are not being rewritten?
A secondary question is: does anyone know how to generate a FREB log for successful request (if it's even possible)?
Thanks
Update:
I have changed the architecture to try to get more info.
I have moved the Target website to a different PC on which I have installed Microsoft Network Monitor to capture the incoming traffic.
Before I changed the url-rewrite rule to point at this new website I got the correct response when I made a request to MyService.svc on the new PC. Fine.
As soon as I changed the rewrite rule to route the request to the new Target website then it responds as before (404). I have made both POST and GET requests. There is no sign of any of the requests in the Network Monitor log (all other calls -200, 404 or otherwise- appear in this log).
This leads me to think that there is something incompatible with url-rewrites and *.svc requests. I tried making a request to MyService.asmx (having created this file) and it correctly returned a page, so it is limited to *.svc. Any ideas?
The solution to this is in the config file of the Target web site.
In web.config (in the Target application) there is a section which read:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>.
I changed this to read:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />.
Credit must go to http://forums.iis.net/post/1956671.aspx for this (although s/he claims it is the proxy's config which needs to be changed, but I found it be the Target app, not the proxy server).
If you still can't get it running, make sure you don't have the WCF handlers on the website which acts as the reverse proxy.
I disabled this by adding this web.config of the reverse proxy:
<system.webServer>
...
<handlers>
<remove name="svc-ISAPI-4.0_64bit" />
<remove name="svc-ISAPI-4.0_32bit" />
<remove name="svc-Integrated-4.0" />
</handlers>
</system.webServer>
Because the rewrite appears to work for all resources except when the extension is .svc I would say this would be the area to concentrate on.
I would imagine that the rewrite rules are matching your other resources, but not your service, and because these are usually regular expressions (which are often complicated) I would say it would be worth testing any rules you find with your urls. Details of how to find the regular expressions for an UrlRewrite can be found here.
It is also probably also worth looking at any outbound rules with the same mindset.

The authentication scheme ' NTLM' is not supported and other issues migrating from .asmx to wcf

I just switched my .asmx web service to WCF (both in .NET 3.5), and now I am getting
The authentication scheme ' NTLM' is not supported.
The only thing I've seen on google is where some w3svc/NTAuthenticationProviders setting had NTML instead of NTLM. I don't have that issue.
Okay - here is a summary of some annoying WCF issues that I encountered while migrating hopefully it helps someone.
Issue 1 - "This collection already contains an address with scheme http. There can be at most one address per scheme in this collection."
This is caused, I guess, by my web server having multiple web sites with multiple host headers. Solution found here: WCF service startup error "This collection already contains an address with scheme http".
I just added a baseAddressPrefixFilter:
<serviceHostingEnvironment>
<baseAddressPrefixFilters>
<add prefix="http://yoururl.blahblah.com/" />
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
Issue 2: I got The authentication scheme ' NTLM' is not supported. error. I struggled with that for a while but eventually a reboot made that one go away (although I also did this (http://bytes.com/topic/net/answers/705912-authentication-scheme-ntml-not-supported):
# change directory to c:\inetpub\admin scripts first
cscript adsutil.vbs set w3svc/NTAuthenticationProviders "Negotiate,NTLM"
There was a space after the first comma in my config, no idea if that was causing an issue.
Issue 3: After a reboot, I got fun .404 errors.
My first guess at this was that wcf was not set up to be hosted on my server so I ran:
(http://msdn.microsoft.com/en-us/library/ms732012.aspx)
serviceModelReg.exe -i
But the real source of the 404 was caused by me trying to go to http://yoururl/somewhere.svc instead of http://yoururl.blahblah.com/somewhere.svc (i.e. my baseAddressPrefixFilter was causing my 404).
Hope this helps!

Categories