WCF inter-service messaging - wcf

I am building a system with 2 WCF Services. Both are IIS Hosted. At the moment they both reside in a single VS2010 website app, running on my local IIS7 (Windows 7) using the Derfault Website. I have enabled net.tcp on both.
Service1
accepts HTTP posts using webHttpBinding
wraps the data in a serializable composite object
sends the composite object to Service2 (we hope) using netMsmqBinding
Service2
receives said message and does something with it
Service 1 works as expected, however instead of placing the message on the configured Private Queue, our code is creating a new Queue under "Outgoing Queues" with the handle
DIRECT=TCP:127.0.0.1\private$\Service2/Service2.svc
note the forward slash
Of course Service2 never sees the message - this is the first time I have attempted this structure so I am not certain that Service2 misses the message because of its location, but based on what I have read it would seem so - I have not come across anything mentioning this Queue-creation behaviour.
Questions:
Am I doing this correctly (is there something wrong in the structure, web.config or code)?
When done properly in VS Debug, should Service1's
proxy.ProcessForm(formMessage);
hit breakpoints in my Service2 code, or is there another way to hande Service2 debug (ala windows services for example)?
Service1 Web.Config
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpFormBinding" crossDomainScriptAccessEnabled="true"/>
</webHttpBinding>
<netMsmqBinding>
<binding name="MsmqFormMessageBindingClient" exactlyOnce="false" useActiveDirectory="false" >
<security mode="None">
<message clientCredentialType="None"/>
<transport msmqAuthenticationMode="None" msmqProtectionLevel="None" />
</security>
</binding>
</netMsmqBinding>
</bindings>
<client>
<endpoint
name="HttpServiceWebEndpoint"
address=""
binding="webHttpBinding"
bindingConfiguration="webHttpFormBinding"
contract="Service1.HttpService.IHttpServiceWeb" />
<endpoint name="MsmqFormMessageBindingClient"
address="net.msmq://127.0.0.1/private/Service2/Service2.svc"
binding="netMsmqBinding"
contract="MyInfrastructure.IService2" />
</client>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
<!--
<serviceAuthenticationManager />
-->
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
On Receipt of an HTTP Post Service1 executes the following:
StreamReader sr = new StreamReader(formData);
string str = sr.ReadToEnd();
var t = HttpUtility.ParseQueryString(str);
Hashtable nvc = new Hashtable();
foreach (string n in t)
{
nvc.Add(n, (string)t[n]);
}
WcfFormMessage formMessage = new WcfFormMessage(nvc);
////create the Service binding
NetMsmqBinding msmq = new NetMsmqBinding("MsmqFormMessageBindingClient");
msmq.Security.Mode = (NetMsmqSecurityMode) MsmqAuthenticationMode.None;
EndpointAddress address = new EndpointAddress("net.msmq://127.0.0.1/private/Service2/Service2.svc");
ChannelFactory<IService2> factory = new ChannelFactory<IFormService>(msmq,address);
IService2 proxy = factory.CreateChannel();
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
proxy.ProcessForm(formMessage);
//do any 'sent to queue logging/updates here
}

I am ready to bet that your problem is related to 127.0.0.1 in your config. Type the machine name in there, even if it is local.

Related

Pass token from MVC to WCF service

