Thrift over HTTP with IIS

The Apache Thrift is a scalable cross-language  service framework. It allows seamless communication between C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi and other languages.

The Thrift .Net library includes an HttpHandler THttpHandler which implements the IHttpHandler interface for hosting handlers within IIS. Documentation of how to use the handler is very sparse. Below are instructions of how to create a Thrift service using THttpHandler.

1. Download thrift

Compiler:

Tutorial Files:

2. Generate C# & Javascript files

  • thrift-0.8.0.exe -r –gen csharp tutorial.thrift
  • thrift-0.8.0.exe -r –gen js tutorial.thrift

3. Install Thrift from NuGet

4. Implement the Tutorial Calculator interface

    public class CalculatorImplementation : Calculator.Iface
    {
        private readonly Dictionary log;

        public CalculatorImplementation()
        {
            log = new Dictionary();
        }

        #region Iface Members

        public void ping()
        {
            Console.WriteLine("ping()");
        }

        public int add(int n1, int n2)
        {
            Console.WriteLine("add({0},{1})", n1, n2);
            return n1 + n2;
        }

        public int calculate(int logid, Work work)
        {
            Console.WriteLine("calculate({0}, [{1},{2},{3}])", logid, work.Op, work.Num1, work.Num2);
            int val = 0;
            switch (work.Op)
            {
                case Operation.ADD:
                    val = work.Num1 + work.Num2;
                    break;

                case Operation.SUBTRACT:
                    val = work.Num1 - work.Num2;
                    break;

                case Operation.MULTIPLY:
                    val = work.Num1*work.Num2;
                    break;

                case Operation.DIVIDE:
                    if (work.Num2 == 0)
                    {
                        var io = new InvalidOperation();
                        io.What = (int) work.Op;
                        io.Why = "Cannot divide by 0";
                        throw io;
                    }
                    val = work.Num1/work.Num2;
                    break;

                default:
                    {
                        var io = new InvalidOperation();
                        io.What = (int) work.Op;
                        io.Why = "Unknown operation";
                        throw io;
                    }
            }

            var entry = new SharedStruct();
            entry.Key = logid;
            entry.Value = val.ToString();
            log[logid] = entry;

            return val;
        }

        public SharedStruct getStruct(int key)
        {
            Console.WriteLine("getStruct({0})", key);
            return log[key];
        }

        public void zip()
        {
            Console.WriteLine("zip()");
        }

        #endregion
    }

5. Derive from THttpHandler

    public class ThriftHttpHandler : THttpHandler
    {
        public ThriftHttpHandler()
            : base(CreateProcessor(), CreateJsonFactory())
        {
        }

        private static Calculator.Processor CreateProcessor()
        {
            return new Calculator.Processor(new CalculatorImplementation());
        }

        private static TJSONProtocol.Factory CreateJsonFactory()
        {
            return new TJSONProtocol.Factory();
        }
    }

6. Reference Http Handler in web.config

<configuration>
 <system.web>
 <compilation debug="true" targetFramework="4.0" />

 <httpHandlers>
 <add verb="*" path="*.thrift"
 type="HandlerSample.ThriftHttpHandler, HandlerSample" />
 </httpHandlers>
 </system.web>

</configuration>

6b.

Note: IIS 7 requires handlers to be registered differently if you’re using Classic or Integrated mode.


<!-- IIS 7 in Integrated Mode -->
<system.webServer>
 <handlers>
 <add name="HandlerSample" path="*.thrift" resourceType="Unspecified" type="HandlerSample.ThriftHttpHandler, HandlerSample" verb="*"/>
 </handlers>
</system.webServer>

<!-- IIS 7 in Classic Mode -->
<system.webserver>
 <handlers>
 <add name="HandlerSample" path="*.thrift" resourceType="Unspecified" scriptprocessor="%windir%\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" type="HandlerSample.ThriftHttpHandler, HandlerSample" verb="*"></add>
 </handlers>
