Disable gzip compression in request to GeoCoder web site - wcf

I have a example application written in Delphi using /N Software's SOAP component. This application communicates with the GeoCoder site (see geocoder.us) to get latitude and longitude from a provided address. Works ok.
Wrote a very simple WCF client to basically do the same thing but it does not work. I used Fiddler2 to see what is happening.
The Delphi application gets back basic text. My WCF application gets back content that is gzip compressed. This must be the default as I did not set this. When the response comes back there is no means to decompress the data.
I see many many web pages going into detail about setting gzip compression but nothing about disabling this.
The question is how can I get my WCF client to request from the GeoCoder site, a response in plain text (I know that compression is good etc) but in this instance I just want to see this working.
The sites WSDL is at http://rpc.geocoder.us/dist/eg/clients/GeoCoder.wsdl
My source code (its very elementary as I learning this stuff)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using GeoCodeTest.ServiceReference1;
namespace GeoCodeTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private GeocoderResult[] LocationResult = null;
private GeoCode_PortTypeClient proxy = null;
private void btnGo_Click(object sender, EventArgs e)
{
LocationResult = proxy.geocode(txtLocation.Text); // always returns a null !!!
geocoderResultBindingSource.DataSource = LocationResult;
}
private void Form1_Load(object sender, EventArgs e)
{
proxy = new GeoCode_PortTypeClient("GeoCode_Port");
}
}
}
This is the app.config file that was generated.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="GeoCode_Binding" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://rpc.geocoder.us/service/soap/" binding="basicHttpBinding"
bindingConfiguration="GeoCode_Binding" contract="ServiceReference1.GeoCode_PortType"
name="GeoCode_Port" />
</client>
</system.serviceModel>
</configuration>
Is there something I can add/remove/change to the app.config so that a WCF client request will ask for plain text like the Delphi application?

Server should compress the response only if your request contains HTTP header Accept-Encoding: gzip. Check your request with Fiddler. If this header is not present in your request, the compression is controlled by some custom mechanism.
Only WCF 4 HTTP based client sends that HTTP header because it is out of the box able to decompress the message. If it doesn't there is something wrong with the message. You can turn off this feature by defining custom binding:
<bindings>
<customBinding>
<binding name="BasicWithNoCompression">
<textMessageEncoding messageVersion="Soap11" />
<httpTransport decompressionEnabled="false" />
</binding>
</customBidning>
</bindings>

I had a quick look at this, and it is NOT the compression that is causing the issue. As already mentioned, WCF clients (v4) can decompress the data fine. In fact, if you modify the outgoing request message header and remove the "gzip, deflate" from the Accept-Encoding header the response comes back already uncompressed, however the data is still not present in the return object. There seems to be an issue with the way the client has generated its datacontract I think.
If I have some more time, I will have a closer look but it may take sometime to find out where the exact mismatch(s) occur.

Related

Programmatically adding an endpoint

I have a WCF service that I am connecting in client application. I am using following in configuration file.
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="MyNameSpace.TestService" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="2147483647" maxBufferPoolSize="524288" maxReceivedMessageSize="2147483647"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:9100/TestService" binding="basicHttpBinding"
bindingConfiguration="MyNameSpace.TestService" contract="TestService.IService" name="MyNameSpace.TestService" />
</client>
</system.serviceModel>
In the code, I am calling API on this service as follows,
TestServiceClient client = new TestServiceClient()
client.BlahBlah()
Now I want to defined endpoint porgramatically. How can that be done? I commented out section from config file as I was thinking I will have to put some code on TestServiceClient instance to add endpoint dynamically but then it throws following exception at the point where TestServiceClient is instantiated.
Could not find default endpoint element that references contract
'TestService.IService' in the ServiceModel client configuration
section. This might be because no configuration file was found for
your application, or because no endpoint element matching this
contract could be found in the client element.
How can I accomplish this? Also any point on code examples for adding endpoint programmatically will be appreciated.
To create endpoints and bindings programmatically, you could do this on the service:
ServiceHost _host = new ServiceHost(typeof(TestService), null);
var _basicHttpBinding = new System.ServiceModel.basicHttpBinding();
//Modify your bindings settings if you wish, for example timeout values
_basicHttpBinding.OpenTimeout = new TimeSpan(4, 0, 0);
_basicHttpBinding.CloseTimeout = new TimeSpan(4, 0, 0);
_host.AddServiceEndpoint(_basicHttpBinding, "http://192.168.1.51/TestService.svc");
_host.Open();
You could also define multiple endpoints in your service config, and choose which one to connect to dynamically at run time.
On the client program you would then do this:
basicHttpBinding _binding = new basicHttpBinding();
EndpointAddress _endpoint = new EndpointAddress(new Uri("http://192.168.1.51/TestService.svc"));
TestServiceClient _client = new TestServiceClient(_binding, _endpoint);
_client.BlahBlah();
Can you just use:
TestServiceClient client = new TestServiceClient();
client.Endpoint.Address = new EndPointAddress("http://someurl");
client.BlahBlah();
Note that your binding configuration will no longer apply, as you're not using that endpoint configuration in your configuration file. You'll have to override that, too.
You can try:
TestServiceClient client = new TestServiceClient("MyNameSpace.TestService")
client.BlahBlah()
if not recheck namespace in file TestService is correct?

