Wout Ware Blog

My external memory

Log soap request/response bytes from ASP.NET web service client

clock December 7, 2011 21:21 by author Administrator
Inspired by jfburdet, I wanted to see if it was possible to directly intercept an ASP.NET web service call at stream/byte level rather than reconstructing XML. And it is! See code below:

using
System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Services.Protocols;
using System.Xml;

using Test.MyWebReference;

namespace Test {
   
/// <summary>
   
/// Adds the ability to retrieve the SOAP request/response.
   
/// </summary>
   
public class ServiceSpy : OriginalService {
       
private StreamSpy writerStreamSpy;
       
private XmlTextWriter xmlWriter;

       
private StreamSpy readerStreamSpy;
       
private XmlTextReader xmlReader;

       
public MemoryStream WriterStream {
           
get { return writerStreamSpy == null ? null : writerStreamSpy.ClonedStream; }
       
}

       
public XmlTextWriter XmlWriter {
           
get { return xmlWriter; }
       
}

       
public MemoryStream ReaderStream {
           
get { return readerStreamSpy == null ? null : readerStreamSpy.ClonedStream; }
       
}

       
public XmlTextReader XmlReader {
           
get { return xmlReader; }
       
}

       
protected override void Dispose(bool disposing) {
           
base.Dispose(disposing);
           
DisposeWriterStreamSpy();
           
DisposeReaderStreamSpy();
       
}

       
protected override XmlWriter GetWriterForMessage(SoapClientMessage message, int bufferSize) {
           
// Dispose previous writer stream spy.
           
DisposeWriterStreamSpy();

            writerStreamSpy
= new StreamSpy(message.Stream);
           
// XML should always support UTF8.
            xmlWriter
= new XmlTextWriter(writerStreamSpy, Encoding.UTF8);

           
return xmlWriter;
       
}

       
protected override XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize) {
           
// Dispose previous reader stream spy.
           
DisposeReaderStreamSpy();

            readerStreamSpy
= new StreamSpy(message.Stream);
            xmlReader
= new XmlTextReader(readerStreamSpy);

           
return xmlReader;
       
}

       
private void DisposeWriterStreamSpy() {
           
if (writerStreamSpy != null) {
                writerStreamSpy
.Dispose();
                writerStreamSpy
.ClonedStream.Dispose();
                writerStreamSpy
= null;
           
}
       
}

       
private void DisposeReaderStreamSpy() {
           
if (readerStreamSpy != null) {
                readerStreamSpy
.Dispose();
                readerStreamSpy
.ClonedStream.Dispose();
                readerStreamSpy
= null;
           
}
       
}

       
/// <summary>
       
/// Wrapper class to clone read/write bytes.
       
/// </summary>
       
public class StreamSpy : Stream {
           
private Stream wrappedStream;
           
private long startPosition;
           
private MemoryStream clonedStream = new MemoryStream();

           
public StreamSpy(Stream wrappedStream) {
               
this.wrappedStream = wrappedStream;
                startPosition
= wrappedStream.Position;
           
}

           
public MemoryStream ClonedStream {
               
get { return clonedStream; }
           
}

           
public override bool CanRead {
               
get { return wrappedStream.CanRead; }
           
}

           
public override bool CanSeek {
               
get { return wrappedStream.CanSeek; }
           
}

           
public override bool CanWrite {
               
get { return wrappedStream.CanWrite; }
           
}

           
public override void Flush() {
                wrappedStream
.Flush();
           
}

           
public override long Length {
               
get { return wrappedStream.Length; }
           
}

           
public override long Position {
               
get { return wrappedStream.Position; }
               
set { wrappedStream.Position = value; }
           
}

           
public override int Read(byte[] buffer, int offset, int count) {
               
long relativeOffset = wrappedStream.Position - startPosition;
               
int result = wrappedStream.Read(buffer, offset, count);
               
if (clonedStream.Position != relativeOffset) {
                    clonedStream
.Position = relativeOffset;
               
}
                clonedStream
.Write(buffer, offset, result);
               
return result;
           
}

           
public override long Seek(long offset, SeekOrigin origin) {
               
return wrappedStream.Seek(offset, origin);
           
}

           
public override void SetLength(long value) {
                wrappedStream
.SetLength(value);
           
}

           
public override void Write(byte[] buffer, int offset, int count) {
               
long relativeOffset = wrappedStream.Position - startPosition;
                wrappedStream
.Write(buffer, offset, count);
               
if (clonedStream.Position != relativeOffset) {
                    clonedStream
.Position = relativeOffset;
               
}
                clonedStream
.Write(buffer, offset, count);
           
}

           
public override void Close() {
                wrappedStream
.Close();
               
base.Close();
           
}

           
protected override void Dispose(bool disposing) {
               
if (wrappedStream != null) {
                    wrappedStream
.Dispose();
                    wrappedStream
= null;
               
}
               
base.Dispose(disposing);
           
}
       
}
   
}
}


WCF EndPointNotFoundException