</system.webserver>

7. Download tutorial.html

Modify tutorial.html


  <script src="/js/thrift.js"                   type="text/javascript"></script>
  <script src="/generated-js/tutorial_types.js"    type="text/javascript"></script>
  <script src="/generated-js/shared_types.js"      type="text/javascript"></script>
  <script src="/generated-js/SharedService.js"     type="text/javascript"></script>
  <script src="/generated-js/Calculator.js"        type="text/javascript"></script>

8. Open tutorial.html

Voila!

Final Comments

Full source code is available here:

https://github.com/marksl/thrift-httphandler-tutorial

Advertisements
This entry was posted in .Net, C#, IIS, Thrift. Bookmark the permalink.

11 Responses to Thrift over HTTP with IIS

  1. Pingback: Thrift API Versioning with .Net | codealoc

  2. Stif says:

    Hi

    Nice tutuorial, thanks!

    However, I can’t get it to work on IIS. It only seems to work in Visual Studio’s built in Web Server. What would be the steps to port this to IIS?

  3. codealoc says:

    Ah you know, my code I think only works on IIS 6 or Web Development environment.

    If you’re running IIS 7, you need to register handlers in the too. I’ve updated the configuration in the post.

  4. Stif says:

    Hi, thanks.
    I am indeed using the same (at least more or less the same, I’m not specifying a scriptProcessor, I see you use the aspnet_isapi.dll for .NET v2.0, is that correct?) handler configuration as in your update.
    But at this point I get an exception in the Processor.Process method.
    It’s throwing a “TTransportException: Cannot read, Remote side has closed” on the line TMessage msg = iprot.ReadMessageBegin();
    If you have an idea what might be causing this, all information is welcome 🙂

  5. codealoc says:

    Yes. We encountered the same problem.

    In the end we used a custom version of the THttpHandler – in hindsight I should submit our patch back to thrift. I’m going to do that.

    The solution for now is to copy-paste the THttpHandler from here:

    https://git-wip-us.apache.org/repos/asf?p=thrift.git;a=blob;f=lib/csharp/src/Transport/THttpHandler.cs;h=0a10d796f8a109ffa5385d0a166da5586471d62e;hb=0.9.x

    Modify the one line:
    while (processor.Process(inputProtocol, outputProtocol)) { }
    to be
    while (input.Position < input.Length && processor.Process(inputProtocol, outputProtocol)) {}

    And that should solve it!

  6. Stif says:

    Great! Thanks for the tip!
    It turns out however, that modifying that one line wasn’t even necessary. So the code in the link must differ from the actual code in the thrift nuget package (version 0.9.0.0).
    And it gets better: on my collegue’s machines the code just works even without using a custom version of the THttpHandler. They didn’t get any exception at all.
    So I’m suspecting this can be solved by modifying some setting. I’m now trying to figure out which setting that could. Looks like it must have something to do with how streams are handled..
    This calls for an investigation (if my boss ‘ll give me the time…) 🙂

  7. Stif says:

    Hi, I have some more info.

    I wanted to see what the difference was between the THttpHandler code in your provided link and the actual code in Thrift.dll. So I looked at the Thrift.dll code using DotPeek. Turns out, there was no difference! What I did see was that the Thrift.dll was built with .NET 3.5 (thus runtime v2.0). So I got the idea of downgrading my code to target framework 3.5 and switched my app-pool to v2.0 and – low-and-behold – it worked!

    I then decided I wanted to figure out why this was working on my collegue’s machines (on a v4.0 app-pool) and not on mine. So I upgraded my code again to target v4.5 and switched my app-pool again to v4.0 and now the problem is totally gone! I’m a bit baffled as to what exactly caused this, but happy that everything is working.

    If you should have further ideas on the subject, I’d be interested to know.

  8. Craig says:

    I finally got it working in IIS 7. The magic incantation was to use resourceType=”Unspecified” rather than “File”. Otherwise I get 404s.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s