#line 157 "/home/travis/build/felix-lang/felix/src/packages/web.fdoc"
  include "web/__init__";
  
  publish """
  Defines types and container for http_request.
  Main entry points are get_param (helper to extract params from http_request)
  and get_http_request which extracts request from stream
  """
  
  class HTTPRequest {
     open HTTPConnection;
     open Assoc_list;
     open URICodec;
     open Logger;
     open Cookie;
     open Stream;
     open Socket;
     open TerminalIOByteStream[socket_t];
     open WebUtil;
  
     union http_method =
       | GET
       | POST
       | BAD;
  
    instance Str[http_method] {
      fun str : http_method ->string =
        | #GET => "GET"
        | #POST => "POST"
        | #BAD => "BAD";
     }
  
    instance Eq[http_method] {
      fun == : http_method*http_method->bool = "$1==$2";
      fun != : http_method*http_method->bool = "$1!=$2";
    }
  
  
     struct http_request {
      hmethod: http_method;
      uri: string;
      path:string;
      params:assoc_list[string,string];
      entity_params:assoc_list[string,string];
      headers:assoc_list[string,string];
    }
  
    instance Str[http_request] {
      fun str (request: http_request) =>
        "HTTP Request\n"+
        "\tMethod:"+str(request.hmethod)+"\n"+
        //"\tURI:"""+request.uri+"\n"+
        "\tPath:"""+request.path+"\n"+
        "\tParams:"""+str(request.params)+"\n"+
        "\tHeaders:"""+str(request.headers)+"\n";
    }
  
    private proc copy_request(orig:&http_request,cpy:&http_request) = {
      *cpy.hmethod = *orig.hmethod;
      *cpy.uri = *orig.uri;
      *cpy.path = *orig.path;
      *cpy.params = *orig.params;
    }
  
    publish """
    Parses a list of URI encoded key value parameters and returns as an assoc_list.
    """
    fun get_params(p:string):list[string*string] ={
       var params = split(p,'&');
       return   map  (fun(x:string):string*string =>let Cons(hd,tl) = split(x,'=') in
                       (uri_decode(hd),uri_decode((fold_left (fun(x:string) (y:string):string => x + y) "" tl)))
                       ) params;
    }
  
    noinline proc get_headers(conn:http_connection,headers:&list[string^2])  {
      var line:string = "";
      get_line(conn.sock, &line);  // shouldg be the GET line.
      while line != "" and line != "\r" do
        get_line(conn.sock, &line);
        match split(line,':') with
          | Cons(key,value) =>
                *headers = Cons((uri_decode(strip(key)),
  	      uri_decode(strip(fold_left (fun(x:string) (y:string):string => x + y) "" value))),
                *headers);
           | x => println("WARNING:Possible malformed request headerline:"+x);
        endmatch;
      done
    }
  
    publish """ Main entry point for extracting HTTP request from stream """
    noinline proc get_request(conn:http_connection,request:&http_request) = {
      var k = conn.sock;
      var line: string = "";
      get_line(k, &line);  // shouldg be the GET line.
      var got = match split(line,' ') with
        | Cons (hmethod,Cons(uri,Cons(prot,_))) => match (hmethod,uri,prot) with
          | ("GET",uri,prot)  => match (GET,uri,split(uri,'?'),prot) with
            | (GET,uri,Cons(path,rest),prot) =>
                 http_request(GET,uri,path,
                  get_params((fold_left (fun(x:string) (y:string):string => x + y) "" rest)),
                  Empty[string*string],Empty[string*string])
              endmatch
          | ("POST",uri,prot)  => match (POST,uri,split(uri,'?'),prot) with
            | (POST,uri,Cons(path,rest),prot) => http_request(POST,uri,path,
                  get_params((fold_left (fun(x:string) (y:string):string => x + y) "" rest)),
                  Empty[string*string],Empty[string*string])
            endmatch
  	  endmatch
          | _ =>  http_request(BAD,"","",Empty[string*string],Empty[string*string],
                               Empty[string*string])
      endmatch;
      var headers = Empty[string^2];
      get_headers(conn,&headers);
      got.headers = headers;
      copy_request(&got,request);
      *request.headers=headers;
    }
  
  
  
  
    publish """
    Populates entity_params in request. Entity params are URI encoded key value pairs in
    request body that are supplied when a POST request is made by the browser.
    """
    proc get_entity_params(conn:http_connection,request:&http_request,attribs:list[string^2]) = {
      val olen = match get_header(*request,"Content-Length") with |Some s=> int(s) |_ => 0 endmatch;
      var len = olen;
      var eof=false;
      var params:assoc_list[string,string] = Empty[string*string];
      if olen > 0 do
        var buf = C_hack::cast[+char] (C_hack::malloc(len+1));
        var buf_a = address(buf);
        read(conn.sock,&len,buf_a,&eof);
        if len > 0 do
          params = get_params(string(buf,len));
        done
        C_hack::free(buf_a);
      done
      *request.entity_params = params;
      return ;
    }
  
  fun read_bytes(conn:http_connection,olen:int) = {
      var len = olen;
      var eof=false;
  
      var ret:string = "";
      if olen > 0 do
        var buf = C_hack::cast[+char] (C_hack::malloc(len+1));
        var buf_a = address(buf);
        read(conn.sock,&len,buf_a,&eof);
        ret= str(buf);
        C_hack::free(buf_a);
       done
       return ret;
    }
  
  
    proc get_multipart_params(conn:http_connection,request:&http_request,attribs:list[string^2]) {
      var line:string = "";
      val llen = match get_header(*request,"Content-Length") with |Some s=> int(s) |_ => 0 endmatch;
      var rest = read_bytes(conn,llen);
      write(conn,HTTPResponse::make_continue());
      *conn.dirty=false;
  
      match (find (fun (s:string) => s == "boundary") attribs) with
        |Some b => { get_line(conn.sock, &line);
          var headers = Empty[string^2];
          get_headers(conn,&headers);
        }
       |_ => {conn.config.log(DEBUG,"No Boundry"); }
      endmatch;
       *request.entity_params=Empty[string*string];
    }
  
    fun get_fname(request:http_request) ={
      val v = match rev(split(request.path,'/')) with
        | Cons(hd,_) => Some(hd)
        | _ => None[string]
      endmatch;
      return v;
    }
  
    fun get_path_and_fname(request:http_request):opt[string^2] ={
      return match rev(split(request.path,'/')) with
        | Cons(hd,tl) => Some(
              (fold_left (fun(x:string) (y:string):string => x +"/"+ y) "" (rev(tl))), hd)
        | _ => None[string*string]
      endmatch;
    }
  
    publish """ Return opt[string] parameter value for given name """
    fun get_param(request:http_request,name:string) =>
       find (fun (a:string,b:string) => eq(a,b)) request.params name;
  
    publish """ Return opt[string] post parameter value for given name """
    fun get_post_param(request:http_request,name:string) =>
       find (fun (a:string,b:string) => eq(a,b)) request.entity_params name;
  
    publish """ Return opt[string] request header value for given name """
    fun get_header(request:http_request,name:string) =>
       find (fun (a:string,b:string) => eq(a,b)) request.headers name;
  
    fun get_cookies(request:http_request):list[cookie] = {
  
       val cline= Assoc_list::find (fun (a:string,b:string) => eq(a,b)) (request.headers)  ('Cookie');
       val lines = match cline with
         | Some s => (match split(s,';') with
                         |Cons (h,t) => Cons(h,t)
                         |_            => Empty[string]
                       endmatch)
         | _        => Empty[string]
       endmatch;
       val pairs = filter (fun (sl:opt[string^2]) => match sl with |Some _ => true |_ => false endmatch) (map (fun (cl:string) => split_first(cl,"=")) lines);
        return (map (fun (p:opt[string^2]) => let Some q = p in cookie(q.(0),q.(1))) pairs);
  }
  
  }