Silverlight not sending byte array to WCF service - c#

I have webcam in a silverlight app. I capture the image and convert it to byte array and send to WCF service. Here is the image capture code:
MessageBox.Show("about to capture");
cs.CaptureImageAsync();
cs is the camera source and is configured correctly (as is the imagecapturecompleted event). On image capture completion, this code executes:
MessageBox.Show("Image Caputred");
var img = e.Result.ToImage();
var encoder = new PngEncoder();
Stream stream = img.ToStreamByExtension("png");
byte[] file = null;
if (stream.Length > 512000)
{
img = ExtendedImage.Resize(img, 240, new NearestNeighborResizer());
stream = img.ToStreamByExtension("png");
}
if (stream.Length < 512000)
{
BinaryReader binary = new BinaryReader(stream);
file = binary.ReadBytes((int)stream.Length);
MessageBox.Show("Stream read into file with length: " + file.Length);
}
else
{
MessageBox.Show("file size too large");
}
MessageBox.Show("Process done");
cs.Stop();
label1.Content = "and answer is : " + file!= null ? file.Length.ToString() : "ERROR";
ServiceReference1.Service1Client obj = new ServiceReference1.Service1Client();
ServiceReference1.ITEM i = new ServiceReference1.ITEM { Image = file };
obj.DoWorkCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(obj_DoWorkCompleted);
obj.DoWorkAsync(i);
This is my web.config in the asp.net project that configures the service:
<system.serviceModel>
<services>
<service name ="AttendanceSystem.IService1" behaviorConfiguration="BasicHttpBinding_IService1">
<endpoint address="" binding="basicHttpBinding" contract="AttendanceSystem.IService1"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="BasicHttpBinding_IService1">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true"/>
And here is the reference to the service in the silverlight app
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:48886/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1"
name="default" />
</client>
</system.serviceModel>
Fiddler returns the following message on the call to my .svc service file:
HTTP/1.1 400 Bad Request
Server: ASP.NET Development Server/10.0.0.0
Date: Wed, 15 Jan 2014 09:17:26 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: private
Content-Length: 0
Connection: Close
So what is going on?
UPDATE: the service seems to be working without error when I pass a smaller byte array (for example BitConverter.GetBytes(123) but fails when the image is sent which is clearly within the max limits)

Try to configure the binding server-side (maxReceivedMessageSize and reader quotas)
<bindings>
<basicHttpBinding>
<binding maxReceivedMessageSize="2147483647">
<readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647"/>
</binding>
</basicHttpBinding>
</bindings>

Related

POST request to WCF REST (json) - WebException (400) bad request

