Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using D, how would I listen to incoming HTTP requests and respond to them?

Tags:

http

sockets

d

Using D, how would I listen to incoming HTTP traffic and respond to it?

For example (in pseudocode):

socket = new socket("locahost", 80)
socket.onRequestRecevied(handleRequest);

function response handleRequest(request) {
  //do something with the request and  respond
  request.respond("hello world")
}

I know there is a lot more to it than that, but I haven't been able to find many resources on responding to incoming http request.

EDIT: My current attempts have yielded only exceptions like "Unable to create socket: Operation not permitted." which may mean I'm doing it correctly but am simply receiving a system error message.

like image 805
rmontgomery429 Avatar asked Oct 04 '11 18:10

rmontgomery429


2 Answers

it's the same as listening to normal incoming TCP connections and responding to them:

  import std.socket;
  Socket s = new TcpSocket(AddressFamily.INET);
  s.bind(new InternetAddress("0.0.0.0", 80));
  s.listen(8);
  while (true) {
    Socket conn=s.accept();
    //handle incoming message (I'm putting it in a task pool to handle ;) )
    taskPool.put(task!handleHttp(conn));
  }

with handleHttp(Socket) containing the logic for receiving the http request and sending the response as defined be the http standard (you'll have to find that your self though)

like image 149
ratchet freak Avatar answered Sep 23 '22 01:09

ratchet freak


There is currently no HTTP server in the standard library. Adam Ruppe has some very good code on Github for Web work, but it currently doesn't include a standalone Web server.

The program below is a bare-bones, single-threaded basic HTTP server, for educational purposes. Generating a valid HTTP response is still up to you; but at least it parses the header, and gives you a chance to respond based on the details of the request.

import std.algorithm;
import std.conv;
import std.stdio;
import std.socket;
import std.string;

// usage: ./server port

void main(string[] args)
{
  enum BACKLOG = 8;
  ushort PORT = to!ushort(args[1]);
  Socket s    = new TcpSocket(AddressFamily.INET);
  s.bind(new InternetAddress("0.0.0.0", PORT));
  s.listen(BACKLOG);

  scope (exit) 
  { 
    s.shutdown(SocketShutdown.BOTH);
    s.close();
  }

  while (true)
  {
    debug writeln("waiting...");
    Socket conn = s.accept();
    scope (exit)  
    {
      conn.shutdown(SocketShutdown.BOTH);
      conn.close();
    }
    try
    {
      handleHttp(conn);
    }
    catch (Throwable e) 
    {
      stderr.writeln("thrown: ", e);
    }
  }    
}

void handleHttp(Socket conn)
{
  // Make a buffer big enough to read a full HTTP header. My approach here is to 
  // read the header in its entirety before proceeding. This isn't production
  // quality, but good enough for some applications.

  ubyte[8192] buf;  // big enough for some purposes...
  size_t position, headerEnd, len, newpos;

  // Receive the whole header before parsing it.
  while (true) 
  {
    len = conn.receive(buf[position..$]);

    if (len == 0)               // empty request
      return;

    newpos    = position + len;
    headerEnd = countUntil(buf[position..newpos], "\r\n\r\n");
    position  = newpos;

    if (headerEnd >= 0) 
      break;
  }

  // Anything beyond headerEnd+4 is part of the request body. For now, bail:
  // no POSTs or PUTs allowed. Feel free to remove the assert & implement them!
  assert (position-(headerEnd+4) == 0, 
          "Sorry, only content-free requests are supported.");

  // Now parse the header.
  auto lines          = splitter(buf[0..headerEnd], "\r\n");
  string request_line =  cast(string) lines.front; 
  lines.popFront;

  debug writeln(request_line);

  // a very simple Header structure.
  struct Pair 
  { 
    string key, value;

    this(ubyte[] line) 
    {
      auto tmp = countUntil(line, ": ");
      key       = cast(string) line[0..tmp]; // maybe down-case these?
      value     = cast(string) line[tmp+2..$];
    }
  }

  Pair[] headers;
  foreach(line; lines) 
    headers ~= Pair(line);

  auto tmp        = splitter(request_line, ' ');
  string method   = tmp.front; tmp.popFront;
  string url      = tmp.front; tmp.popFront;
  string protocol = tmp.front; tmp.popFront;

  debug writefln("%s, %s, %s", method, url, protocol);

  // Prepare a response, and send it

  string resp = join(["HTTP/1.1 200 OK", 
                      "Content-Length: 2",
                      "Content-Type: text/plain",
                      "Connection: close",
                      "",
                      "OK"],
                     "\r\n");

  conn.send(cast(ubyte[]) resp);
}
like image 35
gmfawcett Avatar answered Sep 22 '22 01:09

gmfawcett