How do I consume a WS-Security service in WCF over HTTPS?

I'm trying to consume a WS-Security enabled service with WCF. Authentication works using a UsernameToken. I'm not very knowledgeable with WCF web service clients, but I think my configuration below works for regular HTTP communication. I (mostly) used this guide to configure it. The main difference is that I used the VS2010 "Add Service Reference" UI instead of a command prompt.
My problem is that I need to do this over HTTPS. When I use <security mode="Message"> in my app.config, I believe my soap envelope contains the needed WS-Security headers. I can't tell for sure because I can't get logging to work. However, I get the following error: The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via.
Below are the contents of my app.config file, as well as a sample of my client code.
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="Omitted" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Message">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" negotiateServiceCredential="false" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://omitted.com/service" binding="wsHttpBinding" bindingConfiguration="Omitted" contract="Omitted.Omitted" name="Omitted" />
</client>
</system.serviceModel>
var service = new OmittedClient();
service.ClientCredentials.UserName.UserName = "username";
service.ClientCredentials.UserName.Password = "password";
var response = service.DoSomething(new DoSomethingRequest());
Thanks to 500 - Internal Server error for helping me figure it out. Here are the steps I took:
Generate a proxy using Visual Studio/WCF.
Change the security mode to TransportWithmessageCredential.
Use the following code to specify a username/password.
var client = new WebServiceClient();
client.ClientCredentials.UserName.UserName = "USERNAME";
client.ClientCredentials.UserName.Password = "PASSWORD";
If you get a response back, but WCF complains when processing it, there might be a timestamp missing from the response. If that's the case, try this to fix it.
// WCF complains about a missing timestamp (http://www.west-wind.com/weblog/posts/2007/Dec/09/Tracing-WCF-Messages)
var elements = service.Endpoint.Binding.CreateBindingElements();
elements.Find().IncludeTimestamp = false;
service.Endpoint.Binding = new CustomBinding(elements);

How do I support streaming in WSFederationHttpBinding?

