There are many discussions about this problem, however I have now tried every possible solution and we are still getting 413 Request Entity Too Large errors from the server.
Our WCF service is self hosted via an Azure Worker role, and does not use IIS. All of our configuration is specified in code:
var host = new ServiceHost(searchEngine);
// Create binding
var binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
binding.MaxReceivedMessageSize = 2147483647;
binding.MaxBufferSize = 2147483647;
binding.MaxBufferPoolSize = 2147483647;
var readerQuotas = new XmlDictionaryReaderQuotas
{
MaxStringContentLength = 2147483647,
MaxArrayLength = 2147483647,
MaxBytesPerRead = 2147483647,
MaxDepth = 2147483647,
MaxNameTableCharCount = 2147483647
};
// Setting quotas on a BindingElement after the binding is created has no effect on that binding.
// See: https://stackoverflow.com/questions/969479/modify-endpoint-readerquotas-programatically
binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, readerQuotas, null);
binding.ReceiveTimeout = new TimeSpan(1, 0, 0);
binding.SendTimeout = new TimeSpan(1, 0, 0);
// Add the service endpoint
var ep = host.AddServiceEndpoint(
typeof(ISearchEngine),
binding,
string.Format("https://{0}/SearchEngine", externalEndpoint));
ep.Behaviors.Add(new WebHttpBehavior());
// Increase the MaxItemsInObjectGraph quota for all operations in this service
foreach (var operation in ep.Contract.Operations)
{
operation.Behaviors.Find<DataContractSerializerOperationBehavior>().MaxItemsInObjectGraph = 10000000;
}
return host;
And this is our client configuration - also specified in code:
var binding = new WebHttpBinding(WebHttpSecurityMode.Transport);
binding.MaxReceivedMessageSize = 2147483647;
binding.MaxBufferSize = 2147483647;
binding.MaxBufferPoolSize = 2147483647;
var readerQuotas = new XmlDictionaryReaderQuotas
{
MaxStringContentLength = 2147483647,
MaxArrayLength = 2147483647,
MaxBytesPerRead = 2147483647,
MaxDepth = 2147483647,
MaxNameTableCharCount = 2147483647
};
// Setting quotas on a BindingElement after the binding is created has no effect on that binding.
// See: https://stackoverflow.com/questions/969479/modify-endpoint-readerquotas-programatically
binding.GetType().GetProperty("ReaderQuotas").SetValue(binding, readerQuotas, null);
binding.ReceiveTimeout = new TimeSpan(1, 0, 0);
binding.SendTimeout = new TimeSpan(1, 0, 0);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
var channelFactory = new ChannelFactory<ISearchEngine>(binding, endpointAddress);
channelFactory.Endpoint.Behaviors.Add(new WebHttpBehavior());
// Increase the MaxItemsInObjectGraph quota for all operations in this service
foreach (var operation in channelFactory.Endpoint.Contract.Operations)
{
operation.Behaviors.Find<DataContractSerializerOperationBehavior>().MaxItemsInObjectGraph = 10000000;
}
return channelFactory.CreateChannel();
My only hunch could be a problem with the SSL connection? There are some articles mentioning a problem specific to IIS, however I'm not sure if this is relevant to self hosted services.
Any advice very much appreciated.
UPDATE:
To confirm my hunch that SSL was the problem, I temporarily disabled SSL and lo and behold the problem disappeared.
So now I need to figure out why SSL would be causing the problem. There is a fair bit of documentation about a similar problem, but it relates to IIS hosted services only (ours is self hosted from a windows service):
IIS7 - (413) Request Entity Too Large | uploadReadAheadSize
Would anyone out there know an equivalent setting that would apply to self hosted WCF services only?
I found the problem, thanks to this seemingly unrelated post:
http://forums.newatlanta.com/messages.cfm?threadid=554611A2-E03F-43DB-92F996F4B6222BC0&#top
It was absolutely an SSL issue, and it's to do with binding the SSL certificate to the port you are hosting on. You must bind the certificate using netsh and add clientcertnegotiation=enable to the binding.
In our case we were already using netsh since we were using a different port, so our binding now looks like this:
netsh http add sslcert ipport=0.0.0.0:10100 certhash=000000089A6679262D845B650FDDE5390F0D86AB appid={000007b4-2d4b-4587-ae99-7a6627476f76} clientcertnegotiation=enable
For those of you hosting through IIS, and changing the value of UploadReadAheadSize, the forum post above notes that this may cause high CPU and instead this solution may be better.
In cases where you need to transfer large data, you should use transferMode = "Streaming".
Take a look at this paper from MS:
http://msdn.microsoft.com/en-us/library/ms733742(v=vs.110).aspx
<webHttpBinding>
<binding name="TransportSecurity" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
<security mode="Transport" />
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="16384" />
</binding>
Related
I have a WCF service setup with Messager security using certificates. I am using self-signed certificates for the moment. I do not use the certificate store, but instead load my certificates on the fly.
The service works when I use MessageSecurity=None, but with security turned on, as soon as I make a call to a service endpoint, I get the error:
An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. --> At least one security token of the message could not be validated.
The relevant sections of my service configuration look like this:
<endpoint name="DB_msgAnonymousSecured" address="Secure"
binding="wsHttpBinding" bindingConfiguration="UploadServiceSecure"
contract="DataStoreInterfaces.IDataStoreServices" >
</endpoint>
<wsHttpBinding>
<binding name="UploadServiceSecure" >
<security mode="Message">
<!-- Message security with certificate authentication-->
<message clientCredentialType="Certificate"
negotiateServiceCredential="false" establishSecurityContext="false"/>
</security>
</binding>
</wsHttpBinding>
The certificates are configured in the code like this:
Server:
using (WebServiceHost queryHost = new WebServiceHost(typeof(DataStoreServices.DatabaseServicesImplementation)))
{
try
{
queryHost.Credentials.ServiceCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Application.StartupPath + "\\Server.pfx");
queryHost.Credentials.ClientCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Application.StartupPath + "\\Client.cer");
queryHost.Open();
while (true) //endless loop
{
Thread.Sleep(100);
}
}
catch (Exception ex)
{
}
}
Client:
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
binding.Security.Message.EstablishSecurityContext = false;
binding.Security.Message.NegotiateServiceCredential = false;
EndpointAddress address = new EndpointAddress(new Uri(serviceEndpointAddress), EndpointIdentity.CreateDnsIdentity("CompanyXYZ Server")); //usually, the DnsIdentity is the domain name. In our case, it's the value that was entered during the creation of the self-signed certificate.
ChannelFactory<IDataStoreServices> cf = new ChannelFactory<IDataStoreServices>(binding, address);
cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None; //since our certificate is self-signed, we don't want it to be validated
cf.Credentials.ClientCertificate.Certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Environment.CurrentDirectory + "\\Client.pfx");
cf.Credentials.ServiceCertificate.DefaultCertificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(Environment.CurrentDirectory + "\\Server.cer");
IDataStoreServices channel = cf.CreateChannel();
response = channel.TestConnection(); //this line causes the exception
I have been struggling with this issue for weeks now and I am out of ideas.
Does anybody have any hints for me why this isn't working?
I have a WCF Service that is hosted in a ServiceHost object. The ServiceHost is created on the OnStart method of an Azure Worker Role. Here is the code:
ServiceBusEnvironment.SystemConnectivity.Mode = ConnectivityMode.Http;
Uri baseAddress = ServiceBusEnvironment.CreateServiceUri("http", "my_sb", "SimpleService");
host = new ServiceHost(typeof(SimpleService1.Service1), baseAddress);
BasicHttpRelayBinding binding = new BasicHttpRelayBinding(EndToEndBasicHttpSecurityMode.None, RelayClientAuthenticationType.None);
binding.OpenTimeout = new TimeSpan(1, 1, 0);
binding.ReceiveTimeout = new TimeSpan(1, 10, 0);
binding.SendTimeout = new TimeSpan(1, 10, 0);
binding.MaxReceivedMessageSize = 73400320;
XmlDictionaryReaderQuotas readerQuotas = new XmlDictionaryReaderQuotas();
readerQuotas.MaxArrayLength = 73400320;
binding.ReaderQuotas = readerQuotas;
TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = "owner";
sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = "blablablabla==";
ContractDescription contractDescription = ContractDescription.GetContract(typeof(SimpleService1.IService1), typeof(SimpleService1.Service1));
ServiceEndpoint serviceEndPoint = new ServiceEndpoint(contractDescription);
serviceEndPoint.Address = new EndpointAddress(baseAddress);
serviceEndPoint.Binding = binding;
IEndpointBehavior serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);
serviceEndPoint.Behaviors.Add(serviceRegistrySettings);
serviceEndPoint.Behaviors.Add(sharedSecretServiceBusCredential);
host.Description.Endpoints.Add(serviceEndPoint);
try
{
host.Open();
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message, "Error");
throw;
}
Trace.WriteLine("SimpleService1 running...");
and the binding configuration on the client side is:
<basicHttpBinding>
<binding name="FileTransferBinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="73400320">
<readerQuotas maxArrayLength="73400320"/>
<security mode="None"/>
</binding>
</basicHttpBinding>
and
<endpoint address="http://my_sb.servicebus.windows.net/simpleservice" binding="basicHttpBinding" bindingConfiguration="FileTransferBinding" contract="Service1reference.IService1" name="FileTransferBinding" behaviorConfiguration="sbBehavior"/>
The problem is that if one call to the service takes longer than 1 minute, the client receives this exception:
The content type application/xml; charset=utf-8 of the response message does not match the content type of the binding (text/xml; charset=utf-8). If using a custom encoder, be sure that the IsContentTypeSupported method is implemented properly
If I change the binding to a netTcpRelayBinding everything works fine.
This is because the Windows Azure load-balancer disconnects you if the connection is idle for more than a minute.
Your best bet is to use a WCF callback. This will execute a call from the server to the client to let it know that the long-running operation has completed. For more on how to do this have a look at this blog [WCF Callbacks
We've got to access a web service that uses soap11... no problem I'll just set the binding as:
BasicHttpBinding wsBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
Nope. No dice. So I asked the host of the service why we're having authentication issues and he said that our config needed to have this custom binding:
<bindings>
<customBinding>
<binding name="lbinding">
<security authenticationMode="UserNameOverTransport"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11"
securityHeaderLayout="Strict"
includeTimestamp="false"
requireDerivedKeys="true"
keyEntropyMode="ServerEntropy">
</security>
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport authenticationScheme ="Negotiate" requireClientCertificate ="false" realm =""/>
</binding>
</customBinding>
</bindings>
Only problem is we're creating our binding programmatically not via the config. So if someone could point me in the right direction in regards to changing my BasicHttpBinding into a custombinding that conforms to the .config value provided I'll give them a big shiny gold star for the day.
Solved it!
Here's the winning code for those who are in a similar predicament.
Uri epUri = new Uri(_serviceUri);
CustomBinding binding = new CustomBinding();
SecurityBindingElement sbe = SecurityBindingElement.CreateUserNameOverTransportBindingElement();
sbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11;
sbe.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
sbe.IncludeTimestamp = false;
sbe.SetKeyDerivation(true);
sbe.KeyEntropyMode = System.ServiceModel.Security.SecurityKeyEntropyMode.ServerEntropy;
binding.Elements.Add(sbe);
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, System.Text.Encoding.UTF8));
binding.Elements.Add(new HttpsTransportBindingElement());
EndpointAddress endPoint = new EndpointAddress(epUri);
#D. Forrest already found the solution, but a simple way to see what objects are involved for a given WCF configuration is to call .Endpoint.Binding.CreateBindingElements() on the client proxy you are using. You can the dump the object tree of each item in the list that is returned and see how the binding is configured.
You can use :
Uri epUri = new Uri("http://localhost/TestWCFService/Service.svc");
CustomBinding binding = new CustomBinding()
{
Name = "anyname",
ReceiveTimeout = new TimeSpan(0, 0, 10, 0, 0),
SendTimeout = new TimeSpan(0, 0, 10, 0, 0),
};
var element1 = new TextMessageEncodingBindingElement()
{
ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxDepth = 2147483647,
MaxStringContentLength = 2147483647,
MaxArrayLength = 2147483647,
MaxBytesPerRead = 2147483647,
MaxNameTableCharCount = 2147483647
}
};
var element2 = new HttpsTransportBindingElement()
{
ManualAddressing = false,
MaxReceivedMessageSize = 2147483647,
AllowCookies = false,
AuthenticationScheme = System.Net.AuthenticationSchemes.Anonymous,
BypassProxyOnLocal = false,
MaxBufferSize = 2147483647,
ProxyAuthenticationScheme = System.Net.AuthenticationSchemes.Anonymous,
TransferMode = TransferMode.Buffered,
UseDefaultWebProxy = true
};
var element3 = new TextMessageEncodingBindingElement(MessageVersion.Soap11, System.Text.Encoding.UTF8);
binding.Elements.Add(element1);
binding.Elements.Add(element2);
binding.Elements.Add(element3);
//binding.Elements.Add(new HttpsTransportBindingElement());
EndpointAddress endPoint = new EndpointAddress(epUri);
var client = new ServiceClient(binding, endPoint);
We're developing a Silverlight Client onto a server-based API exposed via WCF.
I'm trying to move my WCF client code (which works fine) from a configuration-based model to a programmatic model. This will enable me to have a single "root" URL which I can apply at start-up and not require installations to have to maintain humongous configuration files.
I'm stuggling converting my configurations to Silverlight-capable code, though.
Where I have the configuration below for one of my services:
<configuration>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="CustomBinding_ISilverlightHelper">
<binaryMessageEncoding />
<httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<extendedProtectionPolicy policyEnforcement="Never" />
</httpTransport>
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://localhost:50072/API/WCF/Silverlight/SilverlightHelper.svc"
binding="customBinding" bindingConfiguration="CustomBinding_ISilverlightHelper"
contract="API.WCF.Silverlight.ISilverlightHelper" name="CustomBinding_ISilverlightHelper" />
</client>
</system.serviceModel>
</configuration>
I can't figure out how to create the equivelant client-config code. At the moment I have:
CustomBinding customBinding = new CustomBinding();
// I see I need to do something with customBinding but the properties don't seem
// logical
// I have used BasicHttpBinding, but it just returns with "Not Found" (the service does resolve to a valid URL)
BasicHttpBinding basicHttpBinding = new BasicHttpBinding() { MaxBufferSize = int.MaxValue, MaxReceivedMessageSize = int.MaxValue };
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:50072/API/WCF/Silverlight/SilverlightHelper.svc");
ISilverlightHelper silverlightHelper= new ChannelFactory<ISilverlightHelper>(basicHttpBinding, endpointAddress).CreateChannel();
AsyncCallback asyncCallback = delegate(IAsyncResult result)
{
ISilverlightHelper asyncSilverlightHelper = (ISilverlightHelper)result.AsyncState;
string[] files=asyncSilverlightHelper.EndGetPlugInXapNames(result).ToArray();
};
silverlightHelper.BeginGetPlugInXapNames(asyncCallback, silverlightHelper);
Any clues would be appreciated. I've spent all morning Googling/Binging/Overflowing but haven't come across this scenario. Or I might be just so far wrong ...
Sorted it.
I created the BinaryMessageEncodingBindingElement and HttpTransportBindingElements, added them to the CustomBinding and it all works.
Here's my annotated code:
// create the binding elements
BinaryMessageEncodingBindingElement binaryMessageEncoding = new BinaryMessageEncodingBindingElement();
HttpTransportBindingElement httpTransport = new HttpTransportBindingElement() { MaxBufferSize = int.MaxValue, MaxReceivedMessageSize = int.MaxValue };
// add the binding elements into a Custom Binding
CustomBinding customBinding = new CustomBinding(binaryMessageEncoding,httpTransport);
// create the Endpoint URL (I'll use a configured URL later - all web services will then move as one)
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:50072/API/WCF/Silverlight/SilverlightHelper.svc");
// create an interface for the WCF service
ISilverlightHelper silverlightHelper= new ChannelFactory<ISilverlightHelper>(customBinding, endpointAddress).CreateChannel();
// set-up the asynchronous callback
AsyncCallback asyncCallback = delegate(IAsyncResult result)
{
ISilverlightHelper asyncSilverlightHelper = (ISilverlightHelper)result.AsyncState;
string[] files=asyncSilverlightHelper.EndGetPlugInXapNames(result).ToArray();
};
// execute the call
silverlightHelper.BeginGetPlugInXapNames(asyncCallback, silverlightHelper);
I have a simple web service, it takes 2 parameters one is a simple xml security token, the other is usually a long xml string. It works with short strings but longer strings give a 400 error message. maxMessageLength did nothing to allow for longer strings.
After the answer on quotas I just did all that in the web.config
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IPayroll" maxReceivedMessageSize="6553600">
<security mode="None"/>
<readerQuotas maxDepth="32"
maxStringContentLength="6553600"
maxArrayLength="16384"
maxBytesPerRead="4096"
maxNameTableCharCount="16384" />
</binding>
</wsHttpBinding>
</bindings>
You should remove the quotas limitations as well.
Here is how you can do it in code with Tcp binding.
I have added some code that shows removal of timeout problems because usually sending very big arguments causes timeout issues. So use the code wisely...
Of course, you can set these parameters in the config file as well.
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, true);
// Allow big arguments on messages. Allow ~500 MB message.
binding.MaxReceivedMessageSize = 500 * 1024 * 1024;
// Allow unlimited time to send/receive a message.
// It also prevents closing idle sessions.
// From MSDN: To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding.’
binding.ReceiveTimeout = TimeSpan.MaxValue;
binding.SendTimeout = TimeSpan.MaxValue;
XmlDictionaryReaderQuotas quotas = new XmlDictionaryReaderQuotas();
// Remove quotas limitations
quotas.MaxArrayLength = int.MaxValue;
quotas.MaxBytesPerRead = int.MaxValue;
quotas.MaxDepth = int.MaxValue;
quotas.MaxNameTableCharCount = int.MaxValue;
quotas.MaxStringContentLength = int.MaxValue;
binding.ReaderQuotas = quotas;