FSharpx.Extras


#r @"../bin/FSharpx.Extras.dll"
#r @"../bin/FSharpx.Async.dll"
#r @"../bin/FSharp.Control.AsyncSeq.dll"

open FSharp.Control
open FSharpx.IO
open FSharpx.Net

open System
open System.Net
open System.Threading

let root = "http://msdn.microsoft.com"
let proxy = "http://localhost:8082/"

// ----------------------------------------------------------------------------
// Simple web proxy implemented using 'HttpListener'. This version downloads
// the entire web page as a string and then writes it to the response stream.

let cts1 = new CancellationTokenSource()
HttpListener.Start(proxy, (fun (req, resp) -> async {
    // Download the web page
    let url = root + req.Url.PathAndQuery
    let wc = new WebClient()
    let! html = wc.AsyncDownloadString(Uri(url))

    // Replace URLs and send to the response stream
    let html = html.Replace(root, proxy)
    do! resp.AsyncReply(html) }), cancellationToken = cts1.Token)

// Now go to: http://localhost:8082/en-us/fsharp
cts1.Cancel()

// ----------------------------------------------------------------------------
// Better version of a proxy - this time, we read data from the input stream
// in chunks and write them to the response stream as they arive.

let cts2 = new CancellationTokenSource()
HttpListener.Start(proxy, (fun (req, resp) -> async {
    // Initialize the download
    let url = root + req.Url.PathAndQuery
    let targetReq = HttpWebRequest.Create(url)
    use! targetResp = targetReq.AsyncGetResponse()
    use stream = targetResp.GetResponseStream()
  
    // Copy data until we read the entire input
    let count = ref 1
    let buffer = Array.zeroCreate 4096
    while !count > 0 do
      let! read = stream.AsyncRead(buffer, 0, buffer.Length)
      do! resp.OutputStream.AsyncWrite(buffer, 0, read)    
      count := read
    resp.Close() }), cancellationToken = cts2.Token)

cts2.Cancel()

// ----------------------------------------------------------------------------
// Proxy that copies data in chunks can be easily implemented using 
// asynchronous sequences. We read all data as asynchronous sequence and
// write them to the output (Even simpler version could use 'AsyncWriteSeq'
// to write all input buffers to the output stream).

let cts3 = new CancellationTokenSource()
HttpListener.Start(proxy, (fun (req, resp) -> async {
    // Initialize the download
    let url = root + req.Url.PathAndQuery
    let targetReq = HttpWebRequest.Create(url)
    use! targetResp = targetReq.AsyncGetResponse()
    use stream = targetResp.GetResponseStream()
  
    // Iterate over chunks read as an asynchronous sequence
    // and write them to the output stream
    for buffer in stream.AsyncReadSeq(4096) do
      do! resp.OutputStream.AsyncWrite(buffer, 0, buffer.Length)
    resp.Close() }), cancellationToken = cts3.Token)

cts3.Cancel()

// ----------------------------------------------------------------------------
// A more sophisticated version of proxy that caches web 
// pages using a simple agent.

type CacheMessage =
  | TryGet of string * AsyncReplyChannel<option<byte[]>>
  | Add of string * byte[]

// Creates an agent that handles 'CacheMessage' and implements the cache
let cache = MailboxProcessor.Start(fun agent -> async {
  let pages = new System.Collections.Generic.Dictionary<_, _>()
  while true do
    let! msg = agent.Receive()
    match msg with 
    | TryGet(url, repl) ->
        // Try to get a value from the dictionary
        match pages.TryGetValue(url) with
        | true, data -> repl.Reply(Some(data))
        | _ -> repl.Reply(None)
    | Add(url, data) ->
        // Add byte array to the cache
        pages.[url] <- data })


let cts4 = new CancellationTokenSource()
HttpListener.Start(proxy, (fun (req, resp) -> async {
    // Generate URL and check data from the cache
    let url = root + req.Url.PathAndQuery
    let! cached = cache.PostAndAsyncReply(fun repl -> TryGet(url, repl))
    match cached with 
    | Some data ->
        // Reply using data from the cache
        do! resp.OutputStream.AsyncWrite(data)
        resp.Close() 
    | None ->
        // Initialize the download
        let targetReq = HttpWebRequest.Create(url)
        use! targetResp = targetReq.AsyncGetResponse()
        use stream = targetResp.GetResponseStream()
  
        // Create a cached asynchronous sequence 
        // (that reads the stream only once)
        let cachedData = stream.AsyncReadSeq(4096) |> AsyncSeq.cache

        // Start workflow that reads all data in memory (for caching)
        let! allBytes = 
          cachedData 
          |> AsyncSeq.fold (fun st data -> data::st) []
          |> Async.StartChild
        // Write all data from the async sequence to the output
        for buffer in cachedData do
          do! resp.OutputStream.AsyncWrite(buffer, 0, buffer.Length)
        resp.Close() 

        // Get all data accumulated in background and save
        // them to the cache (for later use)
        let! allData = allBytes
        let data = allData |> List.rev |> Array.concat 
        cache.Post(Add(url, data)) }), cts4.Token)