I have a MVC app talking to ACS to get token for authentication. It's a claim based application. This works perfectly fine.
I am trying to call WCF service from MVC once authenticated with same taken so that i can use same claims for authorization.
MVC code is as below
var context = (BootstrapContext)identity.BootstrapContext;
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.Message);
binding.Security.Message.IssuedKeyType = SecurityKeyType.SymmetricKey;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.IssuerBinding = new WS2007FederationHttpBinding();
EndpointAddress acsEndPoint =
new EndpointAddress("https://ACS namespace/v2/wsfederation");
binding.Security.Message.IssuerAddress = acsEndPoint;
binding.Security.Message.IssuedTokenType = "urn:ietf:params:oauth:token-type:jwt";
ChannelFactory<IService1> factory =
new ChannelFactory<IService1>(binding, new EndpointAddress("https://localhost/TestWCF/Service1.svc"));
factory.Credentials.SupportInteractive = false;
factory.Credentials.UseIdentityConfiguration = true;
var proxy = factory.CreateChannelWithIssuedToken(context.SecurityToken);
proxy.GetData(1);
WCF web config is as below
<system.serviceModel>
<services>
<service name="TestWCF.Service1">
<endpoint address="" behaviorConfiguration="webHttpAutoFormat" binding="ws2007FederationHttpBinding" bindingConfiguration="secureHttpBinding" contract="TestWCF.IService1"/>
<endpoint address="soap" binding="basicHttpBinding" contract="TestWCF.IService1" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<ws2007FederationHttpBinding>
<binding name="secureHttpBinding">
<security mode="None">
<message establishSecurityContext="false" issuedKeyType="SymmetricKey" issuedTokenType="urn:ietf:params:oauth:token- type:jwt">
<issuerMetadata address="https://ACS namespace/v2/wstrust/mex"></issuerMetadata>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials useIdentityConfiguration="true"></serviceCredentials>
<serviceAuthorization principalPermissionMode="Always" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttpAutoFormat">
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
<serviceActivations>
<add relativeAddress="Service1.svc" service="TestWCF.Service1" />
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
Please note my WCF service is not HTTPS also I am using JWT token from ACS. No certificates.
I get below error
The provided URI scheme 'https' is invalid; expected 'http'.
Parameter name: via
Can anyone help?
You are currently initializing your binding with
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.Message)
Try changing to
var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential)
From (MSDN - WS Transport With Message Credential):
By default, the wsHttpBinding binding provides HTTP communication.
When configured for transport security, the binding supports HTTPS
communication. HTTPS provides confidentiality and integrity protection
for the messages that are transmitted over the wire. However the set
of authentication mechanisms that can be used to authenticate the
client to the service is limited to what the HTTPS transport supports.
Windows Communication Foundation (WCF) offers a
TransportWithMessageCredential security mode that is designed to
overcome this limitation. When this security mode is configured, the
transport security is used to provide confidentiality and integrity
for the transmitted messages and to perform the service
authentication. However, the client authentication is performed by
putting the client credential directly in the message. This allows you
to use any credential type that is supported by the message security
mode for the client authentication while keeping the performance
benefit of transport security mode.
Your web config should have this instead for <ws2007FederationHttpBinding>:
<ws2007FederationHttpBinding>
<binding name="secureHttpBinding">
<security mode="TransportWithMessageCredential">
<message establishSecurityContext="false" issuedKeyType="SymmetricKey" issuedTokenType="urn:ietf:params:oauth:token- type:jwt">
<issuerMetadata address="https://ACS namespace/v2/wstrust/mex"></issuerMetadata>
</message>
</security>
</binding>
</ws2007FederationHttpBinding>
See also the following answer for some additional info as well: StackOverflow - The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via

Setting binding in WCF service