I have a wcf service which is used to upload and download large files to server. I'm using MTOM message encoding and I want to use streamed transfer mode. But we are using wsFederationHttpBinding. How do I support streaming in wsFederationHttpBinding?
My WCF Service web.config code is given below,
<wsFederationHttpBinding>
<binding name="UploadserviceFederation"
messageEncoding="Mtom"
maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647" >
<readerQuotas maxStringContentLength="2147483647"
maxDepth="2147483647"
maxBytesPerRead="2147483647"
maxArrayLength="2147483647"/>
<security mode="TransportWithMessageCredential">
<!-- Ping token type MUST be SAML 1.1, do not change -->
<message
issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" negotiateServiceCredential="false">
<!-- TODO: You must put the proper issuer URN of the Ping STS; normally this would be the Ping base URL -->
<issuer address="https://my-issuer.com" binding="customBinding" bindingConfiguration="FileUploadSTSBinding" />
</message>
</security>
</binding>
</wsFederationHttpBinding>
<customBinding>
<binding name="FileUploadSTSBinding">
<security authenticationMode="UserNameOverTransport" requireDerivedKeys="false"
keyEntropyMode="ServerEntropy" requireSecurityContextCancellation="false"
requireSignatureConfirmation="false">
</security>
<httpsTransport maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
It's been a few years, so I don't know if this still helps, but I came across this post while trying to figure out the same issue, so it might help someone.
As it turns out, it's actually pretty simple..once you get the dance just right.
Probably the easiest thing (and what I tried first) is to inherit from WS2007FederationHttpBinding. As it turns out, it has a GetTransport method that's virtual, so you can override it and return an instance of HttpsTransport with TransferMode set to Streamed:
public class FileUploadSTSBinding : WS2007FederationHttpBinding
{
protected override TransportBindingElement GetTransport()
{
return new HttpsTransportBindingElement()
{
TransferMode = TransferMode.Streamed
};
}
}
However, doing this revealed something else: since my binding was no longer a recognized binding type, svcutil didn't treat it like a WS2007FederationHttpBinding anymore, but rather as a custom binding, which lead to the client-side configuration being generated as a stack of binding elements rather than using the shortcuts provided by the federation binding:
<customBinding>
<binding name="CustomBinding_ISdk">
<security defaultAlgorithmSuite="Default" authenticationMode="IssuedTokenOverTransport"
requireDerivedKeys="true" includeTimestamp="true" messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10">
<issuedTokenParameters keyType="BearerKey">
<additionalRequestParameters>
<trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<trust:KeyType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
</trust:SecondaryParameters>
</additionalRequestParameters>
</issuedTokenParameters>
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
</security>
<textMessageEncoding />
<httpsTransport />
</binding>
..which shows what the underlying binding elements actually are, which lets you tweak them all you like. And, as it turns out, they're really not that different from the actual binding since the only really special part is the security element, and it doesn't change much.
Hope that helps.
You will have to enable streamed transfer mode in a custom binding since only the BasicHttpBinding, NetTcpBinding and NetNamedPipeBinding bindings expose the TransferMode property. See this article for an example.

File Transfer via WCF

