3 people like it.

REST For Free

HttpListener + FParsec + Pattern Matching = Simple REST Endpoints

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
131: 
132: 
133: 
134: 
135: 
136: 
137: 
138: 
139: 
140: 
141: 
142: 
143: 
144: 
145: 
146: 
147: 
148: 
149: 
150: 
151: 
152: 
153: 
154: 
155: 
156: 
157: 
158: 
159: 
160: 
161: 
162: 
//  EXAMPLES:
//
//    http://localhost:8888/people/chefs/Jimbo?eggs=2&cheese=4
//    http://localhost:8888/people/chefs/Jimbo?cheese=4&eggs=2
//    http://localhost:8888/people/chefs/Jimbo?cheese=4&eggs=2&bacon=5
//
//  USAGE:
//    C:\RestForFree>fsi restForFree.fsx
//
//  SEE ALSO:
//    https://www.branded3.com/blog/creating-a-simple-http-server-with-f

#I @"C:\RestForFree"
#r @"FParsec.1.0.2\lib\net40-client\FParsecCS.dll"
#r @"FParsec.1.0.2\lib\net40-client\FParsec.dll"

////////////////////////////////////////////////////////////////////////////////

//Naive, but its just a pedantic example
module parser =
  open System; open FParsec

  //Just give me my result, parsec!
  let (-=>) input someParser =
    run someParser input
    |> function
    | Success(result,_,_) -> result
    | _ -> Unchecked.defaultof<'T>    //Equivalent to C#'s default(T),
                                      //both match paths need same return type
  type url = {
    pathString: string
    queryString: string }

  let not = (<>) //Just eliminating parens

  let psplitBy char =
    let keep = manySatisfy(not char)
    let toss = pstring(string(char))
    sepBy(keep)(toss)

  let pathParser = psplitBy('/')

  let keyOrVal = many(noneOf "&=") |>> String.Concat
  let keyValuePair =  keyOrVal .>> pstring "=" .>>. keyOrVal
  let queryParser = sepBy(keyValuePair)(pstring "&")

  let preamble = parse {
    do! pstring "http" |>> ignore                                       //Proto
    do! skipString "://"                                                //
    do! many(noneOf ":/") |>> String.Concat |>> ignore                  //host
    do! pstring(":") >>. manySatisfy(Char.IsDigit) <|>% "" |>> ignore } //port?

  let urlParser = parse {
    do! preamble
    let! pathString = pstring("/") >>. manySatisfy(not '?') <|>% ""
    let! queryString = pstring("?") >>. restOfLine(true) <|>% ""
    return {
      pathString = pathString
      queryString = queryString } }

////////////////////////////////////////////////////////////////////////////////

module listener =
  open System; open System.Net; open System.Text

  //Eliminate .NET line noise,
  type HttpListenerResponse with
    member me.WriteAsync str =
      Encoding.ASCII.GetBytes(s=str)
      |> me.OutputStream.AsyncWrite

  //Config
  let root,host = @"C:\RestForFree","http://localhost:8888/"

  //.NET init stuff
  let initListener =
    let l = new HttpListener()
    l.Prefixes.Add host
    l.Start()
    l

  //As it turns out, Async.* was recently, to accomodate futures/deferreds
  //However, HttpListener uses 'the IAsyncResult design pattern [sic]',
  //which is older/more-lower-level
  //https://msdn.microsoft.com/en-us/library/system.iasyncresult(v=vs.110).aspx
  let listen =
    let l = initListener  //Called once upon definition of 'listen'
    async {               //Is accessed repeatedly, however
      let b,e = l.BeginGetContext, l.EndGetContext
      let! context = Async.FromBeginEnd(b, e)
      return context.Request,context.Response }

  let listening requestHandler =
    Async.Start <| async {                    //Non-blocking
      while true do
        let! request, response = listen       //Get the context asynchronously
        do! requestHandler request response } //Handle the context async
    Console.ReadLine() |> ignore              //Retain console for printfn

////////////////////////////////////////////////////////////////////////////////

module http =
  open System.Net; open listener

  //Parens for sake of type inferencer confused by partial application
  let httpWrapper (restHandler)(request)(response:HttpListenerResponse) =
    async {
      response.StatusCode  <- int HttpStatusCode.OK   //Response packaging
      response.ContentType <- "text/html"             //Response packaging
      let! mainOutput = restHandler request response
      do! response.WriteAsync mainOutput
      response.Close() }                               //Resource management :-)
                                                       //TODO: Find IDisposables
                                                       //      and 'use' them.

////////////////////////////////////////////////////////////////////////////////

open parser; open listener; open http

