WCF - how to create programatically custom binding with binary encoding over HTTP(S) - wcf

I'd like to convert my current HTTP/HTTPS WCF binding settings to use binary message encoding and I need to do it in code - not in XML configuration. AFAIK it's necessary to create CustomBinding object and set proper BindingElements, but I'm not able to figure out what elements should I use in my scenario.
Main points in my WCF configuration are:
use HTTP or HTTPS transport depending on configuration (in app.config)
use username message security
todo: add binary encoding instead of default text
My current code for setting the binding up (working, but without the binary encoding):
var isHttps = Settings.Default.wcfServiceBaseAddress.StartsWith("https://", StringComparison.InvariantCultureIgnoreCase);
var binding = new WSHttpBinding(isHttps ? SecurityMode.TransportWithMessageCredential : SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
I was trying this code, but it doesn't work - I don't know how to set message security element for username message security:
var custBinding = new CustomBinding();
custBinding.Elements.Add(new BinaryMessageEncodingBindingElement());
//Transport Security (Not Required)
if (isHttps)
{
custBinding.Elements.Add(SecurityBindingElement.CreateUserNameForSslBindingElement());
}
//Transport (Required)
custBinding.Elements.Add(isHttps ?
new HttpsTransportBindingElement() :
new HttpTransportBindingElement());
Anybody knows how to set this up? I tried to search for similar problem/solution, but didn't succeeded...

I almost forgot this question, but here is my custom binding class which works with binary binding over HTTP with username+password validation and also allows to turn GZip compression on...
public class CustomHttpBinding: CustomBinding
{
private readonly bool useHttps;
private readonly bool useBinaryEncoding;
private readonly bool useCompression;
private readonly HttpTransportBindingElement transport;
public CustomHttpBinding(bool useHttps, bool binaryEncoding = true, bool compressMessages = false)
{
this.useHttps = useHttps;
transport = useHttps ? new HttpsTransportBindingElement() : new HttpTransportBindingElement();
useBinaryEncoding = binaryEncoding;
useCompression = compressMessages;
}
public long MaxMessageSize{set
{
transport.MaxReceivedMessageSize = value;
transport.MaxBufferSize = (int) value;
}}
public override BindingElementCollection CreateBindingElements()
{
BindingElement security;
if (useHttps)
{
security = SecurityBindingElement.CreateSecureConversationBindingElement(
SecurityBindingElement.CreateUserNameOverTransportBindingElement());
}
else
{
security = SecurityBindingElement.CreateSecureConversationBindingElement(
SecurityBindingElement.CreateUserNameForSslBindingElement(true));
}
MessageEncodingBindingElement encoding;
if (useCompression)
{
encoding = new GZipMessageEncodingBindingElement(useBinaryEncoding
? (MessageEncodingBindingElement)
new BinaryMessageEncodingBindingElement()
: new TextMessageEncodingBindingElement());
}
else
{
encoding = useBinaryEncoding
? (MessageEncodingBindingElement) new BinaryMessageEncodingBindingElement()
: new TextMessageEncodingBindingElement();
}
return new BindingElementCollection(new[]
{
security,
encoding,
transport,
});
}
}

The SecurityBindingElement has a AllowInsecureTransport property. If you set this to true you can use the HttpTransportBindingElement with message user name and password security.

Try SecurityBindingElement.CreateUserNameOverTransportBindingElement() instead:
var custBinding = new CustomBinding();
custBinding.Elements.Add(new BinaryMessageEncodingBindingElement());
//Transport Security (Not Required)
if (isHttps)
{
custBinding.Elements.Add(SecurityBindingElement.CreateUserNameOverTransportBindingElement());
}
//Transport (Required)
custBinding.Elements.Add(isHttps ?
new HttpsTransportBindingElement() :
new HttpTransportBindingElement());

Related

Proxy with CefSharp

how can I set up a proxy that has username and password in CefSharp browser in visual basic?
I tried with this code but doesn't work:
Dim proxy = "IP:PORT#USERNAME:PASSWORD"
Dim settings As New CefSettings()
settings.CefCommandLineArgs.Add("proxy-server", proxy)
Thanks
Your browser implement a IBrowser interface with GetHost method that allow you get RequestContext. You can set the proxy:
var requestContext = browser.GetHost().RequestContext;
var values = new Dictionary<string, object>
{
["mode"] = "fixed_servers",
["server"] = $"{proxyScheme}://{proxyHost}:{proxyPort}"
};
string error;
bool success = requestContext.SetPreference("proxy", values, out error);
To set user/password, you need to implement an IRequestHandler interface and implement this method:
public bool GetAuthCredentials(
IWebBrowser browserControl, IBrowser browser, IFrame frame, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
{
if (isProxy)
{
var browser2 = browserControl as IChromeRequestHandler;
var proxyOptions = browser2?.ProxySettings;
if (proxyOptions != null)
{
callback.Continue(proxyOptions.UserName, proxyOptions.Password);
return true;
}
}
callback.Dispose();
return false;
}
Then, you must set the RequestHandler property of your browser:
browser.RequestHandler = new YourIRequestHandlerImplementation();
Sorry for the C# implementation, but I think may be useful to you.

Apache CXF Password Type Always Sets Digest

I am working on a web service client project and using Apache CXF to send request to web service.
I need to set passwordType as PasswordText. But even if I set it in OutInterceptor property, It always sets passwordType as Digest. How can I solve this issue?
My Code is this:
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(Test.class);
factory.setAddress(url);
factory.getInInterceptors().add(new SoapActionInInterceptor(action));
factory.getOutInterceptors().add(new SoapActionOutInterceptor());
Map<String, Object> outProps = new HashMap<String, Object>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, username);
outProps.put(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PASSWORD_TEXT);
ClientPasswordHandler handler = new ClientPasswordHandler();
handler.setPassword(password);
outProps.put(WSHandlerConstants.PW_CALLBACK_REF, handler);
WSS4JStaxOutInterceptor wssOut = new WSS4JStaxOutInterceptor(outProps);
factory.getOutInterceptors().add(wssOut);
T serviceClient = (T) factory.create();
Client client = ClientProxy.getClient(serviceClient);
setClientPolicy(client);
And clientPolicy is this
protected synchronized void setClientPolicy(Client client) {
if (client != null) {
HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
httpConduit.setAuthSupplier(null);
httpConduit.setAuthorization(null);
HTTPClientPolicy clientPolicy = new HTTPClientPolicy();
clientPolicy.setConnectionTimeout(60000L);
clientPolicy.setReceiveTimeout(60000L);
httpConduit.setClient(clientPolicy);
}
}
org.apache.cxf -> version 3.1.6
org.apache.wss4j -> version 2.1.7
I have found the solution. WSS4JStaxOutInterceptor extends AbstractWSS4JStaxInterceptor and it has a function to set incoming properties which we have send. When it try to set password property it checks incoming property with "PasswordText" string and when we use WSConstants its value is different. That's why when we set property value with "PasswordText" string it works fine. Final code for interceptor is:
private WSS4JStaxOutInterceptor createSecurityInterceptor() {
Map<String, Object> outProps = new HashMap<>();
outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
outProps.put(WSHandlerConstants.USER, username);
// AbstractWSS4JStaxInterceptor class parseNonBooleanProperties require "PasswordText" check this function before changing this line
outProps.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordText");
// AbstractWSS4JStaxInterceptor class parseNonBooleanProperties require "PasswordText" check this function before changing this line
ClientPasswordHandler handler = new ClientPasswordHandler();
handler.setPassword(password);
outProps.put(WSHandlerConstants.PW_CALLBACK_REF, handler);
return new WSS4JStaxOutInterceptor(outProps);
}
This solves the issue.

DataContractSerializerOperationBehavior is not found when trying to use DataContractResolver

I am trying to use DataContractResolver as an alternative to KnownTypes in WCF.
I have the following code and I've used it before on the server side. But on the client side, the code returns null when trying to find DataContractSerializerOperationBehavior in operation behaviors collection.
public override IMyService CreateProxy(Uri url)
{
ServiceEndpoint endpoint = CreateEndpoint(url);
var channelFactory = new ChannelFactory<IMyService>(endpoint);
InjectResolver(channelFactory.Endpoint);
return channelFactory.CreateChannel();
}
private void InjectResolver(ServiceEndpoint endpoint)
{
foreach (OperationDescription operation in endpoint.Contract.Operations)
{
var behavior = operation.Behaviors.Find<DataContractSerializerOperationBehavior>();
behavior.DataContractResolver = new DerivedTypeResolver(); // behavior is null here!
}
}
Why is the behavior missing?
UPDATE: I found out the real issue is that WCF was using XmlSerializer instead of DataContractSerializer. Is there a way to force a DataContractSerializer instead? Does WCF choose the serializer based on the wsdl? Considering I don't (yet) have the capacity to change the server side, what is my option? XmlSerializer behavior doesn't seem to have a similar option of resolving the type myself.
See here for example on how to create DataContractSerializerOperationBehavior if it does not exist:
private void DataContractBehavior()
{
WSHttpBinding b = new WSHttpBinding(SecurityMode.Message);
Uri baseAddress = new Uri("http://localhost:1066/calculator");
ServiceHost sh = new ServiceHost(typeof(Calculator), baseAddress);
sh.AddServiceEndpoint(typeof(ICalculator), b, "");
// Find the ContractDescription of the operation to find.
ContractDescription cd = sh.Description.Endpoints[0].Contract;
OperationDescription myOperationDescription = cd.Operations.Find("Add");
// Find the serializer behavior.
DataContractSerializerOperationBehavior serializerBehavior =
myOperationDescription.Behaviors.
Find<DataContractSerializerOperationBehavior>();
// If the serializer is not found, create one and add it.
if (serializerBehavior == null)
{
serializerBehavior = new DataContractSerializerOperationBehavior(myOperationDescription);
myOperationDescription.Behaviors.Add(serializerBehavior);
}
// Change the settings of the behavior.
serializerBehavior.MaxItemsInObjectGraph = 10000;
serializerBehavior.IgnoreExtensionDataObject = true;
sh.Open();
Console.WriteLine("Listening");
Console.ReadLine();
}
example from https://msdn.microsoft.com/en-us/library/system.servicemodel.description.datacontractserializeroperationbehavior.aspx

How can I provide Metadata from my WCF service for consumption in Breeze

I am trying to adapt an existing WebApi/MVC4 app to use Breeze lookups.
Currently I retrieve my DTOs via
[HttpGet]
public IQueryable<ThingDto> GetThings()
{
var channelFactory = ThingServiceConfiguration.CreateChannelFactory();
_serviceFactory = () => new WcfProxy<IThingService>(channelFactory.CreateChannel());
var client = _serviceFactory();
IQueryable<ThingDto> result = client.Execute(p => p.GetThings()).OrderBy(x => x.Name).AsQueryable();
return result;
}
I'm not sure how I implement this method Metadata()
public string Metadata()
{
//normally something like this if using a EF DataContext
// return _someContextProvider.Context.Things;
}
How I setup the WCF config
public class ThingServiceConfiguration
{
const string AppSettingKey = "ThingServiceUrl";
public static ChannelFactory<IThingService> CreateChannelFactory()
{
// var serviceUrl = ConfigurationManager.AppSettings[AppSettingKey];
var serviceUrl = "http://localhost:86/ThingService.svc";
var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
MaxReceivedMessageSize = 200000000,
SendTimeout = TimeSpan.FromMinutes(2),
ReceiveTimeout = TimeSpan.FromMinutes(2)
};
var address = new EndpointAddress(serviceUrl);
return new ChannelFactory<IThingService>(binding, address);
}
}
Could this metadata be provided with the WCF call into the Metadata() property (by providing arguments through BasicHttpBinding ?
Many thanks!
You can return Breeze 'native' metadata simply by returning the metadata in json form. Something like this:
[HttpGet]
public String Metadata() {
var folder = Path.Combine(HttpRuntime.AppDomainAppPath, "App_Data");
// metadata.json is the name of a file containing your metadata - pick any file name you like.
var fileName = Path.Combine(folder, "metadata.json");
var jsonMetadata = File.ReadAllText(fileName);
return jsonMetadata;
}
where the syntax of the metadata file is described here: Breeze metadata format.

Dealing with Azure staging crazy URL

i'm deploying a webrole in azure that contains a web-site and a wcf service...
The site consumes services from the wcf.
The problem here is that the staging deploy creates a crazy url for the endpoints and i have to keep changing the endpoints in the web.config...
I'm wondering if theres a way to either "predict" what the url will be or to force one or even point to a generic host such as "localhost"???
You should be able to use role discovery to find the WCF endpoint. See this SO answer here and the blog post it links to.
My own abstract base class for connecting to azure services was based on that article. It uses role discovery to crate a channel like this:
#region Channel
protected String roleName;
protected String serviceName;
protected String endpointName;
protected String protocol = #"http";
protected EndpointAddress _endpointAddress;
protected BasicHttpBinding httpBinding;
protected NetTcpBinding tcpBinding;
protected IChannelFactory channelFactory;
protected T client;
protected virtual AddressHeader[] addressHeaders
{
get
{
return null;
}
}
protected virtual EndpointAddress endpointAddress
{
get
{
if (_endpointAddress == null)
{
var endpoints = RoleEnvironment.Roles[roleName].Instances.Select(i => i.InstanceEndpoints[endpointName]).ToArray();
var endpointIP = endpoints.FirstOrDefault().IPEndpoint;
if(addressHeaders != null)
{
_endpointAddress = new EndpointAddress(new Uri(String.Format("{1}://{0}/{2}", endpointIP, protocol, serviceName)), addressHeaders);
}
else
{
_endpointAddress = new EndpointAddress(String.Format("{1}://{0}/{2}", endpointIP, protocol, serviceName));
}
}
return _endpointAddress;
}
}
protected virtual Binding binding
{
get
{
switch (protocol)
{
case "tcp.ip":
if (tcpBinding == null) tcpBinding = new NetTcpBinding();
return tcpBinding;
default:
//http
if (httpBinding == null) httpBinding = new BasicHttpBinding();
return httpBinding;
}
}
}
public virtual T Client
{
get
{
if (this.client == null)
{
this.channelFactory = new ChannelFactory<T>(binding, endpointAddress);
this.client = ((ChannelFactory<T>)channelFactory).CreateChannel();
((IContextChannel)client).OperationTimeout = TimeSpan.FromMinutes(2);
var scope = new OperationContextScope(((IContextChannel)client));
addCustomMessageHeaders(scope);
}
return this.client;
}
}
#endregion
And in a derived class I pass it the following variables (for example):
this.roleName = "WebServiceRole";
this.endpointName = "HttpInternal";
this.serviceName = "services/Accounts.svc";
I never need to refer to the staging (or production) URLs at all.
See my answer here for more details: Add WCF reference within the same solution without adding a service reference
There is no way to either predict the GUID, control it, or use some constant name.
What you can do, to make things easier, is to move the URL into .CSCFG and update the URL of the WCF service from Azure Management Portal