I'm a bit of the newbie to WCF so i would really appreciate if you could answer as detailed as possible :) i have a WCF service library and a WPF application (who is a client). the wanted result is an application that will enable file sharing between the connected clients.I build a really basic WCF service library with one method:
[ServiceContract]
public interface IFileService
{
[OperationContract]
byte[] GetFile(string fullPath);
}
And implemented this method like this:
public class FileService : IFileService
{
public byte[] GetFile(string fullPath)
{
return System.IO.File.ReadAllBytes(fullPath);
}
}
This is the App.config file in the WPF client project:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IFileService" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:9355/TankusFileTransferService/Service/"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IFileService"
contract="TankusFileService.IFileService" name="WSHttpBinding_IFileService">
<identity>
<userPrincipalName value="GIL-LAPTOP\Gil" />
</identity>
</endpoint>
</client>
</system.serviceModel>
And this is the code from the main window WPF application:
public partial class MainWindow : Window
{
ServiceHost sh;
TankusFileService.FileServiceClient fsc;
public MainWindow()
{
InitializeComponent();
}
private void btn_Connect_Click(object sender, RoutedEventArgs e)
{
Uri uri = new Uri("http://127.0.0.1:1234/");
sh = new ServiceHost(typeof(TankusFileTransferService.FileService), uri);
sh.Open();
lbl_Listener.Content = sh.Description.Endpoints[0].Address.ToString();
}
private void btn_Disconnect_Click(object sender, RoutedEventArgs e)
{
sh.Close();
lbl_Listener.Content = string.Empty;
}
private void btn_GetFile_Click(object sender, RoutedEventArgs e)
{
fsc = new TankusFileService.FileServiceClient();
fsc.Endpoint.Address = new EndpointAddress("http://127.0.0.1:1234/");
fsc.Endpoint.Binding = new BasicHttpBinding();
byte[] bytes = fsc.GetFile(#"D:\mika.txt");
System.IO.File.WriteAllBytes(#"D:\mika_new.txt", bytes);
}
}
After i press the connect button and initialize the ServiceHost object so it can start listening i press the getFile button. when the GetFile() function is called it throws a TimeoutException. Why is this? am i even on the right way for accomplishing my wanted application? Thanks :)
You are likely getting a TimeoutException because it is taking longer to send the file than is allowed by your service.
In your config file for both the server and the client be sure to increase the receiveTimeout and sendTimeout.
You may also bump into size limits as WCF configure the maximum message size, and the file would be considered part of the message. Look at maxBufferPoolSize, maxReceivedMessageSize, and the members below
<readerQuotas
maxDepth="32" maxStringContentLength="8192"
maxArrayLength="16384" maxBytesPerRead="4096"
maxNameTableCharCount="16384" />
A synchronous web service request is not the best way to transfer files about. Even if it works, if you need to scale the endpoint to process concurrent requests you will quickly run in to trouble. By uploading files to service endpoints you are compromising the availability of the endpoint.
A better solution - the WPF app writes the filestream to disk (or a database, ftp server, or queue), then sends a quick one way command message to the server, which then goes and grabs the file.
This is hugely more scalable and will result in far fewer availability-type exceptions.
UPDATE
In my experience, when you upload large files to web service endpoints you can get availability problems, especially if there is any significant concurrency. You can plan for this stuff if you know what your upper bounds are (file sizes, concurrent connections, etc.) and you can formalise this as a service level agreement, but the nature of what you are trying to do (peer-to-peer) is by definition a volatile environment where such planning would be difficult.
However, that said, the fact that your requirement is for P2P means that there ideally shouldn't be a centralised environment to implement the type of store-and-retrieve messaging pattern I am suggesting.
Windows Azure blob storage is an example of how this may be achieved.

net.tcp service works when hosted by Net.Tcp Listener Adapter, but not Windows Service

I created a WCF net.tcp service and hosted it using the Net.Tcp Listener Adapter, and it works great - I have some messaging set up on the callback so the service updates the client with the status. Now, I'm trying to get it to work by being hosted via a Windows Service, and all I'm doing is creating a ServiceHost using the same class that the original uses:
using System.Diagnostics;
using System.ServiceModel;
using System.ServiceProcess;
using BuilderService;
namespace BuilderWindowsService
{
public class BuilderWindowsService : ServiceBase
{
public ServiceHost ServiceHost = null;
public BuilderWindowsService()
{
ServiceName = ServiceNames.Builder;
}
public static void Main()
{
Run(new BuilderWindowsService());
}
protected override void OnStart(string[] args)
{
if (ServiceHost != null)
ServiceHost.Close();
ServiceHost = new ServiceHost(typeof(Builder));
ServiceHost.Open();
}
protected override void OnStop()
{
if(ServiceHost != null)
{
ServiceHost.Close();
ServiceHost = null;
}
}
}
}
I can connect to the service and send a request, but it never responds nor times out. I know I'm hitting the Windows Service because I have it on another port (8002), and I can add it as a reference using that.
My App.config for the Windows Service is pretty much identical to the Web.config of the original too. Same thing for the client I am using, except it is pointing to the 8002 endpoint instead of 808. Also, I already have this working for another service, doing the exact same setup, but for some reason this one never responds.
UPDATE
I created a little client app to test out directly hitting the windows service to rule out anything interfering, and it generated the following app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IBuilder"
closeTimeout="00:01:00"
openTimeout="00:01:00"
receiveTimeout="00:10:00"
sendTimeout="00:01:00"
transactionFlow="false"
transferMode="Buffered"
transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard"
listenBacklog="10"
maxBufferPoolSize="2147483647"
maxBufferSize="2147483647"
maxConnections="10"
maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="32"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="4096"
maxNameTableCharCount="2147483647" />
<reliableSession ordered="true"
inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows"
protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows"
algorithmSuite="Default" />
</security>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:8002/BuilderService/Builder.svc"
binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IBuilder"
contract="RGBRef.IBuilder"
name="NetTcpBinding_IBuilder">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
Which looks pretty normal to me (note: I manually upped the buffer/string length values to the maximum). Only things that are different from my original config:
transferMode="Buffered"
transactionProtocol="OleTransactions"
listenBacklog="10"
<transport clientCredentialType="Windows"
protectionLevel="EncryptAndSign" />
Not sure if the service is expecting those or something. Either way, it's still not getting any response back, nor an error.
Perhaps the service is faulting since it now runs under different credentials as a Windows Service. Write some EventLog entries to trace where the fault is occurring. I don't believe is the callback, I suspect it's something else in the service failing.