//Have to sort tuples by key alphabetically, lest there be no match! :-(
let (|ATOZ|) = ATOZ List.sortBy fst
let pretty s = sprintf "%A" s

listening(httpWrapper(fun request response ->

  //Thank you Microsoft, but I will parse the url myself
  request.Url.AbsoluteUri -=> urlParser
  |> function
  | { url.pathString = pathString; url.queryString = queryString } ->

      //'Routes'
      pathString -=> pathParser
      |> function

      //"href=/people/chefs/Jimbo"
      | [ "people"; "chefs"; chef ] ->

        //Remote Procedure Call (RPC)-style 'rest' endpoints
        queryString -=> queryParser
        |> function

        | ATOZ[ "cheese",b ; "eggs",a ] -> async {
            return
              chef + " cannot make omlettes :-( ... <br />" +
              "But, Dexter can make " + string(int a + int b + 1000) +
              " omlettes du fromage!!  <br />" +
              "because he is a boy-genious, with a very large laboratory." }

        //b, then c, then e, here, but not in the url!
        | ATOZ[ "bacon",b ; "cheese",c ; "eggs",a ] -> async {
            return
              chef + " can make " + string(int a + int b + int c) + " omlets!" }

        //b, then c, then e, here, but not in the url!
        | ATOZ[ "bacon",b ; "cheese",c ; "eggs",a ; "mushrooms",m ] -> async {
            return "Mushrooms...Disgusting!" }

        | ATOZ query -> async {
            return "NO QUERY MATCH FOR: " + pretty query }

      | path -> async { return "NO PATH MATCH FOR: " + pretty path }
))
namespace System
namespace FParsec
val input : string
val someParser : Parser<'T,unit>
val run : Parser<'Result,unit> -> string -> ParserResult<'Result,unit>

Full name: FParsec.CharParsers.run
union case ParserResult.Success: 'Result * 'UserState * Position -> ParserResult<'Result,'UserState>
val result : 'T
module Unchecked

from Microsoft.FSharp.Core.Operators
val defaultof<'T> : 'T

Full name: Microsoft.FSharp.Core.Operators.Unchecked.defaultof
type url =
  {pathString: string;
   queryString: string;}

Full name: Script.parser.url
url.pathString: string
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = String

Full name: Microsoft.FSharp.Core.string
url.queryString: string
val not : ('a -> 'a -> bool) (requires equality)

Full name: Script.parser.not
val psplitBy : char:char -> Parser<string list,'a>

Full name: Script.parser.psplitBy
Multiple items
val char : char

--------------------
type char = Char

Full name: Microsoft.FSharp.Core.char
val keep : Parser<string,'a>
val manySatisfy : (char -> bool) -> Parser<string,'u>

Full name: FParsec.CharParsers.manySatisfy
val toss : Parser<string,'a>
val pstring : string -> Parser<string,'u>

Full name: FParsec.CharParsers.pstring
val sepBy : Parser<'a,'u> -> Parser<'b,'u> -> Parser<'a list,'u>

Full name: FParsec.Primitives.sepBy
val pathParser : Parser<string list,unit>

Full name: Script.parser.pathParser
val keyOrVal : Parser<string,unit>

Full name: Script.parser.keyOrVal
val many : Parser<'a,'u> -> Parser<'a list,'u>

Full name: FParsec.Primitives.many
val noneOf : seq<char> -> Parser<char,'u>

Full name: FParsec.CharParsers.noneOf
Multiple items
type String =
  new : value:char -> string + 7 overloads
  member Chars : int -> char
  member Clone : unit -> obj
  member CompareTo : value:obj -> int + 1 overload
  member Contains : value:string -> bool
  member CopyTo : sourceIndex:int * destination:char[] * destinationIndex:int * count:int -> unit
  member EndsWith : value:string -> bool + 2 overloads
  member Equals : obj:obj -> bool + 2 overloads
  member GetEnumerator : unit -> CharEnumerator
  member GetHashCode : unit -> int
  ...

Full name: System.String

--------------------
String(value: nativeptr<char>) : unit
String(value: nativeptr<sbyte>) : unit
String(value: char []) : unit
String(c: char, count: int) : unit
String(value: nativeptr<char>, startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int) : unit
String(value: char [], startIndex: int, length: int) : unit
String(value: nativeptr<sbyte>, startIndex: int, length: int, enc: Text.Encoding) : unit
String.Concat([<ParamArray>] values: string []) : string
   (+0 other overloads)
String.Concat(values: Collections.Generic.IEnumerable<string>) : string
   (+0 other overloads)
String.Concat<'T>(values: Collections.Generic.IEnumerable<'T>) : string
   (+0 other overloads)
