Question

quick overview of what I've got here:

WCF Soap service is running over HTTPS, with Message credential of type certificate. I've got 2 endpoints that I use (besides mex), 1 for my normal service calls, the other for streaming files. Here's my web.config for the service (edited some names of items):

<bindings>
  <wsHttpBinding>
    <binding name="wsHttpEndpointBinding">
      <security mode="TransportWithMessageCredential">
        <message clientCredentialType="Certificate" />
      </security>
    </binding>
  </wsHttpBinding>
  <basicHttpBinding>
      <binding name="streamBinding" transferMode="Streamed" messageEncoding="Mtom" maxReceivedMessageSize="2147483646">
          <security mode="TransportWithMessageCredential">
              <message clientCredentialType="Certificate" />
          </security>
      </binding>
  </basicHttpBinding>        
</bindings>
<services>
  <service behaviorConfiguration="Services.Behavior" name="MyInterface">
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="wsHttpEndpoint" contract="IMyInterface" />
    <endpoint address="stream" binding="basicHttpBinding" bindingConfiguration="streamBinding" name="streamEndpoint" contract="IMyInterface" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="Service.Behavior">
      <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="true" />
      <dataContractSerializer maxItemsInObjectGraph="2147483646" />
      <serviceCredentials>
        <serviceCertificate findValue="CN=Server.Temp.Dev" />
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>
</behaviors>

The service has a method, DownloadDocument, that I'm testing. Here's the signature:

DocumentDownloadResponse DownloadDocument(DocumentDownloadRequest request);

It's worth noting the the service throws Exceptions when the data passed in is not valid, and DownloadDocument catches those exceptions and passes back the error message in the response object.

The DocumentDownloadRequest looks like this:

[MessageContract]
public class DocumentDownloadRequest
{
    [MessageHeader]
    public string SecurityToken { get; set; }

    [MessageHeader]
    public string LoginName { get; set; }

    [MessageHeader]
    public string DocumentId { get; set; }
}

And DownloadDocumentResponse:

[MessageContract]
public class DocumentDownloadResponse : ServiceResponse<Stream>
{
    public DocumentDownloadResponse()
    {
        Data = Stream.Null;
    }

    [MessageHeader(MustUnderstand = true)]
    public bool Success { get; set; }

    [MessageHeader(MustUnderstand = true)]
    public string ErrorMessage { get; set; }

    [MessageBodyMember(Order = 1)]
    public Stream Data { get; set; }
}

Here's how I call it from the client:

        var soapServiceClient = new SoapServiceClient("streamEndpoint");
        bool success;
        Stream stream;
        string errorMessage =
            soapServiceClient.DownloadDocument(documentId, loginName, securityToken, out success, out stream);

        serviceClient.Close();

Where SecurityToken and LoginName are items that need to validates. What's strange is that from my test client, when I call DownloadDocument with valid data, I am able to download the file perfectly as many times as I want. However, if I pass in an invalid LoginName or SecurityToken, I get error messages indicating incorrect data (as expected). If I pass in invalid data 3 times, however, the client times out. Running the service locally I don't get this issue, everythign runs as expected. Strangely enough, whenI run with fiddler open, I don't get this issue. When I run with the service on my dev server, I have the problems.

The configuration on the dev server matches what I run locally. Using the SvcTraceTool I don't see any errors, except that it only documents the first 2 successful calls, and not the one that failed. It almost makes me think the endpoint just closed itself somehow.

Cliffs:

1) Service with 2 endpoints, one of if which is streaming (the one I'm concerned with). 2) Able to use streaming endpoint to call method to Download files with valid data 3) Service correctly catches bad data 2 times, hangs the 3rd time. No logs in SvcTraceTool, client times out.

Any ideas?

Thanks

Was it helpful?

Solution

In order to answer Rodrigo, I figured I'd post more details:

First off, wrap your generated proxy class in something sort of like this to handle error properly:

public class ProxyWrapper<TServiceClientType, TResultType>
    where TServiceClientType : class, ICommunicationObject
{
    private static string _endPoint;

    public ProxyWrapper(string endPoint = "")
    {
        _endPoint = endPoint;
    }

    public TResultType Wrap(Func<string, TServiceClientType> constructWithEndpoint,
                                    Func<TServiceClientType, TResultType> codeBlock)
    {
        TResultType result = default(TResultType);
        TServiceClientType client = default(TServiceClientType);
        try
        {
            client = constructWithEndpoint(_endPoint);
            result = codeBlock(client);
            client.Close();
        }
        catch (Exception)
        {
            if (client != null)
            {
                client.Abort();
            }
            throw;
        }
        return result;
    }
}

I then have a client class that wraps around the service calls. Here's the DownloadDocument method:

    public MyServiceResponse<Stream> DownloadDocument(string loginName,
                                                            string documentId)
    {

        var proxyWrapper = new MyProxyWrapper<DocumentDownloadResponse>(StreamEndpoint);
        DocumentDownloadResponse response =
            proxyWrapper.Wrap((client) =>
            {
                Stream data;
                bool success;
                string errorMessage = client.DownloadDocument(documentId, loginName,
                                                              out success,
                                                              out data);
                return new DocumentDownloadResponse
                {
                    Data = data,
                    Success = success,
                    ErrorMessage = errorMessage
                };
            });

        var result = new MyServiceResponse<Stream>
        {
            Success = response.Success,
            ErrorMessage = response.ErrorMessage
        };

        if (!response.Success)
        {
            result.Data = null;
            response.Data.Close();
        }
        else
        {
            result.Data = response.Data;
        }

        return result;
    }

Note: MyProxyWrapper inherits from ProxyWrapper and specified the WCF client proxy class. Now the actual call looks like this:

var myClient = new MyClient();
var downloadDocumentResponse = myClient.DownloadDocument(someId);
                                using (
                                    Stream output =
                                        File.OpenWrite(someFilePath))
                                {
                                    downloadDocumentResponse.Data.CopyTo(output, 2048);
                                    downloadDocumentResponse.Data.Close();
                                }

Note the two areas that that I call .Close() on the stream, once after writing the file, and once is response.Success == false.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top