This post is intended to give a step-by-step example of linking D with a C library. If you want to mimic my windows development environment, to start with, you'll need to download Tango and DigitalMars C. I used the below versions, but you may also want to check for the latest. I'm also toying with the idea of creating a Rack-style D http server, and coming up with a D web framework. Download dm850c.zip from here, and tango-0.99.6-bin-win32-dmd.1.029.zip from here. Amusingly, i'd like to highlight that here we're combining Ragel, which creates the Mongrel HTTP parser C code, which was intended for a Ruby web server, finally linking it with D. Download this article's code here Make sure you read this first, too. The (awesome) Mongrel HTTP parser I grabbed parser.c and parser.h from Ebb, as i had problems with the Mongrel one. You can get it here. Place the two files into a folder and run the following command to compile them with this command: \dm\bin\dmc -c parser.c -I. This will produce the parser.obj if you've installed dmc (Digitalmars' C) correctly. Bindings between C and D To make bindings to D, I basically placed the contents of the parser.h into an 'extern(C)' block and deleted all the 'const' references, like so:
extern(C)
{
  enum { MONGREL_CONTENT_LENGTH
       , MONGREL_CONTENT_TYPE
       , MONGREL_FRAGMENT
       , MONGREL_HTTP_VERSION
       , MONGREL_QUERY_STRING
       , MONGREL_REQUEST_PATH
       , MONGREL_REQUEST_METHOD
       , MONGREL_REQUEST_URI
       };

  typedef void (*field_cb)(void *data, char *field, size_t flen, char *value, size_t vlen);
  typedef void (*element_cb)(void *data, int type, char *at, size_t length);

  struct http_parser { 
    int cs;
    int overflow_error;
    size_t body_start;
    size_t content_length;
    size_t nread;
    size_t mark;
    size_t field_start;
    size_t field_len;
    size_t query_start;

    void *data;

    field_cb http_field;
    element_cb on_element;
  };

  void http_parser_init(http_parser *parser);
  int http_parser_finish(http_parser *parser); 
  size_t http_parser_execute(http_parser *parser, char *data, size_t len, size_t off);
  int http_parser_has_error(http_parser *parser);
  int http_parser_is_finished(http_parser *parser);
}
Since mongrel uses a couple of call-back functions, they must also be within 'extern(C)' blocks:
extern (C) void http_field_cb(void *data, char *field, size_t flen, char *value, size_t vlen)
{ ...code here... }

extern (C) void on_element(void *data, int type, char *at, size_t length)
{ ...code here... }
The D Server I'm using Tango in the simplest possible way to listen to port 80, feed the input to Mongrel, and return an html page. Here's the entire serve.d:
import tango.io.Stdout;
import tango.stdc.stringz;
import tango.net.ServerSocket, tango.net.SocketConduit;
         
// Stuff for the mongrel parser
extern(C)
{

  enum { MONGREL_CONTENT_LENGTH
       , MONGREL_CONTENT_TYPE
       , MONGREL_FRAGMENT
       , MONGREL_HTTP_VERSION
       , MONGREL_QUERY_STRING
       , MONGREL_REQUEST_PATH
       , MONGREL_REQUEST_METHOD
       , MONGREL_REQUEST_URI
       };

  typedef void (*field_cb)(void *data, char *field, size_t flen, char *value, size_t vlen);
  typedef void (*element_cb)(void *data, int type, char *at, size_t length);

  struct http_parser { 
    int cs;
    int overflow_error;
    size_t body_start;
    size_t content_length;
    size_t nread;
    size_t mark;
    size_t field_start;
    size_t field_len;
    size_t query_start;

    void *data;

    field_cb http_field;
    element_cb on_element;
  };

  void http_parser_init(http_parser *parser);
  int http_parser_finish(http_parser *parser); 
  size_t http_parser_execute(http_parser *parser, char *data, size_t len, size_t off);
  int http_parser_has_error(http_parser *parser);
  int http_parser_is_finished(http_parser *parser);
}
// End of stuff for the mongrel parser

// My callback functions

extern (C) void http_field_cb(void *data, char *field, size_t flen, char *value, size_t vlen)
{
  char[]* display = cast(char[]*)data;
  *display ~= "<i>http_field_cb</i> <b>";
  *display ~= field[0..flen];
  *display ~= "</b> = ";
  *display ~= value[0..vlen];
  *display ~= "<br>";
}

extern (C) void on_element(void *data, int type, char *at, size_t length)
{
  char[]* display = cast(char[]*)data;

  char[] line;
  line ~= "<i>on_element</i> <b>";
  if (type==MONGREL_CONTENT_LENGTH) line ~= "MONGREL_CONTENT_LENGTH";
  if (type==MONGREL_CONTENT_TYPE) line ~= "MONGREL_CONTENT_TYPE";
  if (type==MONGREL_FRAGMENT) line ~= "MONGREL_FRAGMENT";
  if (type==MONGREL_HTTP_VERSION) line ~= "MONGREL_HTTP_VERSION";
  if (type==MONGREL_QUERY_STRING) line ~= "MONGREL_QUERY_STRING";
  if (type==MONGREL_REQUEST_PATH) line ~= "MONGREL_REQUEST_PATH";
  if (type==MONGREL_REQUEST_METHOD) line ~= "MONGREL_REQUEST_METHOD";
  if (type==MONGREL_REQUEST_URI) line ~= "MONGREL_REQUEST_URI";
  line ~= "</b> = " ~ at[0..length] ~ "<br>";
  *display ~= line;
}

void main()
{
  // Set up the server
  Stdout("Now open your browser to http://localhost/").newline();
  Stdout("Ctrl-Break or Ctrl-C to quit").newline();
  auto server = new ServerSocket (new InternetAddress(80));

  while(1)
  {
    // wait for requests
    SocketConduit request = server.accept;

    // wait for the 'http get ...'
    char[1024] response;
    uint len = request.read (response);
    Stdout(response[0..len]).newline; // you'll want to comment this out if you run apachebench against this...

    // parse it
    char[] display;
    display = "";
    http_parser parser;
    http_parser_init(&parser);
    parser.data = &display; // data that is sent to the callback function
    parser.http_field = &http_field_cb;
    parser.on_element = &on_element;
    http_parser_execute( &parser
                       , cast(char*)response
                       , len
                       , 0
                       );
    display ~= http_parser_has_error(&parser) ? "-mongrel error;" : "-no mongrel error;";
    display ~= http_parser_is_finished(&parser) ? "mongrel finished-" : "mongrel not finished-";;
    
    // send HTML
    request.output.write("HTTP/1.1 200 OK

<html>
<style>
  body {font-family:trebuchet ms;background:#524D4A;color:#fff;}
  .comment {font-size:80%;margin:0;color:#cccccc;}
  form {background:#6B7552; margin:20px 0 20px 0; padding:5px;}
</style>
<body>
  <h1>D + Mongrel's HTTP parser</h1>

  <form method='get'>
    <h2>GET form</h2>
    <input name='blah' value='test value'>
    <input type='submit'>
  </form>
  
  <form method='post'>
    <h2>POST form</h2>
    <input name='blah' value='test value'>
    <input type='submit'>
  </form>  

  <form method='post'>
    <h2>POST form with file</h2>
    <input type='file' name='blah'>
    <input type='submit'>
  </form>
  
  <form>
    <h2>Mongrel Parser Results:</h2>
    " ~ display ~ " 
  </form>
  </body>
</html>");

    request.close();
  }
}
Compiling it all together To compile it together, use the following command, which will produce serve.exe: \dmd\bin\dmd serve.d parser.obj Then open your web browser to http://localhost/ and you should see this: [[posterous-content:JylngbAclaJhIuBBEHCz]] Voila! That's all until next time.

Thanks for reading! And if you want to get in touch, I'd love to hear from you: chris.hulbert at gmail.

Chris Hulbert

(Comp Sci, Hons - UTS)

iOS Developer in Sydney.

I have worked at places such as Google, Cochlear, News Corp, Fox Sports, NineMSN, FetchTV, Woolworths, and Westpac, among others. If you're looking for a good iOS developer, drop me a line!

Get in touch:
[email protected]
github.com/chrishulbert
linkedin
my resume



 Subscribe via RSS