k10n
Jim Klopfenstein's Radio Weblog

 



Subscribe to "k10n" in Radio UserLand.

Click to see the XML version of this web page.

Click here to send an email to the editor of this weblog.
 
 

SoapExtension Walkthrough -- VS7 version

Follow these steps to create an XML web service, a client test program, and a SoapExtension that logs and obfuscates SOAP packets passing between the client and the service. You must have Microsoft Visual Studio .NET for this walkthrough

Tasks Detailed steps
1. Create a solution
  1. Create a blank solution, named SampleSoapExtension
2. Create a simple Web Service
  1. Add a new C# ASP.NET Web Service Project, called WSForSampleSoapExtension
  2. Rename Service1.asmx Echo.asmx
  3. Rename the class name and the constructor name in this service Echo
  4. Add the following function to the class:
    [WebMethod]public string YouSaid(string whatyousaid)
    {
      return “You said “ + whatyousaid;
    }
3. Create a test program for the Web Service
  1. Add a new C# Console App, called Test
  2. Rename Class1.cs Test.cs
  3. Add a Web Reference to the WSForSampleSoapExtension serviced. Add the following code to the Main function
    localhost.Echo e = new localhost.Echo();
    Console.WriteLine(e.YouSaid(“Howdy!”));
  4. Set Test to be the Startup Project
  5. Set a breakpoint on the curly brace after at the end of Main
  6. Build and debug; when you reach the breakpoint, the console window should show the message You said Howdy!
4. Create an empty SoapExtension
  1. Add a new C# Class Library called SampleSoapExtension
  2. Rename Class1.cs SampleSoapExtension.cs
  3. Rename the class and delete the constructor
  4. Add a reference to System.Web.Services.dll
  5. At the top of the source file, add
    using System.Web.Services;
    using System.Web.Services.Protocols;
  6. Add the base class SoapExtension to the class SampleSoapExtensionClass
  7. In the Class View window, open the SampleSoapExtensionClass all the way to Bases and Interfaces.
  8. Using Add…Override in the context menu, add class skeletons for ChainStream, both GetInitializer methods, Initialize, and ProcessMessages.
  9. Modify ChainStream to return stream instead of null.
  10. Build the solution. You should see the message: 3 succeeded
5. Create an attribute to apply the extension to the Web Service
  1. Add a new public class called SampleSoapExtensionAttribute that extends SoapExtensionAttribute
  2. Using the Class View window, add overrides for ExtensionType and Priority
  3. Modify the get part of ExtensionType to return typeof(SampleSoapExtensionClass)
  4. Add a private member variable of type int called priority
  5. Modify the Priority property Get part to return priority, and the set part to priority = value
  6. Decorate the class with the attribute [AttributeUsage(AttributeTargets.Method)]
  7. Build the solution.
6. Apply the SoapExtension to the Web Service
  1. Add to the Web Service project a reference to the SampleSoapExtension project.
  2. Add the line using SampleSoapExtension; to Echo.cs.asmx
  3. Add the decoration [SampleSoapExtension] to the method YouSaid.
  4. Build the solution.
  5. With the breakpoint still on the curly brace at the end of the Main method, debug the solution. It should work exactly as it did before.
7. Add logging to the SoapExtension
  1. Add the logMessage method to the SampleSoapExtension class.
    private void logMessage(string header,string message)
    {
      FileStream fs = new FileStream("c:\\logs\\log.txt", FileMode.Append,FileAccess.Write);
      StreamWriter w = new StreamWriter(fs);
      w.WriteLine("--- " + header);
      w.WriteLine(message);
      w.Flush();
      w.Close();
    } 
  2. Add using System.IO; to the SampleSoapExtension source file.
  3. Add an invocation of logMessage(“Initialize”,””);
  4. Make sure that the logs directory exists and that Everyone has write access to it.
  5. Build and run the solution. You should have a log file with a single entry. This proves that the soap extension is being invoked.