String.Concat([<ParamArray>] args: obj []) : string
   (+0 other overloads)
String.Concat(arg0: obj) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string) : string
   (+0 other overloads)
String.Concat(arg0: obj, arg1: obj) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string, str2: string) : string
   (+0 other overloads)
String.Concat(arg0: obj, arg1: obj, arg2: obj) : string
   (+0 other overloads)
String.Concat(str0: string, str1: string, str2: string, str3: string) : string
   (+0 other overloads)
val keyValuePair : Parser<(string * string),unit>

Full name: Script.parser.keyValuePair
val queryParser : Parser<(string * string) list,unit>

Full name: Script.parser.queryParser
val preamble : Parser<unit,unit>

Full name: Script.parser.preamble
val parse : ParserCombinator

Full name: FParsec.Primitives.parse
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val skipString : string -> Parser<unit,'u>

Full name: FParsec.CharParsers.skipString
type Char =
  struct
    member CompareTo : value:obj -> int + 1 overload
    member Equals : obj:obj -> bool + 1 overload
    member GetHashCode : unit -> int
    member GetTypeCode : unit -> TypeCode
    member ToString : unit -> string + 1 overload
    static val MaxValue : char
    static val MinValue : char
    static member ConvertFromUtf32 : utf32:int -> string
    static member ConvertToUtf32 : highSurrogate:char * lowSurrogate:char -> int + 1 overload
    static member GetNumericValue : c:char -> float + 1 overload
    ...
  end

Full name: System.Char
Char.IsDigit(c: char) : bool
Char.IsDigit(s: string, index: int) : bool
val urlParser : Parser<url,unit>

Full name: Script.parser.urlParser
val pathString : string
val queryString : string
val restOfLine : bool -> Parser<string,'u>

Full name: FParsec.CharParsers.restOfLine
namespace System.Net
namespace System.Text
type HttpListenerResponse =
  member Abort : unit -> unit
  member AddHeader : name:string * value:string -> unit
  member AppendCookie : cookie:Cookie -> unit
  member AppendHeader : name:string * value:string -> unit
  member Close : unit -> unit + 1 overload
  member ContentEncoding : Encoding with get, set
  member ContentLength64 : int64 with get, set
  member ContentType : string with get, set
  member Cookies : CookieCollection with get, set
  member CopyFrom : templateResponse:HttpListenerResponse -> unit
  ...

Full name: System.Net.HttpListenerResponse
val me : HttpListenerResponse
member HttpListenerResponse.WriteAsync : str:string -> Async<unit>

Full name: Script.listener.WriteAsync
val str : string
type Encoding =
  member BodyName : string
  member Clone : unit -> obj
  member CodePage : int
  member DecoderFallback : DecoderFallback with get, set
  member EncoderFallback : EncoderFallback with get, set
  member EncodingName : string
  member Equals : value:obj -> bool
  member GetByteCount : chars:char[] -> int + 3 overloads
  member GetBytes : chars:char[] -> byte[] + 5 overloads
  member GetCharCount : bytes:byte[] -> int + 2 overloads
  ...