clock November 23, 2011 07:53 by author Administrator

Wasted another couple of hours today trying to figure out why WCF is throwing an EndPointNotFoundException (with a 404 error in the inner exception) on the client when trying to connect to a .svc WCF service, hosted in the ASP.NET development server (VS 2008). Turns out that on the WCF Service Application side, the address should be left empty (or at least not something like http://localhost/Service), otherwise the client will get a mysterious EndPointNotFoundException. The reason for this seems to be that for IIS hosted (and probably ASP.NET development server) the address should be a relative address! See http://msdn.microsoft.com/en-us/library/aa751792.aspx for an explanation. Quoting the text here from paragraph "Endpoint Addresses for IIS-Hosted Services":

When hosted in IIS, endpoint addresses are always considered to be relative to the address of the .svc file that represents the service. For example, if the base address of a WCF service is http://localhost/Application1/MyService.svc with the following endpoint configuration.

<endpoint address="anotherEndpoint" .../>

This provides an endpoint that can be reached at "http://localhost/Application1/MyService.svc/anotherEndpoint".

Similarly, the endpoint configuration element that uses an empty string as the relative address provides an endpoint reachable at http://localhost/Application1/MyService.svc, which is the base address.

<endpoint address="" ... />

You must always use relative endpoint addresses for IIS-hosted service endpoints. Supplying a fully-qualified endpoint address (for example, http://localhost/MyService.svc) can lead to errors in the deployment of the service if the endpoint address does not point to the IIS-application that hosts the service exposing the endpoint. Using relative endpoint addresses for hosted services avoids these potential conflicts.



svn commit "failed to load properties from disk" "malformed file"

clock August 12, 2011 19:35 by author Administrator

Yesterday I wanted to commit a change and it failed with the messages "failed to load properties from disk" and "malformed file". Apparently my working copy on the client got corrupted somehow. Server side was ok. So I created a patch file, ditched the working copy, did a new checkout, re-applied the patch, and then the commit went ok.



Drawing points in Silverlight 5

clock May 8, 2011 02:49 by author Administrator

During the port of our Cadlib DWG DXF .NET library to Silverlight 5 we ran into a problem while implementing the 3D rendering. There was no support for PrimitiveType.PointList unfortunately. After some help from Peter Kuhn we managed to draw points using triangles (follow the discussion here). This is apparently the way to do it nowadays as DirectX 10/11 won't support points anymore (alas!). So each point is represented by 2 small triangles. Some vertex shader magic is needed to make this happen. I won't go into too much detail here, to see how it's done just download the demo application that draws half a million points.

Each point will need 4 vertices, and each vertex takes up 20 bytes of space, so 80 bytes per point are needed. In the future with shader model 4 this might be improved upon by using geometry shaders, but these are not yet supported as Silverlight 5 supports shader model 2 at the moment.



VirtualBox won't start vdi as SATA that was used as ATA originally

clock December 3, 2010 01:48 by author Administrator

I was just cloning a VDI file to try something out. I used to do this all the time, copy the harddisk file, then run "%ProgramFiles%\Oracle\VirtualBox\VBoxManage.exe" internalcommands sethduuid "cloned.vdi" to give it a new uuid, and finally creating a new virtual machine with the cloned harddisk.

Well, today it didn't work. Windows 7 kept failing to start, but the original virtual machine worked fine. Also the windows repair function couldn't repair it. Turned out that the new virtual machine put the cloned vdi under a sata controller. The old virtual machine had it under an ata controller. So I switched it back to ata, and now it starts up ok again!

I'm not sure why it now uses sata, instead of ata. I switched to a newer laptop, and at the same time to a new VirtualBox version, so it might be either one of those.



How to reset the domain administrator password on Windows Server 2008 with just the Windows Installation Disk

clock April 8, 2010 01:02 by author Administrator

I had to reset the domain administrator password on Windows Server 2008 because the password was lost (not by me for a change!). I ran across this very nice youtube video by Mathieu (Chateau?) that worked perfectly: http://www.youtube.com/watch?v=Ar-VoO9ogHc.

I'm kinda paranoid about useful webpages being removed, so I am writing down a description of the video here just in case:

  • Insert the original Windows Server 2008 installation DVD.
  • Reset the computer, boot from DVD.
  • Choose option "Repair your computer".
  • Start command prompt.
  • C:\
  • CD C:\Windows\System32
  • MOVE Utilman.exe Utilman.exe.bak
  • COPY Cmd.exe Utilman.exe
  • Restart windows
  • At the login screen, type Windows-U, this fires up cmd.exe now
  • NET USER administrator <new password>
  • Presto! Login as administrator!
  • Now repair computer again and move the original Utilman.exe back.

There are a bunch of commercial tools to reset the administrator password, but there is no need for it! Furthermore most of them seem to reset just the local administrator account's password, rather than the domain administrator password.



vsdiag_regwcf.exe

clock February 23, 2010 01:46 by author Administrator

Note to self: don't forget to run this command:

"%ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\vsdiag_regwcf.exe" -u

when getting the annoying error popup in VisualStudio:

---------------------------
Microsoft Visual Studio
---------------------------
Unable to automatically debug 'Xyz'. The remote procedure could not be debugged. This usually indicates that debugging has not been enabled on the server. See help for more information.
---------------------------
OK   Help  
---------------------------

 



File change notifications not happening on VirtualBox

clock February 21, 2010 08:04 by author Administrator

I recently started using VirtualBox as a replacement for VirtualPC 2007 to do builds using an old build environment. The NUnit test was failing, so I fixed the problem, compiled, and reran the test. Hmmm, still failing! Retried a couple of times, and then it hit me! NUnit didn't see the changed binaries when in VirtualBox. So I restarted NUnit, and presto! Tests are running again! Now I hope I won't forget next time...



Use .svc extension in address when hosting using ASP.NET server (e.g. in VS "WCF Service Application" project)

clock December 26, 2009 08:49 by author Administrator

Today I was playing with WCF and for a change I created a "WCF Service Application" project to act as a WCF service. I removed the default created stuff and added my own service class. I quickly whipped up a client project, and fired up the debugger:

+        [System.ServiceModel.CommunicationException]    {"An error occurred while receiving the HTTP response to http://localhost:8083/Service. This could be due to the service endpoint binding not using the HTTP protocol. This could also be due to an HTTP request context being aborted by the server (possibly due to the service shutting down). See server logs for more details."}    System.ServiceModel.CommunicationException

Argh! Why me? A couple of hours, a lot of cursing and trial and erroring later I figured out that the ASP.NET development server must be able to recognize the .svc extension. So the client configuration must refer to the actual .svc file:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <client>
            <endpoint address="http://localhost:8083/Service.svc" binding="wsHttpBinding" bindingConfiguration="" contract="WcfData.IService"
                name="MyService">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

 

The service's config may have the address attribute empty or the explicit reference to the .svc file, but I had the string "http://localhost:8083/Service" initially, which works ok with anything else but ASP.NET hosts. Hosting in a Console app, or windows service using ServiceHost works just fine without .svc extension.



Implementing IErrorHandler and working around SerializationException.

clock December 17, 2009 06:40 by author Administrator

EDIT Nov 25, 2009: note that the problem described in this post below only happens in the VS debugger, under some circumstances.

Today I was attempting to implement a custom IErrorHandler to wrap Exception instances into a FaultException<T> instance, so they can be properly turned into a soap fault by WCF. Seemed easy at first, but there was a nasty fly in the ointment. The original code in the service would work fine, which looked like this:

throw new FaultException<Exception>(new Exception("test"));


When I would just throw a new Exception("test") in the service, and wrap it in the error handler, then it wouldn't work. The service code now looks like this:

throw new Exception("test");


The error handler code looks like this:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace Test {
    public class ErrorHandler : IErrorHandler {
        #region IErrorHandler Members

        public bool HandleError(Exception error) {
            return false;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
            if (error is FaultException) {
                return;
            }

            Type faultExceptionType = typeof(FaultException<>).MakeGenericType(error.GetType());
            FaultException faultException = (FaultException)Activator.CreateInstance(faultExceptionType, error);
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(
                version,
                messageFault,
                faultException.Action
            );
        }

        #endregion
    }
}


Now the exception doesn't get turned into a soap fault anymore, instead we're getting a SerializationException hidden away quite nicely. What's going on???

After a whole day of digging around, googling, debugging and trying hacks I did manage to find out what's going on and also find a workaround. Googling around first made me find part of the problem. The normal DataContractSerializer can't handle the Exception.Data property when it has a value. And when the exception arrives at the error handler, it does have a Data value. This isn't the complete story though, the field behind the Data property is _data. Whenever Data gets evaluated, it will set _data to a value, and then the serialization breaks. The next piece in the puzzle is this: initially the _data field of an Exception is null, but doing a throw/catch causes the _data to be initialized (Edit Nov 25, 2009: only when the debugger stops when e.g. the exception is thrown, when the debugger doesn't stop, _data remains null and there is no problem). In other words: a throw/catch will break serialization! Note that this all is a debugger side effect, so once you've figured out it's a debugger side effect, you can just make sure you don't break on exceptions in the service.

There's also a workaround. The _data field can be set to null in the error handler using reflection. Modify the ProvideFault method like this and the serialization will work again:

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {
            if (error is FaultException) {
                return;
            }

            // This hack sets the _data field to null again.
            FieldInfo fieldInfo = typeof(Exception).GetField("_data", BindingFlags.Instance | BindingFlags.NonPublic);
            fieldInfo.SetValue(error, null);

            Type faultExceptionType = typeof(FaultException<>).MakeGenericType(error.GetType());
            FaultException faultException = (FaultException)Activator.CreateInstance(faultExceptionType, error);
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(
                version,
                messageFault,
                faultException.Action
            );
        }