I wanna upload a file to db by web Service (REST - WCF) but I have WebException (400) bad request, I read many solution but my code still not working!
.config
<system.serviceModel>
<bindings>
<webHttpBinding>
<!--Limits to 10MB-->
<binding name="ApiQuotaBinding"
maxReceivedMessageSize="1048576000"
maxBufferPoolSize="1048576000"
maxBufferSize="1048576000"
closeTimeout="00:03:00"
openTimeout="00:03:00"
receiveTimeout="00:03:00"
sendTimeout="00:03:00"
>
<readerQuotas maxDepth="32"
maxStringContentLength="104857600"
maxArrayLength="1048576000"
maxBytesPerRead="1048576000"
/>
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
<services>
<service name="TransferService">
<endpoint address=""
binding="webHttpBinding"
bindingConfiguration="ApiQuotaBinding"
contract="ITransferService"
behaviorConfiguration="webHttpBehavior"/>
<endpoint address="mex"
contract="IMetadataExchange"
binding="mexHttpBinding"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior >
<!--To avoid disclosing metadata information, set the values below to false before deployment-->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="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>
this is my web method:
public Guid UploadFile(byte[] ByteStream)
{
Guid id = Guid.Empty;
//upload
using (RepoDbWave dbc = new RepoDbWave())
{
FileItem f = new FileItem();
var count_row = dbc.FileItems.Count(a => a.ID != Guid.Empty);
f.FileContent = ByteStream;
f.FileSize = f.FileContent.Length;
f.Time = DateTime.Now;
FileItem newItem = dbc.FileItems.Add(f);
dbc.SaveChanges();
id = newItem.ID;
}
return id;
}
and my request code:
private void btnUpload_Click(object sender, EventArgs e)
{
OpenFileDialog open = new OpenFileDialog();
open.Filter = "Wave files (*.*)|*.*";
if (open.ShowDialog() == DialogResult.OK)
{
string WaveLocation = open.FileName;
txtUpload.Text = WaveLocation;
byte[] WavebyteArray = File.ReadAllBytes(WaveLocation);
///webClient//////////////////////////////////////////////
WebClient Proxy1 = new WebClient();
Proxy1.Headers["Content-type"] = "application/json";
MemoryStream ms = new MemoryStream();
DataContractJsonSerializer serializerToUplaod = new DataContractJsonSerializer(typeof(byte[]));
serializerToUplaod.WriteObject(ms, WavebyteArray);
byte[] data = Proxy1.UploadData("http://localhost:1866/TransferService.svc/UploadFile", "POST", ms.ToArray());
MemoryStream stream = new MemoryStream(data);
DataContractJsonSerializer obj = new DataContractJsonSerializer(typeof(byte[]));
var guID = obj.ReadObject(stream);
lblUpload.Text = guID.ToString();
//////////////////////////////////////////////////////////
}
I think the problem is you are setting the content-type as application/json. But you are passing a byte array in the body. This might confuse the WCF. Instead of using content-type as json try using any stream.

function called twice with WCF

I am using a DuplexChannelFactory to connect my client to my server and what I want to do now is connect the server to each client that will register on the server. But every time I launch a client, my "init()" function is ran twice. And my DuplexChannelFactory on the server side doesn't work.
Here is how I open my server for clients :
host = new ServiceHost(typeof(NewServeur.NewServeur));
host.Open();
with its App.Config :
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MEX">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MEX" name="NewServeur.NewServeur">
<endpoint address="IProxyNewServeur" binding="netTcpBinding"
bindingConfiguration="configWin" name="NetTcpEndpoint" contract="ProxyNewServeur.IProxyNewServeur" />
<endpoint address="MEX" binding="mexTcpBinding" bindingConfiguration=""
name="MexTcpEndpoint" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://192.156.142.176:8080/" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="configWin">
<security mode="None"></security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
Here is how I connect my client to my server :
void init()
{
ServiceCablingCallback ServiceCablingCallback = new ServiceCablingCallback();
ServiceCablingCallback.ClientNotified += ServiceCablingCallback_ClientNotified;
this._instanceContext = new InstanceContext(ServiceCablingCallback);
this.factory = new DuplexChannelFactory<ProxyNewServeur.IProxyNewServeur>(_instanceContext, "configClient");
this._serviceCablingClient = factory.CreateChannel();
this._clientId = _serviceCablingClient.Subscribe();
}
with its App.Config
<system.serviceModel>
<client>
<endpoint address="net.tcp://192.156.142.176:8080/IProxyNewServeur"
binding="netTcpBinding" bindingConfiguration="configWin" contract="ALSTEF.Cabling.ProxyNewServeur.IProxyNewServeur"
name="configClient" />
</client>
<bindings>
<netTcpBinding>
<binding name="configWin">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
This works fine, but now I want to do the reverse thing, meaning connecting the server to the client. In order to do so, I added those lines before the last line of my client :
host = new ServiceHost(typeof(Service), new Uri(_serviceCablingClient.getIp()));
host.Open();
with getIp() returning the Ip address + the port 4242 of my client.
and this to the client App.config
<behaviors>
<serviceBehaviors>
<behavior name="MEX">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="MEX" name="Client.Model.Service">
<endpoint address="IClientFunction" binding="netTcpBinding" bindingConfiguration="ClientConfig"
name="NetTcpEndpoint" contract="ProxyNewServeur.IClientFunction" />
<endpoint address="MEX" binding="mexTcpBinding" bindingConfiguration=""
name="MexTcpEndpoint" contract="IMetadataExchange" />
</service>
</services>
<!-- in the bindings html tag -->
<binding name="ClientConfig">
<security mode="None" />
</binding>
And I modified the _serviceCablingClient.Subscribe() by adding those lines (so in the server)
public Guid Subscribe()
{
ProxyNewServeur.INewServeurCallback callback =
OperationContext.Current.GetCallbackChannel<ProxyNewServeur.INewServeurCallback>();
System.ServiceModel.Channels.RemoteEndpointMessageProperty endpointProperty =
OperationContext.Current.IncomingMessageProperties[System.ServiceModel.Channels.RemoteEndpointMessageProperty.Name] as System.ServiceModel.Channels.RemoteEndpointMessageProperty;
Guid clientId = Guid.NewGuid();
if (callback != null)
{
lock (clientsCallback)
{
clientsCallback.Add(clientId, callback);
}
}
/* code I added */
string addr = "net.tcp://" + endpointProperty.Address + ":" + "4242" + "/IClientFunction";
ClientCallback cb = new ClientCallback();
var Okvp = new KeyValuePair<Guid, DuplexChannelFactory<ProxyNewServeur.IClientFunction>>(clientId,
new DuplexChannelFactory<ProxyNewServeur.IClientFunction>(new InstanceContext(cb), "configServer",
new EndpointAddress(addr)));
this.DictClient.Add(Okvp, Okvp.Value.CreateChannel());
this.DictClient[Okvp].CreateCallbackChannel();
/* end code I added */
}
And I added this to the server App.config
<client>
<endpoint binding="netTcpBinding" bindingConfiguration="ClientConfig" contract="ProxyNewServeur.IClientFunction" name="configServer" />
</client>
<!-- in the bindings html tag -->
<binding name="ClientConfig">
<security mode="None" />
</binding>
Here is how I define my ProxyServeur file
namespace ProxyNewServeur
{
[ServiceContract(CallbackContract = typeof(INewServeurCallback))]
public interface IProxyNewServeur
{
[OperationContract]
Guid Subscribe();
[OperationContract]
string getIp();
}
[ServiceContract]
public interface INewServeurCallback
{
[OperationContract(IsOneWay = true)]
void HandleMessage(Message message);
}
[ServiceContract(CallbackContract = typeof(IClientCallback))]
public interface IClientFunction
{
[OperationContract(IsOneWay = true)]
void CreateCallbackChannel();
}
[ServiceContract]
public interface IClientCallback
{
[OperationContract(IsOneWay = true)]
void BroadcastToServer(bool isActive, Guid ClientId);
}
}
Now When I launch a client the line
DictClient[Okvp].CreateCallbackChannel();
won't work and I will go twice in the
init()
{ /* code*/
}
function.
I have no idea why this is happening. I did the exact same thing in a test project and it worked fine.
EDIT
As it turns out, you cannot have a DuplexChannelFactory<> and a ServiceHost in the same method. So what I did is that I added a new Class "DoubleConnection" that have only one attribute.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class DoubleConnection : ProxyNewServeur.IClientFunction
{
public ServiceHost host;
public void CreateCallbackChannel()
{}
public void isActive()
{}
}
and in the init() function I removed
host = new ServiceHost(typeof(Service), new Uri(_serviceCablingClient.getIp()));
host.Open();
and added
varClass = new DoubleConnection();
varClass.host = new ServiceHost(typeof(DoubleConnection), new Uri(_serviceCablingClient.getIp()));
varClass.host.Open();
I works fine now but I still don't know why you cannot have a DuplexChannelFactory<> and a ServiceHost in the same method.

WCF Service- The protocol 'https' is not supported

I am developing a WCF service using WsHttpBinding and an SSL certificate for security. Works perfectly in my local IIS, but when I publish I get the following error message;
The protocol 'https' is not supported.
Here is my web.config file..
<system.serviceModel>
<services>
<service name="PeopleService.Service.PeopleService">
<host>
<baseAddresses>
<add baseAddress="https://www.mywebsite.com/service/"/>
</baseAddresses>
</host>
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="BasicBinding" contract="PeopleService.Service.IPeopleService" name="BasicEndpoint"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="BasicBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
I am using a ServiceHost factory to add my endpoints, so that I can load the SSL certificate from a physical file rather than from the certificate store.
public class PeopleServiceHost : ServiceHost
{
public PeopleServiceHost(params Uri[] addresses) : base(typeof(PeopleService), addresses)
{
}
protected override void InitializeRuntime()
{
Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
Description.Behaviors.Find<ServiceDebugBehavior>().HttpsHelpPageUrl = new Uri("https://www.mywebsite.com/service/PeopleService.svc/mex");
ServiceMetadataBehavior metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpsGetEnabled = true;
metadataBehavior.HttpsGetUrl = new Uri("https://www.mywebsite.com/service/PeopleService.svc/mex");
Description.Behaviors.Add(metadataBehavior);
var serviceCredentials = new ServiceCredentials();
serviceCredentials.ServiceCertificate.Certificate = new X509Certificate2(AppDomain.CurrentDomain.BaseDirectory + "\\mywebsite.pfx", "password123", X509KeyStorageFlags.MachineKeySet);
Description.Behaviors.Remove((typeof(ServiceCredentials)));
Description.Behaviors.Add(serviceCredentials);
base.InitializeRuntime();
}
}
I've been trying to solve this problem for days, but to no avail. I've contacted by host and they have advised me that HTTPS is definitely supported and enabled in IIS.
Any help is really appreciated. Thanks.

How can I enable Security in LogReceiverService (NLog)

I have to make a centralized log repository and I decided to mount a WCF service implementing NLog's LogReceiverService (through wsHttpBinding). I followed this topic where I found a working example (there is a working code at bitbucket).
Ok, now the problem: I would like to add some security to this WCF Service, expose it through HTTPS and maybe add an Authentication Token. I have programmed this kind of authentication earlier, so I do know how to do it, it's just I don't know how can I program that within NLog. Should I modify the Class where NLog makes the call to the WCF Method? I just can't picture how to do it. Any ideas about how to achieve this functionality is really appreciated.
Finally I was able to do this.
Let me tell you I was able to configure the desired behavior :)
First we configure the server as follows:
The configuration of System.ServiceModel for the web.config of the WCFService is:
<system.serviceModel>
<services>
<service name="Your.Namespace.Path.To.Your.Service" behaviorConfiguration="SecureBehavior">
<endpoint binding="wsHttpBinding" bindingConfiguration="SecureBinding" contract="NLog.LogReceiverService.ILogReceiverServer"/>
<endpoint binding="mexHttpBinding" contract="IMetadataExchange" address="mex"/>
<host>
<baseAddresses>
<add baseAddress="https://your_secure_domain.com/servicePath"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SecureBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpsGetEnabled="true"/>
<serviceCredentials>
<!--You must set your certificate configuration to make this example work-->
<serviceCertificate findValue="0726d1969a5c8564e0636f9eec83f92e" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySerialNumber"/>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="AssamblyOf.YourCustom.UsernameValidator.UsernameValidator, AssamblyOf.YourCustom.UsernameValidator"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="SecureBinding" closeTimeout="00:00:20" openTimeout="00:00:20" receiveTimeout="00:00:20" sendTimeout="00:00:20">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
<transport clientCredentialType="None"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
The CustomUserNameValidator
public class UsernameValidator : UserNamePasswordValidator
{
private const string UserName = "your_username_here";
private const string Password = "your_password_here";
public override void Validate(string userName, string password)
{
// validate arguments
if (string.IsNullOrEmpty(userName))
throw new ArgumentNullException("userName");
if (string.IsNullOrEmpty(password))
throw new ArgumentNullException("password");
//
// Nombre de usuario y contraseñas hardcodeados por seguridad
//
if (!userName.Equals(UserName) || !password.Equals(Password))
throw new SecurityTokenException("Nombre de usuario o contraseña no válidos para consumir este servicio");
}
}
then we go to the Client configuration
First, create a inherited class from LogReceiverWebServiceTarget and I override the method CreateWcfLogReceiverClient, then in that method add the credentials.
// we assume that this class is created in NLog.CustomExtendedService namespace
[Target("LogReceiverSecureService")]
public class LogReceiverSecureService : NLog.Targets.LogReceiverWebServiceTarget
{
/// <summary>
/// Gets or sets the UserName of the service when it's authentication is set to UserName
/// </summary>
/// <value>The name of the endpoint configuration.</value>
public string ServiceUsername { get; set; }
/// <summary>
/// Gets or sets de Password of the service when it's authentication is set to UserName
/// </summary>
public string ServicePassword { get; set; }
/// <summary>
/// Creates a new instance of WcfLogReceiverClient.
///
/// We make override over this method to allow the authentication
/// </summary>
/// <returns></returns>
protected override NLog.LogReceiverService.WcfLogReceiverClient CreateWcfLogReceiverClient()
{
var client = base.CreateWcfLogReceiverClient();
if (client.ClientCredentials != null)
{
//
// You could use the config file configuration (this example) or you could hard-code it (if you do not want to expose the credentials)
//
client.ClientCredentials.UserName.UserName = this.ServiceUsername;
client.ClientCredentials.UserName.Password = this.ServicePassword;
}
return client;
}
}
Then we set up the application's config file
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ILogReceiverServer">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
<transport clientCredentialType="None" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://your_secure_domain.com/servicePath/Logger.svc" binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_ILogReceiverServer" contract="NLog.LogReceiverService.ILogReceiverClient"
name="WSHttpBinding_ILogReceiverServer" />
</client>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Finally we configure the NLog.config
<extensions>
<add assembly="NLog.CustomExtendedService" /> <!--Assuming the custom Target was added to this assambly -->
</extensions>
<targets>
<target xsi:type="LogReceiverSecureService"
name="RemoteWcfLogger"
endpointConfigurationName="WSHttpBinding_ILogReceiverServer"
endpointAddress="https://your_secure_domain.com/servicePath/Logger.svc"
ServiceUsername="your_username_here"
ServicePassword="your_password_here"
useBinaryEncoding="True"
clientId="YourApplicationNameOrId"
includeEventProperties="True">
</target>
</targets>
I posted an entire answer at the googlegroup of NLog, so enjoy it
https://groups.google.com/d/msg/nlog-users/Xryu61TaZKM/Utbvrr5mwA0J

