Specify a single address for a self-hosted WCF service - c#

I have a self-hosted WCF service written that needs to run on one specific address on a machine with multiple addresses. To that end, I have written the config so that the address to use is specified in the endpoint:
<endpoint address="http://A.B.C.D:8000/MyService" binding="webHttpBinding" name="MyServiceEndpoint" behaviorConfiguration="MyServiceBehavior" contract="IMyServiceInterface" />
When I run this app and start the service, it is running on ALL addresses rather than the one specified. I tried moving the address into the baseAddress field and leave the endpoint address blank, but got the exact same result. What am I missing?

OK, for anyone else who happens to run into this problem, it isn't with the service configuration, it's with the binding configuration.
The webHttpBinding binding has a hostNameComparisonMode property that defaults to StrongWildcard.
This means that an http service ignores the host name and responds to any hostname. As a side effect, it runs on all open addresses on the machine.
If this value is changed to Exact, then it uses the host name or IP address specified either in the endpoint or the base address.

Related

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.

URL rewrite issue with WCF services

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

Regarding WCF service url & Mex Endpoint usage

i am learning wcf so often questions comes to my mind.recently i developed a small calculator wcf service. i had three project one has service contract and another has wcf service with main class from where i self host the service. in app.config file i gave my endpoint address
net.tcp://localhost:5555/Calculator
i always thought that i have to give my service class name at the end of service url. in my apps my service url looks like above and Calculator is my service class name.
just few days ago i was browsing a wcf code and i saw that there service class name was chatsrv but service url would look like net.tcp://localhost:5555/chat
i like to know that can i give any name to my service at the end?
1) if so then how self host code can understand at which url service need to start ?
2) if i set false for <serviceMetadata httpGetEnabled="false"/> then how other client consuming my service because i have set httpGetEnabled="false" and self host the service but from other .net project i could consume that service by channel factory and as add reference? so tell me what people need to set httpGetEnabled="true" if false works fine?
whatever i know that if httpGetEnabled="false" then mex endpoint will not expose to other client and hence forth no other client may not be able to add reference to their project of my service reference. i am new so not good about the service internal. if possible please discuss it in detail.
3) why mex endpoint is required when client can instantiate & call service function through channel factory without mex endpoint then why & when mex endpoint would require?
thanks
UPDATE
1)
i like to know that can i give any url as endpoint address ?
i have given this net.tcp://localhost:5555/chat as my endpoint address and it works but if i give the endpoint address like net.tcp://localhost:5555/Mychat then does it work?
i like to know what is the convention of giving endpoint address url?
2) now regarding mex endpoint. in my service config file i disable mex like but still i saw any client can add reference like net.tcp://localhost:5555/chat/mex how it is possible ?
when mex is disable then mex point should not be expose or accessible before client...am i right?
please guide me. thanks
I'll try to answer some of your questions:
1) When you configure your service, you have to configure the endpoint URL, either with the popular config file or in code like this:
Dim URL as string = "http://ServerName:port/SomeClass/SomeFunction"
Dim ServiceHost As New ServiceHost(GetType(YourImplementationClass), URL)
Dim ServiceHostEndPoint As EndpointAddress = New EndpointAddressURL(URL)
ServiceHost.AddServiceEndpoint(GetType(iYourImplementationClass), binding, ServiceHostEndPoint.ToString)
ServiceHost.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpsBinding(), "mex")
ServiceHost.Open()
2) You can give your clients the URL and xsl directly, but exposing your endpoint with Mex is easier for everyone. Also, if you make changes, they can re-reference your mex metadata and automatically update their interfaces.
3) mex isn't required if clients are configuring the endpoints themselves using the channel factory, it's just easier using mex. Just typing your endpoint URL into a browser will generate most of what they need to connect to your web service.
Hope that helps.
Comment out below line because it'll be added automatically while configuring the proxy in your configuration file where you defined endpoints of the service.
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
If you want to check, Right click on service proxy where you added service proxy, and select "Configure Service Reference" and you'll notice /mex is added in URL of the service at last

The HTTP request is unauthorized with client authentication scheme 'Ntlm'. The authentication header received from the server was 'Negotiate,NTLM'