Full name: System.Text.Encoding
property Encoding.ASCII: Encoding
Encoding.GetBytes(s: string) : byte []
Encoding.GetBytes(chars: char []) : byte []
Encoding.GetBytes(chars: char [], index: int, count: int) : byte []
Encoding.GetBytes(chars: nativeptr<char>, charCount: int, bytes: nativeptr<byte>, byteCount: int) : int
Encoding.GetBytes(s: string, charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
Encoding.GetBytes(chars: char [], charIndex: int, charCount: int, bytes: byte [], byteIndex: int) : int
argument s : string
property HttpListenerResponse.OutputStream: IO.Stream
member IO.Stream.AsyncWrite : buffer:byte [] * ?offset:int * ?count:int -> Async<unit>
val root : string

Full name: Script.listener.root
val host : string

Full name: Script.listener.host
val initListener : HttpListener

Full name: Script.listener.initListener
val l : HttpListener
Multiple items
type HttpListener =
  new : unit -> HttpListener
  member Abort : unit -> unit
  member AuthenticationSchemeSelectorDelegate : AuthenticationSchemeSelector with get, set
  member AuthenticationSchemes : AuthenticationSchemes with get, set
  member BeginGetContext : callback:AsyncCallback * state:obj -> IAsyncResult
  member Close : unit -> unit
  member DefaultServiceNames : ServiceNameCollection
  member EndGetContext : asyncResult:IAsyncResult -> HttpListenerContext
  member ExtendedProtectionPolicy : ExtendedProtectionPolicy with get, set
  member ExtendedProtectionSelectorDelegate : ExtendedProtectionSelector with get, set
  ...
  nested type ExtendedProtectionSelector

Full name: System.Net.HttpListener

--------------------
HttpListener() : unit
property HttpListener.Prefixes: HttpListenerPrefixCollection
HttpListenerPrefixCollection.Add(uriPrefix: string) : unit
HttpListener.Start() : unit
val listen : Async<HttpListenerRequest * HttpListenerResponse>

Full name: Script.listener.listen
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val b : (AsyncCallback * 'a -> IAsyncResult)
val e : (#IAsyncResult -> HttpListenerContext)
HttpListener.BeginGetContext(callback: AsyncCallback, state: obj) : IAsyncResult
HttpListener.EndGetContext(asyncResult: IAsyncResult) : HttpListenerContext
val context : HttpListenerContext
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member Async.FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member Async.FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member Async.FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
property HttpListenerContext.Request: HttpListenerRequest
property HttpListenerContext.Response: HttpListenerResponse
val listening : requestHandler:(HttpListenerRequest -> HttpListenerResponse -> Async<unit>) -> unit

Full name: Script.listener.listening
val requestHandler : (HttpListenerRequest -> HttpListenerResponse -> Async<unit>)
static member Async.Start : computation:Async<unit> * ?cancellationToken:Threading.CancellationToken -> unit
val request : HttpListenerRequest
val response : HttpListenerResponse
type Console =
  static member BackgroundColor : ConsoleColor with get, set
  static member Beep : unit -> unit + 1 overload
  static member BufferHeight : int with get, set
  static member BufferWidth : int with get, set
  static member CapsLock : bool
  static member Clear : unit -> unit
  static member CursorLeft : int with get, set
  static member CursorSize : int with get, set
  static member CursorTop : int with get, set
  static member CursorVisible : bool with get, set
  ...

Full name: System.Console
Console.ReadLine() : string
module listener

from Script
val httpWrapper : restHandler:('a -> HttpListenerResponse -> Async<string>) -> request:'a -> response:HttpListenerResponse -> Async<unit>

Full name: Script.http.httpWrapper
val restHandler : ('a -> HttpListenerResponse -> Async<string>)
val request : 'a
property HttpListenerResponse.StatusCode: int
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
type HttpStatusCode =
  | Continue = 100
  | SwitchingProtocols = 101
  | OK = 200
  | Created = 201
  | Accepted = 202
  | NonAuthoritativeInformation = 203
  | NoContent = 204
  | ResetContent = 205
  | PartialContent = 206
  | MultipleChoices = 300
  ...

Full name: System.Net.HttpStatusCode
field HttpStatusCode.OK = 200
property HttpListenerResponse.ContentType: string
val mainOutput : string
member HttpListenerResponse.WriteAsync : str:string -> Async<unit>
HttpListenerResponse.Close() : unit
HttpListenerResponse.Close(responseEntity: byte [], willBlock: bool) : unit
module parser

from Script
module http

from Script
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val sortBy : projection:('T -> 'Key) -> list:'T list -> 'T list (requires comparison)

Full name: Microsoft.FSharp.Collections.List.sortBy
val fst : tuple:('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val pretty : s:'a -> string

Full name: Script.pretty
val s : 'a
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val listening : requestHandler:(System.Net.HttpListenerRequest -> System.Net.HttpListenerResponse -> Async<unit>) -> unit

Full name: Script.listener.listening
val httpWrapper : restHandler:('a -> System.Net.HttpListenerResponse -> Async<string>) -> request:'a -> response:System.Net.HttpListenerResponse -> Async<unit>

Full name: Script.http.httpWrapper
val request : System.Net.HttpListenerRequest
val response : System.Net.HttpListenerResponse
property System.Net.HttpListenerRequest.Url: System.Uri
property System.Uri.AbsoluteUri: string
val urlParser : FParsec.Primitives.Parser<url,unit>

Full name: Script.parser.urlParser
val pathParser : FParsec.Primitives.Parser<string list,unit>

Full name: Script.parser.pathParser
val chef : string
val queryParser : FParsec.Primitives.Parser<(string * string) list,unit>

Full name: Script.parser.queryParser
active recognizer ATOZ: (string * string) list -> (string * string) list

Full name: Script.( |ATOZ| )
val b : string
val a : string
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val c : string
val m : string
val query : (string * string) list
val path : string list

More information

Link:http://fssnip.net/td
Posted:1 years ago
Author:Ted Cackowski Jr
Tags: rest , restful , fparsec , parsec , parse , parser , parsers , httplistener , pattern matching , web , script , fsi , url , rpc