8. Apply the SoapExtension to the client (Test) program
  1. Create a new text file, using File/New…/File…
  2. Add the following lines to this file
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
     <system.web>
      <webServices>
       <soapExtensionTypes>
        <add type="SampleSoapExtension.SampleSoapExtensionClass, sampleSoapExtension" priority="0" group="1"/>
       </soapExtensionTypes>
      </webServices>
     </system.web>
    </configuration>
  3. Save the file in the bin/Debug subdirectory of the Test subdirectory of the solution directory.
  4. Add to the Test project a reference to the SampleSoapExtension project.
  5. Delete the log.txt file from the logs directory.
  6. Build and run the solution. This time there will be two entries in the log.txt file—one from the client and one from the server
9. Make the SoapExtension actually do something
  1. Add two private member variables to the SampleSoapExtensionClass:
    private Stream inwardStream;
    private Stream outwardStream;
  2. Replace the method body of ChainStream with:
    outwardStream = stream;
    inwardStream = new MemoryStream();
    return inwardStream;
  3. Add the following method body to ProcessMessages:
    StreamReader r;
    StreamWriter w;
    switch (message.Stage)
    {
      case SoapMessageStage.BeforeDeserialize:
        logMessage("BeforeDeserialize",message.GetType().ToString());
        r = new StreamReader(outwardStream);
        w = new StreamWriter(inwardStream);
        w.Write(r.ReadToEnd());
        w.Flush();
        inwardStream.Position = 0;
        break;
      case SoapMessageStage.AfterSerialize:
        logMessage("AfterSerialize",message.GetType().ToString());
        inwardStream.Position = 0;
        r = new StreamReader(inwardStream);
        w = new StreamWriter(outwardStream);
        w.Write(r.ReadToEnd()); w.Flush();
        break;
    }
  4. Delete the log file, then build and run the program. You will see the following progression of calls:
    Initialize AfterSerialize with a type of ClientSoapMessage Initialize BeforeDeserialize with a type of ServerSoapMessage AfterSerialize with a type of ServerSoapMessage BeforeDeserialize with a type of ClientSoapMessage
10. Add the SOAP messages to the log file
  1. In the ProcessMessages method of the SampleSoapExtensionClass, add two member variables:
    string whatami;
    string soapText;
  2. In the same method, delete the calls to logMessages
  3. Add the following line at the start of the BeforeDeserialize case:
    whatami = (message is SoapClientMessage) ? "Response to Client" : "Request to Server"; 
  4. Add the following line at the start of the AfterSerialize case:
    whatami = (message is SoapClientMessage) ? "Request from Client" : "Response from Server"; 
  5. Replace the line
    w.write(r.ReadToEnd());
    in both cases with:
    soapText = r.ReadToEnd();
    logMessage(whatami,soapText);
    w.Write(soapText);
  6. Delete the log file, then build and run the program. You will see the same progression of calls as in 9 above, but with clearer descriptions and the actual SOAP messages
11.
  1. Add the following helper functions to the SampleSoapExtensionClass:
    private void bumpup(ref StringBuilder instr)
    {
      for (int i=0; i<instr.Length; i++)
        if (instr[i] >= 'a' && instr[i] < 'z')
          instr[i] = Convert.ToChar(Convert.ToByte(instr[i])+1);
        else if (instr[i] == 'z')
          instr[i] = 'a';
    }
    private void bumpdown(ref StringBuilder instr)
    {
      for (int i=0; i<instr.Length; i++)
        if (instr[i] > 'a' && instr[i] <= 'z')
          instr[i] = Convert.ToChar(Convert.ToByte(instr[i])-1);
        else if (instr[i] == 'a')
          instr[i] = 'z'; } 
  2. At the top of the file, add
    using System.Text;
  3. Replace the declaration of soapText with
    StringBuilder sb;
  4. Replace the three lines you added in 10e above in both cases with:
    sb = new StringBuilder();
    sb.Append(r.ReadToEnd());
    logMessage("Unmodified "+whatami,sb.ToString());
    logMessage("Modified "+whatami,sb.ToString());
    w.Write(sb.ToString()); 
  5. In the BeforeDeserialize case, add
    bumpup(ref sb);
    between the two logMessage calls. In the AfterSerialize case, add
    bumpdown(ref sb);
  6. Delete the log file, then build and run the program. You will see a pair of messages, one unmodified, one ummodified in place of the SOAP messages in 10 above. This demonstrates that obfuscated messages are being passed between the client and the server.


Click here to visit the Radio UserLand website. © Copyright 2003 Jim Klopfenstein.
Last update: 2/10/2003; 8:43:42 AM.