WCF Custom authentication - HTTP status 401: Unauthorized

WCF Service WebConfig(partial).
<services>
<service name="Bricks99.LicensingServer.LicensingService"
behaviorConfiguration="Bricks99ServiceBehavior">
<!-- use base address specified above, provide one endpoint -->
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="Bricks99Binding"
contract="Bricks99.LicensingServer.ILicensingService" />
<!--<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />-->
</service>
</services>
<basicHttpBinding>
<binding name="Bricks99Binding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Bricks99.LicensingServer.CCustomValidatorClass, Bricks99.LicensingServer"/>
</serviceCredentials>
Client is ASP.NET 2.0
LicensingService service = new LicensingService();
//Authentication
CredentialCache credCache = new CredentialCache();
credCache.Add(new Uri(service.Url), "Basic", new NetworkCredential("Test", "1234567"));
service.Credentials = credCache;
service.PreAuthenticate = true;
service.UseDefaultCredentials = false;
result = service.VerifyLicenseKey(licenseKey, string.Empty);
The result is always The request failed with HTTP status 401: Unauthorized. I have also turned off Anonymous Access on the folder. Its still not working.
Any ideas on how to set the credentials correctly?
EDIT: Seems like the overridden method
public override void Validate(string userName, string password)
{
try
{
if(userName != "Test" || password != "1234567")
{
throw new FaultException("The provided credentials are invalid.");
}
}
catch(FaultException ex)
{
LicensingData.Operations.LogError(ex);
}
}
is never getting hit.
Well after hours of research I found out that the hosting provider did not allow basic authentication by default. For custom authentication to work it is necessary that basic authentication be enabled in IIS.

Categories