new object $http_connection: $string_connection;

var $root inited = 1;
var $http_connection method = 0;
var $http_connection page = 0;
var $http_connection args = 0;
var $http_connection version = 0;
var $http_connection headers = #[];


public method .init_http_connection() {
  .debug("Anew!");
  headers = #[];
};

public method .headers() {
  return headers;
};

private method .respond() {
  arg code, head, text;
  var len, item, x;

  // Set up keepalive, if they've asked for it.
  if (headers["Connection"] != "Keep-Alive") {
    head = head.add("Connection", "Close");
  }
  if (version) {
    // if version isn't defined, it's HTTP/0.9.  That means no headers.  None.

    // First, set these headers if they haven't been already.
    if (! (| head["Server"] |)) {
      head = head.add("Server", $http_lib.server());
    }
    if (! (| head["Content-type"] |)) {
      head = head.add("Content-type", "text/plain");
    }
    if (! (| head["Content-length"] |)) {
      len = (| text.mmap('length).sum() + text.length() |) || 0;
      head = head.add("Content-length", tostr(len));
    }

    // First thing out is the response header
    .write(version + " " + code + " " + $http_lib.response_phrase(code));
    for item in (head) {
      if (type(item[2]) == 'list) {
	// If it's a list, they want multiple headers with the same name
	map x in (item[2]) to (item[1] + ": " + item[2]);
      } else {
	.write(item[1] + ": " + item[2]);
      }
    }
    .write("");
  }
  if (method != "HEAD") {
    // Print out the body.  The HEAD method means they just want the header :)
    switch ((| text[1] |)) {
      case 'file:
	// Write out the file.  This saves lots of space and time for big files
	cwritef(text[2]);
      default:
	.write(text);
    }
  }
  if ((| head["Connection"] |) == "Close") {
    .close();
  }
};

public method .error() {
  arg code, @text;
  var head;

  // Just a wrapper to make life simpler.
  [code, head, text] = $http_lib.error(code, @text);

  // Make sure method isn't HEAD, so the body will be sent out
  method = "";
  
  // RFC2068 says these responses should have *no* body.
  if (version && (((code >= 100) && (code < 200)) ||
		  (code == 204) || (code == 304))) {
    text = [];
  }
  return .respond(code, head, text);
};

  
public method .parse_line() {
  arg line;
  var URI, parts, i;

  if (!method) {
    // If method is false, that means this is the first line.  The first
    // line must be a request line.
    catch ~range {
      // Requests can either be two or three words.  If three, the third
      // is the version.
      [method, URI, @i] = line.explode();
      if (i) {
	[version, @i] = i;
      }
    } with {
      // If ~range was thrown, it means that not enough word were sent.
      i = 1;
    }
    if (i) {
      // A malformed request
      return .error(400);
    }

    // HTTP/1.1 and beyond have keepalive on by default.
    if (!version || (version = "HTTP/1.0")) {
      headers = headers.add("Connection", "Close");
    } else {
      headers = headers.add("Connection", "Keep-Alive");
    }

    // HTTP/0.9 can't send a HEAD, since there are no headers in that protocol!
    if (!version && (method == "HEAD")) {
      return .error(501);
    }

    // unsupported_methods = ["OPTIONS", "POST", "PUT", "DELETE", "TRACE"];
    if (method in ["GET", "HEAD"]) {
      if ((parts = URI.regexp("^(http://([-A-Za-z0-9.]+))?/?([^/]*)(.*)"))) {
	page = parts[3];
	args = parts[4];
      } else {
	page = URI;
      }
      // If HTTP/0.9, fall through so we can send the page
      if (version) return;
    } else {
      return .error(501, method + " to " + URI + " not supported.");
    }
  }

  // If the line is blank, or if we're speaking HTTP/0.9
  if ((line == "") || (!version)) {
    catch any {
      return .respond(@(> lookup(tosym("http_page_" + page)).generate(page, args) <));
    } with {
      if (error() == ~namenf) {
	// The infamous 404, file not found
	return .error(404);
      } else {
	i = "<PRE>" + $parse_lib.traceback(traceback()).join("<BR>") + "</PRE>";
	// Server error
 	return .error(500, i);
      }
    }
  } else {
    i = stridx(line, ": ", 1);
    headers = headers.add(substr(line, 1, i-1), substr(line, i+2));
  }
};