I've looked through a ton of SO articles, and even other sites, but can't seem to get this service working. I have a SOAP service I'm trying to hit and it's configured like this:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="PROVIDERSSoapBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm" proxyCredentialType="None" realm="" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://xxx.xx.xx.xxx:9011/provider/services/PROVIDERS"
binding="basicHttpBinding" bindingConfiguration="PROVIDERSSoapBinding"
contract="ServiceReference1.ProviderRemote" name="PROVIDERS" />
</client>
</system.serviceModel>
However, I'm getting the following error when hitting it from my console application:
The HTTP request is unauthorized with client authentication scheme 'Ntlm'. The authentication header received from the server was 'Negotiate,NTLM'.
Can somebody help me out?
Try setting 'clientCredentialType' to 'Windows' instead of 'Ntlm'.
I think that this is what the server is expecting - i.e. when it says the server expects "Negotiate,NTLM", that actually means Windows Auth, where it will try to use Kerberos if available, or fall back to NTLM if not (hence the 'negotiate')
I'm basing this on somewhat reading between the lines of: Selecting a Credential Type
You can eliminate the client from the problem by using wftech, this is an old tool but I have found it useful in diagnosing authentication issues. wfetch allows you to specify NTLM, Negotiate and kerberos, this may well help you better understand your problem. As you are trying to call a service and wfetch knows nothing about WCF, I would suggest applying your endpoint binding (PROVIDERSSoapBinding) to the serviceMetadata then you can do an HTTP GET of the WSDL for the service with the same security settings.
Another option, which may be available to you is to force the server to use NTLM, you can do this by either editing the metabase (IIS 6) and removing the Negotiate setting, more details at http://support.microsoft.com/kb/215383.
If you are using IIS 7.x then the approach is slightly different, details of how to configure the authentication providers are here http://www.iis.net/configreference/system.webserver/security/authentication/windowsauthentication.
I notice that you have blocked out the server address with xxx.xx.xx.xxx, so I'm guessing that this is an IP address rather than a server name, this may cause issues with authentication, so if possible try targeting the machine name.
Sorry that I haven't given you the answer but rather pointers for getting closer to the issue, but I hope it helps.
I'll finish by saying that I have experienced this same issue and my only recourse was to use Kerberos rather than NTLM, don't forget you'll need to register an SPN for the service if you do go down this route.
If both your client and service is installed on the same machine, and you are facing this problem with the correct (read: tried and tested elsewhere) client and service configurations, then this might be worth checking.
Check host entries in your host file
%windir%/system32/drivers/etc/hosts
Check to see if you are accessing your web service with a hostname, and that same hostname has been associated with an IP address in the hosts file mentioned above.
If yes, NTLM/Windows credentials will NOT be passed from the client to the service as any request for that hostname will be routed again at the machine level.
Try either of the following
Remove the host entry of that hostname from the hosts file
OR
If removing host entry is not possible, then try accessing your service with another hostname. You might also try with IP address instead of hostname
Edit:
Somehow the above situation is relevant on a load-balanced scenario.
However, if removing the host entries is not possible, then disabling loop back check on the machine will help.
Refer method 2 in the article https://support.microsoft.com/en-us/kb/896861
We encountered this issue and discovered that the error was being thrown when using (IE in our case) the browser logged in as the process account, then changing the session log in through the application (SharePoint). I believe this scenario passes two authentication schemes:
Negotiate
NTLM
The application hosted an *.asmx web service, that was being called on a load balanced server, initiating a web service call to itself using a WCF-like .NET3.5 binding.
Code that was used to call the web service:
public class WebServiceClient<T> : IDisposable
{
private readonly T _channel;
private readonly IClientChannel _clientChannel;
public WebServiceClient(string url)
: this(url, null)
{
}
/// <summary>
/// Use action to change some of the connection properties before creating the channel
/// </summary>
public WebServiceClient(string url,
Action<CustomBinding, HttpTransportBindingElement, EndpointAddress, ChannelFactory> init)
{
var binding = new CustomBinding();
binding.Elements.Add(
new TextMessageEncodingBindingElement(MessageVersion.Soap12, Encoding.UTF8));
var transport = url.StartsWith("https", StringComparison.InvariantCultureIgnoreCase)
? new HttpsTransportBindingElement()
: new HttpTransportBindingElement();
transport.AuthenticationScheme = System.Net.AuthenticationSchemes.Ntlm;
binding.Elements.Add(transport);
var address = new EndpointAddress(url);
var factory = new ChannelFactory<T>(binding, address);
factory.Credentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Impersonation;
if (init != null)
{
init(binding, transport, address, factory);
}
this._clientChannel = (IClientChannel)factory.CreateChannel();
this._channel = (T)this._clientChannel;
}
/// <summary>
/// Use this property to call service methods
/// </summary>
public T Channel
{
get { return this._channel; }
}
/// <summary>
/// Use this porperty when working with
/// Session or Cookies
/// </summary>
public IClientChannel ClientChannel
{
get { return this._clientChannel; }
}
public void Dispose()
{
this._clientChannel.Dispose();
}
}
We discovered that if the session credential was the same as the browser's process account, then just NTLM was used and the call was successful. Otherwise it would result in this captured exception:
The HTTP request is unauthorized with client authentication scheme 'Ntlm'. The authentication header received from the server was 'Negotiate,NTLM'.
In the end, I am fairly certain that one of the authentication schemes would pass authentication while the other wouldn't, because it was not granted appropriate access.
You need to set the NTAuthenticationProviders to NTLM
MSDN Article: https://msdn.microsoft.com/en-us/library/ee248703(VS.90).aspx
IIS Command-line (http://msdn.microsoft.com/en-us/library/ms525006(v=vs.90).aspx):
cscript adsutil.vbs set w3svc/WebSiteValueData/root/NTAuthenticationProviders "NTLM"
I know this question is old, but the solution to my application, was different to the already suggested answers. If anyone else like me still have this issue, and none of the above answers works, this might be the problem:
I used a Network Credentials object to parse a windows username+password to a third party SOAP webservice. I had set the username="domainname\username", password="password" and domain="domainname". Now this game me that strange Ntlm and not NTLM error.
To solve the problems, make sure not to use the domain parameter on the NetworkCredentials object if the domain name is included in the username with the backslash. So either remove domain name from the username and parse in domain parameter, or leave out the domain parameter. This solved my issue.
I'm using .NET 5. In my case I had to downgrade System.ServiceModel.Http.dll from 4.8.1 to 4.4.4. Didn't have much time to dig deeper the root cause.

Is it better to specify a relative address in an endpoint or in a UriTemplate?

Is it better practice to specify a relative address in an endpoint for example
<endpoint address="json" ... />
or in the UriTemplates:
[WebGet(UriTemplate="json/....")]
What is better practice here? Does it matter? I know if I decide to change json to js later, I would have to change it in all the UriTemplates, but only one time in the endpoint, but what if I have multiple endpoints. I can't use the same address.
It is different things. Lets look to each of it.
'EndpointAddress provides a unique network address that a client uses to communicate with a service endpoint'. The endpoint address belongs to the service endpoint, which also contains the binding, contract and behaviors for the endpoint.
The endpoint address for a service can be specified either imperatively using code or declaratively through configuration. Defining endpoints in code is usually not practical because the bindings and addresses for a deployed service are typically different from those used while the service is being developed. It is more practical to define service endpoints using configuration.
In cases when do you need multiple endpoints defined in your configuration - you can manually instantiate Client using endpoint Name attribute instead of endpoint address.
'WebGetAttribute indicates that a service operation is logically a retrieval operation and that it can be called by the REST programming model'.
The WebGetAttribute attribute is applied to a service operation in addition to the OperationContractAttribute and associates the operation with a UriTemplate as well as the HTTP protocol Get verb.
Here is great post about REST / SOAP endpoints for a WCF service where you can see how to configure endpoints for both technologies and how to use WebGetAttribute.
I think this comes down to a question of granularity. An endpoint defines the entire contract you are exposing and a URI template is used to locate/manipulate a specific resource. If you are attempting to vary the output format for the consumer the WCF plumbing handles this for you(by appending the format to the uri, can't recall the specific string).

Categories