This may seem like a really easy question but I can't seem to figure it out at all.
I'm trying to create a new WCF service, and I'm new to having to secure them. I'm using a custom username/password for authentication. The problem [right now anyways] that I seem to be running into is that I can't figure out how to define the service to use the WSHttpBinding (on the service side, not the client side).
Am I missing something incredibly simple? Any pointers and/or recommendations would be greatly appreciated!
EDIT
Here's my code so far:
IAccountService
[ServiceContract]
public interface IAccountService
{
[OperationContract]
bool IsCardValid(string cardNumber);
[OperationContract]
bool IsAccountActive(string cardNumber);
[OperationContract]
int GetPointBalance(string cardNumber);
}
Service web.config
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
<StructureMapServiceBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="StructureMapServiceBehavior" type="Marcus.Loyalty.WebServices.Setup.StructureMapServiceBehavior, Marcus.Loyalty.WebServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true" />
<services>
<service name="Marcus.Loyalty.WebServices.Account.IAccountService">
<endpoint address=""
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_Config"
contract="Marcus.Loyalty.WebServices.Account.IAccountService"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_Config"/>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Testing app (console app)
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please enter card number");
var number = Console.ReadLine();
var endPoint = new EndpointAddress("http://localhost:59492/Account/AccountService.svc");
var binding = new WSHttpBinding(SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var cf = new ChannelFactory<IAccountService>(binding, endPoint);
cf.Credentials.UserName.UserName = "testuser";
cf.Credentials.UserName.Password = "Password1!";
var service = cf.CreateChannel();
var balance = service.IsAccountActive(number);
Console.WriteLine("\nBALANCE: {0:#,#}", balance);
Console.Write("\n\nPress Enter to continue");
Console.Read();
}
}
Testing app app.config
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="BasicHttpBinding_IAccountService" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:59492/Account/AccountService.svc"
binding="wsHttpBinding" bindingConfiguration="BasicHttpBinding_IAccountService"
contract="ServiceReference1.IAccountService" name="BasicHttpBinding_IAccountService" />
</client>
</system.serviceModel>
</configuration>
You need to define the abc (address, binding, contract) configuration into de web.config file (you can also do it programmatically. the b part, the binding, you can specify the wsHttpBinding
<system.serviceModel>
<services>
<service name = "MyNamespace.MyService">
<endpoint
address = "http://localhost:8000/MyService"
binding = "wsHttpBinding"
contract = "MyNamespace.IMyContract" />
</service>
</services>
</system.serviceModel>
If you wish to enable security in a proper way, there is a lot of literature and options. You can use certificates, windows based, tokens, ... passing a username & password like a parameter could not be the best way to do it.
There is an extensive sample on MSDN (How to: Specify a Service Binding in code) - but basically, you need to have:
your service contract (IMyService)
an implementation of that service (MyService)
a code where you create your ServiceHost to host your service
You got all of that? Great!
In that case, just do something like this:
// Specify a base address for the service
string baseAddress = "http://YourServer/MyService";
// Create the binding to be used by the service.
WsHttpBinding binding1 = new WsHttpBinding();
using(ServiceHost host = new ServiceHost(typeof(MyService)))
{
host.AddServiceEndpoint(typeof(IMyService), binding1, baseAddress);
host.Open();
Console.ReadLine();
}
and now you should have your service host up and running, on your chosen base address and with the wsHttpBinding defined in code.

WCF - There was no endpoint listening

One of my WCF Services has an operation contract taking a large sized file as a parameter. So, when the client tries to send this over, I got an exception and when I looked at the server trace this is what I saw:
MESSAGE: The maximum message size quota for incoming messages (65536)
has been exceeded. To increase the quota, use the
MaxReceivedMessageSize property on the appropriate binding element.
I was using the default simplified configuration for my WCF services, so added a new service definition as follows:
<system.serviceModel>
<services>
<service name="MyNamespace.MyService">
<endpoint address="MyService.svc"
binding="basicHttpBinding" bindingConfiguration="basicHttp"
contract="MyNamespace.IMyService" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="basicHttp" allowCookies="true"
maxReceivedMessageSize="10485760"
maxBufferSize="10485760"
maxBufferPoolSize="10485760">
<readerQuotas maxDepth="32"
maxArrayLength="10485760"
maxStringContentLength="10485760"/>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
...
</behaviors>
<protocolMapping>
...
</protocolMapping>
The way I consume my services is, I have a function returning a channel in my helper class, and I use that channel to call the operations:
public static T CreateChannel<T>() where T : IBaseService
{
System.ServiceModel.BasicHttpBinding binding= new System.ServiceModel.BasicHttpBinding();
binding.TransferMode = TransferMode.Streamed;
binding.Security = new BasicHttpSecurity() { Mode = BasicHttpSecurityMode.None };
binding.MaxReceivedMessageSize = 10485760;
binding.MaxBufferSize = 10485760;
System.ServiceModel.ChannelFactory<T> cf2 = new ChannelFactory<T>(binding,
new System.ServiceModel.EndpointAddress(MyEndpointAddress)); //I checked this part, the address is correct.
T Channel= cf2.CreateChannel();
return Channel;
}
and then,
var businessObject = WcfHelper.CreateChannel<IMyService>();
var operationResult = await businessObject.MyOperationAsync(...);
Even though, my other services are running correctly, the one I defined in the configuration explicitly returns an exception of "There was no endpoint listening..." I am developing on VS2012, using IISExpress. What may be the problem, any suggestions?
I think there is a mismatch for transfert mode. In client-side, you are are using streamed transfert whereas in server-side it is not in the config. In addition, you have specified 10MB, which is not so high.
Please visit this for more info on streaming.
Edit :
If you are hosting under IIS, please also check (default is 4Mb) :
<system.web>
<httpRuntime maxRequestLength="4096 " />
</system.web>

TransactionFlowAttribute attribute set to Mandatory but the channel's binding is not configured with a TransactionFlowBindingElement

I have a big problem. I am trying to create a web service that will work with a distributed transaction.
All the code below is on the server side of the web service(the web service that is called from a client).
I wrote this in my interface:
[ServiceContract]
public interface IClientOperations
{
[OperationContract]
[ServiceKnownType(typeof(TriggerExecInput))]
[ServiceKnownType(typeof(TriggerExecOutput))]
[TransactionFlow(TransactionFlowOption.Mandatory)]
TriggerExecOutput TriggeredProfileDataUpdate(TriggerExecInput triggerInputData, bool isST3StatusActive);
And this in the web.config file:
<services>
<service name="ClientOperationsService" behaviorConfiguration="ServiceBehavior">
<endpoint address="" binding="wsHttpBinding"
bindingConfiguration="wsHttpBinding_Common" contract="SL.STAdmin.Applications.WebAPI.IClientOperations"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding_Common" transactionFlow="true">
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
If I right click the .svc file and click on "View in browser" I get the following error
Exception Details: System.InvalidOperationException: At least one operation on the 'ClientOperations' contract is configured with the TransactionFlowAttribute attribute set to Mandatory but the channel's binding 'BasicHttpBinding' is not configured with a TransactionFlowBindingElement. The TransactionFlowAttribute attribute set to Mandatory cannot be used without a TransactionFlowBindingElement.
I have other .svc files that don't use transactions.
They all work well.
I don't understand why it still tries to use the BasicHttpTransaction when I instruct it to use the other binding type.
DOes anyone have any idea what I am doing wrong?
Thank you in advance.
Add this inside your <system.serviceModel> element of your web.config:
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding_Common"/>
</protocolMapping>
You need to do a few things to get the transaction working.
Add the transactionflow to your operation
[OperationContract]
[TransactionFlow(TransactionFlowOption.Mandatory)]
void TransactionSupported(int id, string name);
After that you add an operationbehavior to your implementation
[OperationBehavior(TransactionScopeRequired = true)]
public void TransactionSupported(int id, string name)
{
...
}
In your config file you need to add the transaction flow to your host binding
<system.serviceModel>
...
<bindings>
<netNamedPipeBinding> --> Your binding (don't use basicHttpBinding)
<binding transactionFlow="true"/>
</netNamedPipeBinding>
</bindings>
</system.serviceModel>
And last but not least you need to set the transactionflow of your client to get it working. In my example I do this in my code in my unit test, I think you can also do this in your configuration of your client, in your config file.
var factory = new ChannelFactory<IService>(callback,
new NetNamedPipeBinding() { TransactionFlow = true },
new EndpointAddress("net.pipe://localhost/ping"));

WCF Max Instances seems to be limited to 10

I have a simple WCF service hosted in IIS7.5 exposed over a wsHttp binding using message security and InstanceContextMode.PerCall
I have a simple UI that spins up a configurable number of threads, each calling the service.
I have added the perfmon counter ServiceModel4.Instances. Regardless of the number of threads created and calling the service, perfmon shows that the service creates a maximum of 10 Instances.
My client config is as follows:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService3">
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost/NGCInstancing/Service3.svc/~/Service3.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService3"
contract="NGCSecPerCall.IService3" name="WSHttpBinding_IService3">
<identity>
<servicePrincipalName value="host/RB-T510" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
My service config is as follows:
<?xml version="1.0"?>
<system.serviceModel>
<configuration>
<behaviors>
<serviceBehaviors>
<behavior name="SecPerCallBehaviour">
<serviceThrottling maxConcurrentCalls="30" maxConcurrentSessions="1000"
maxConcurrentInstances="30" />
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="BindingMessageSecPerCall" >
<security mode="Message">
<!-- it's by setting establishSecurityContext to false that we enable per call instancing with security -->
<message establishSecurityContext="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="ServiceInstancingDemo.Service3" behaviorConfiguration="SecPerCallBehaviour">
<endpoint address="~/Service3.svc"
binding="wsHttpBinding" bindingConfiguration="BindingMessageSecPerCall"
contract="ServiceInstancingDemo.IService3" />
</service>
</services>
</configuration>
</system.serviceModel>
The client code is as follows:
private void btnSecPerCall_Click(object sender, EventArgs e)
{
int i;
int requests;
int delay;
lblStatus.Text = "";
DateTime startTime = DateTime.Now;
this.listBox1.Items.Add("start time=" + DateTime.Now);
delay = Convert.ToInt16(txtDelay.Text);
requests = Convert.ToInt16(txtRequests.Text);
Task<string>[] result;
result = new Task<string>[requests];
for (i = 0; i < requests; i++)
{
result[i] = Task<string>.Factory.StartNew(() => _ngcSecPerCall.WaitThenReturnString(delay));
}
for (i = 0; i < requests; i++)
{
this.listBox1.Items.Add(result[i].Result);
}
DateTime endTime = DateTime.Now;
TimeSpan ts = endTime - startTime;
lblStatus.Text = "Finished! Time taken= " + ts.Seconds + " seconds";
this.listBox1.Items.Add("end time=" + DateTime.Now);
}
My service code is as follows:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class Service3 : IService3
{
private int m_counter;
public string WaitThenReturnString(int waitMilliSeconds)
{
System.Threading.Thread.Sleep(waitMilliSeconds);
int maxT, workCT;
System.Threading.ThreadPool.GetMaxThreads(out maxT, out workCT);
m_counter++;
return String.Format("Incrementing counter to {0}.\r\nSession Id: {1}. Threads {2}, {3}", m_counter, OperationContext.Current.SessionId, maxT, workCT);
}
}
The service returns 400,400 for the number of threads.
Does anyone know why the service refused to create more that 10 instances?
If I create a copy of the service but with a a wsHttp binding that has <security mode="None"/> then the service happily created many more instances.
Are you testing on a Windows Server or Windows 7? The reason I ask is that IIS on the client OS versions has a 10 connection limit. This is to prevent the client OS from being used in a server environment.
The MaxConcurrentSessions documentation mentions an optimization for client requests that come from the same AppDomain. Your sample code is basically hitting the same socket on the service. Since there is some throttling done per client, that may be affecting your service behavior. Also, 10 happens to be the default value for MaxConcurrentSessions so it may be that your config is not changing the WCF default value.
Enabling security affects the total threads because it establishes a "session" per client so that each request sent does not need to be authenticated on every call. Those security "sessions" count toward the MaxConcurrentSessions total.
Are you running the client and server on the same machine? Then they might be fighting for available threads.
If you are running on a different PC, please ensure you have done the following config changes on both client and server.
http://www.codeproject.com/KB/webservices/quickwins.aspx
It is possible that the client is not generating more than 10 requests to the same server. So, please try the above configs first on both client and server.
And of course, you must have a Windows Server. Otherwise it will be the IIS limit on Windows 7.