cts4.Cancel()
Multiple items
namespace Microsoft.FSharp

--------------------
namespace FSharp
namespace Microsoft.FSharp.Control
namespace System
namespace System.Net
namespace System.Threading
val root: string
val proxy: string
val cts1: CancellationTokenSource
Multiple items
type CancellationTokenSource = interface IDisposable new: unit -> unit + 2 overloads member Cancel: unit -> unit + 1 overload member CancelAfter: millisecondsDelay: int -> unit + 1 overload member Dispose: unit -> unit member TryReset: unit -> bool static member CreateLinkedTokenSource: token: CancellationToken -> CancellationTokenSource + 2 overloads member IsCancellationRequested: bool member Token: CancellationToken
<summary>Signals to a <see cref="T:System.Threading.CancellationToken" /> that it should be canceled.</summary>

--------------------
CancellationTokenSource() : CancellationTokenSource
CancellationTokenSource(millisecondsDelay: int) : CancellationTokenSource
CancellationTokenSource(delay: TimeSpan) : CancellationTokenSource
Multiple items
type HttpListener = interface IDisposable new: unit -> unit member Abort: unit -> unit member BeginGetContext: callback: AsyncCallback * state: obj -> IAsyncResult member Close: unit -> unit member EndGetContext: asyncResult: IAsyncResult -> HttpListenerContext member GetContext: unit -> HttpListenerContext member GetContextAsync: unit -> Task<HttpListenerContext> member Start: unit -> unit member Stop: unit -> unit ...
<summary>Provides a simple, programmatically controlled HTTP protocol listener. This class cannot be inherited.</summary>

--------------------
HttpListener() : HttpListener
val async: AsyncBuilder
type WebClient = inherit Component new: unit -> unit member CancelAsync: unit -> unit member DownloadData: address: string -> byte array + 1 overload member DownloadDataAsync: address: Uri -> unit + 1 overload member DownloadDataTaskAsync: address: string -> Task<byte array> + 1 overload member DownloadFile: address: string * fileName: string -> unit + 1 overload member DownloadFileAsync: address: Uri * fileName: string -> unit + 1 overload member DownloadFileTaskAsync: address: string * fileName: string -> Task + 1 overload member DownloadString: address: string -> string + 1 overload ...
<summary>Provides common methods for sending data to and receiving data from a resource identified by a URI.</summary>
Multiple items
type Uri = interface ISerializable new: uriString: string -> unit + 6 overloads member Equals: comparand: obj -> bool member GetComponents: components: UriComponents * format: UriFormat -> string member GetHashCode: unit -> int member GetLeftPart: part: UriPartial -> string member IsBaseOf: uri: Uri -> bool member IsWellFormedOriginalString: unit -> bool member MakeRelative: toUri: Uri -> string member MakeRelativeUri: uri: Uri -> Uri ...
<summary>Provides an object representation of a uniform resource identifier (URI) and easy access to the parts of the URI.</summary>

--------------------
Uri(uriString: string) : Uri
Uri(uriString: string, creationOptions: inref<UriCreationOptions>) : Uri
Uri(uriString: string, uriKind: UriKind) : Uri
Uri(baseUri: Uri, relativeUri: string) : Uri
Uri(baseUri: Uri, relativeUri: Uri) : Uri
property CancellationTokenSource.Token: CancellationToken with get
<summary>Gets the <see cref="T:System.Threading.CancellationToken" /> associated with this <see cref="T:System.Threading.CancellationTokenSource" />.</summary>
<exception cref="T:System.ObjectDisposedException">The token source has been disposed.</exception>
<returns>The <see cref="T:System.Threading.CancellationToken" /> associated with this <see cref="T:System.Threading.CancellationTokenSource" />.</returns>
CancellationTokenSource.Cancel() : unit
CancellationTokenSource.Cancel(throwOnFirstException: bool) : unit
val cts2: CancellationTokenSource
type HttpWebRequest = inherit WebRequest interface ISerializable member Abort: unit -> unit member AddRange: range: int -> unit + 7 overloads member BeginGetRequestStream: callback: AsyncCallback * state: obj -> IAsyncResult member BeginGetResponse: callback: AsyncCallback * state: obj -> IAsyncResult member EndGetRequestStream: asyncResult: IAsyncResult -> Stream + 1 overload member EndGetResponse: asyncResult: IAsyncResult -> WebResponse member GetRequestStream: unit -> Stream + 1 overload member GetResponse: unit -> WebResponse ...
<summary>Provides an HTTP-specific implementation of the <see cref="T:System.Net.WebRequest" /> class.</summary>
Multiple items
val ref: value: 'T -> 'T ref

--------------------
type 'T ref = Ref<'T>
type Array = interface ICollection interface IEnumerable interface IList interface IStructuralComparable interface IStructuralEquatable interface ICloneable member Clone: unit -> obj member CopyTo: array: Array * index: int -> unit + 1 overload member GetEnumerator: unit -> IEnumerator member GetLength: dimension: int -> int ...
<summary>Provides methods for creating, manipulating, searching, and sorting arrays, thereby serving as the base class for all arrays in the common language runtime.</summary>
val zeroCreate: count: int -> 'T array
val cts3: CancellationTokenSource
type CacheMessage = | TryGet of string * AsyncReplyChannel<byte array option> | Add of string * byte array
union case CacheMessage.TryGet: string * AsyncReplyChannel<byte array option> -> CacheMessage
Multiple items
val string: value: 'T -> string

--------------------
type string = String
type AsyncReplyChannel<'Reply> = member Reply: value: 'Reply -> unit
type 'T option = Option<'T>
Multiple items
val byte: value: 'T -> byte (requires member op_Explicit)

--------------------
type byte = Byte

--------------------
type byte<'Measure> = byte
union case CacheMessage.Add: string * byte array -> CacheMessage
val cache: MailboxProcessor<CacheMessage>
Multiple items
type MailboxProcessor<'Msg> = interface IDisposable new: body: (MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken: CancellationToken -> MailboxProcessor<'Msg> member Dispose: unit -> unit member Post: message: 'Msg -> unit member PostAndAsyncReply: buildMessage: (AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout: int -> Async<'Reply> member PostAndReply: buildMessage: (AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout: int -> 'Reply member PostAndTryAsyncReply: buildMessage: (AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout: int -> Async<'Reply option> member Receive: ?timeout: int -> Async<'Msg> member Scan: scanner: ('Msg -> Async<'T> option) * ?timeout: int -> Async<'T> member TryPostAndReply: buildMessage: (AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout: int -> 'Reply option ...

--------------------
new: body: (MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken: CancellationToken -> MailboxProcessor<'Msg>
static member MailboxProcessor.Start: body: (MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken: CancellationToken -> MailboxProcessor<'Msg>
val agent: MailboxProcessor<CacheMessage>
val pages: Collections.Generic.Dictionary<string,byte array>
namespace System.Collections
namespace System.Collections.Generic
Multiple items
type Dictionary<'TKey,'TValue> = interface ICollection<KeyValuePair<'TKey,'TValue>> interface IEnumerable<KeyValuePair<'TKey,'TValue>> interface IEnumerable interface IDictionary<'TKey,'TValue> interface IReadOnlyCollection<KeyValuePair<'TKey,'TValue>> interface IReadOnlyDictionary<'TKey,'TValue> interface ICollection interface IDictionary interface IDeserializationCallback interface ISerializable ...
<summary>Represents a collection of keys and values.</summary>
<typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
<typeparam name="TValue">The type of the values in the dictionary.</typeparam>


--------------------
Collections.Generic.Dictionary() : Collections.Generic.Dictionary<'TKey,'TValue>
Collections.Generic.Dictionary(dictionary: Collections.Generic.IDictionary<'TKey,'TValue>) : Collections.Generic.Dictionary<'TKey,'TValue>
Collections.Generic.Dictionary(collection: Collections.Generic.IEnumerable<Collections.Generic.KeyValuePair<'TKey,'TValue>>) : Collections.Generic.Dictionary<'TKey,'TValue>
Collections.Generic.Dictionary(comparer: Collections.Generic.IEqualityComparer<'TKey>) : Collections.Generic.Dictionary<'TKey,'TValue>
Collections.Generic.Dictionary(capacity: int) : Collections.Generic.Dictionary<'TKey,'TValue>
Collections.Generic.Dictionary(dictionary: Collections.Generic.IDictionary<'TKey,'TValue>, comparer: Collections.Generic.IEqualityComparer<'TKey>) : Collections.Generic.Dictionary<'TKey,'TValue>
Collections.Generic.Dictionary(collection: Collections.Generic.IEnumerable<Collections.Generic.KeyValuePair<'TKey,'TValue>>, comparer: Collections.Generic.IEqualityComparer<'TKey>) : Collections.Generic.Dictionary<'TKey,'TValue>
Collections.Generic.Dictionary(capacity: int, comparer: Collections.Generic.IEqualityComparer<'TKey>) : Collections.Generic.Dictionary<'TKey,'TValue>
val msg: CacheMessage
member MailboxProcessor.Receive: ?timeout: int -> Async<'Msg>
val url: string
val repl: AsyncReplyChannel<byte array option>
Collections.Generic.Dictionary.TryGetValue(key: string, value: byref<byte array>) : bool
val data: byte array
member AsyncReplyChannel.Reply: value: 'Reply -> unit
union case Option.Some: Value: 'T -> Option<'T>
union case Option.None: Option<'T>
val cts4: CancellationTokenSource
member MailboxProcessor.PostAndAsyncReply: buildMessage: (AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout: int -> Async<'Reply>
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> + 1 overload 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 Choice: computations: Async<'T option> seq -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * obj -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...

--------------------
type Async<'T>
static member Async.StartChild: computation: Async<'T> * ?millisecondsTimeout: int -> Async<Async<'T>>
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val rev: list: 'T list -> 'T list
val concat: arrays: 'T array seq -> 'T array
member MailboxProcessor.Post: message: 'Msg -> unit