Last active
August 29, 2015 14:28
-
-
Save allykzam/fb88fedf7b99a758bf4d to your computer and use it in GitHub Desktop.
Bad home-made server and basic Suave site for proxying calls for source code off to GitHub; for use with SourceLink
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Toy async server for fun | |
open System.Net.Sockets | |
let mutable runServer = true | |
let handleHeaders (text : string) = | |
let lines = text.Split([| '\n' |]) |> List.ofArray | |
let requestType = lines.Head.Split([| ' ' |]) | |
lines.Tail | |
|> List.filter (fun x -> not <| System.String.IsNullOrWhiteSpace x) | |
|> List.map (fun x -> x.Split([| ':' |], 2)) | |
|> List.map (fun x -> x.[0].ToLower(), x.[1]) | |
|> Map.ofList | |
|> Map.add "path" requestType.[1] | |
let optStr x = match x with None -> "" | Some(x) -> x | |
let noauth_message = "An authorization header is required to access private GitHub repositories. For 2FA users, please create an access token with just the \"repo\" access level checked off, and use the token as your password. May want to save it somewhere (like 1Password) for safekeeping :)" | |
let writeDataToClient (stream : NetworkStream) input endpoint = | |
let headers = handleHeaders input | |
let path = optStr <| headers.TryFind "path" | |
let (respHeaders, resp) = | |
printfn "Path requested: %s" path | |
match path with | |
| "/" | "" | null -> "HTTP/1.1 404 Not Found", "" | |
| "/index2.txt" -> "HTTP/1.1 200 Success", "Empty File!" | |
| _ -> | |
match headers.TryFind "authorization" with | |
| None -> "HTTP/1.1 401\nWWW-Authenticate: Basic realm=\"Private GitHub code proxy for SourceLink'd code", noauth_message | |
| Some(auth) -> | |
let githubUrl = sprintf "https://raw.githubusercontent.com%s" path | |
use wc = new System.Net.WebClient() | |
wc.Headers.Add("Authorization", auth) | |
try | |
let response = wc.DownloadString(githubUrl) | |
printfn "Retrieved data from GitHub" | |
"HTTP/1.1 200 Success", response | |
with | |
| :? System.Net.WebException as ex when ex.Message = "The remote server returned an error: (404) Not Found." -> "HTTP/1.1 404 Not Found", "GitHub can't find the specified file, or you do not have permission to view it." | |
| ex -> "HTTP/1.1 520 Unknown Error", ex.Message | |
let contentLength = System.Text.Encoding.UTF8.GetBytes(resp).Length | |
let text = sprintf "%s\nAccept-Ranges: bytes\nContent-Length: %i\n\n%s" respHeaders contentLength resp | |
let toWrite = System.Text.Encoding.UTF8.GetBytes(text) | |
printfn "Sending response to client..." | |
stream.Write(toWrite, 0, toWrite.Length) | |
let serveQuoteStream (client : TcpClient) = async { | |
let stream = client.GetStream() | |
let endpoint = client.Client.RemoteEndPoint | |
let incomingData = new System.Text.StringBuilder() | |
while client.Connected && runServer do | |
try | |
if not stream.DataAvailable then | |
do! Async.Sleep(10) | |
else | |
let data = stream.ReadByte() | |
if (data > -1 && data <> 13) then | |
let c = System.Text.Encoding.UTF8.GetChars([| byte data |]) | |
incomingData.Append(c) |> ignore | |
if incomingData.[incomingData.Length - 1] = '\n' && incomingData.[incomingData.Length - 2] = '\n' then | |
//System.Console.WriteLine("Incoming Data:") | |
let input = incomingData.ToString() | |
//System.Console.Write(input) | |
writeDataToClient stream input endpoint | |
incomingData.Clear() |> ignore | |
with ex -> | |
System.Console.WriteLine(ex.Message) | |
stream.Dispose() | |
printfn "Client %A disconnected" endpoint | |
if not runServer then | |
stream.Dispose() | |
client.Close() | |
} | |
let watchServer = async { | |
runServer <- true | |
let server = new TcpListener(System.Net.IPAddress.Any, 8004) | |
server.Start() | |
while runServer do | |
if server.Pending() then | |
let client = server.AcceptTcpClient() | |
serveQuoteStream client |> Async.Start | |
printfn "Accepted client %A" client.Client.RemoteEndPoint | |
else | |
do! Async.Sleep 10 | |
server.Stop() | |
System.Console.WriteLine("Server stopped!") | |
} | |
Async.RunSynchronously watchServer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#r "packages/Suave/lib/net40/Suave.dll" | |
open Suave.Http | |
open Suave.Http.Applicatives | |
open Suave.Http.Successful | |
open Suave.Types | |
let hasText = not << System.String.IsNullOrWhiteSpace | |
let checkAuthentication = Authentication.authenticateBasic (fun (x, y) -> hasText x && hasText y) | |
let getGitHubData (r : HttpRequest) : WebPart = | |
match r.header("authorization") with | |
| Choice2Of2(_) -> RequestErrors.FORBIDDEN "Authorization is required to access private GitHub repositories. If using 2-factor authentication, use a token with repo-level access as your password when authenticating." | |
| Choice1Of2(auth) -> | |
try | |
use wc = new System.Net.WebClient() | |
wc.Headers.Add("Authorization", auth) | |
let url = "https://raw.githubusercontent.com" + r.url.AbsolutePath | |
let responseData = wc.DownloadString(url) | |
OK responseData | |
with | |
| :? System.Net.WebException as ex when ex.Message = "The remote server returned an error: (404) Not Found." -> RequestErrors.NOT_FOUND "GitHub can't find the specified file, or you do not have permission to view it." | |
| ex -> ServerErrors.INTERNAL_ERROR <| sprintf "Received the following error requesting data from GitHub:\n\n%s" ex.Message | |
let app = | |
choose | |
[ | |
GET >>= choose | |
[ | |
path "/index2.txt" >>= OK "Hello, Visual Studio! Yes, this server is running."; | |
checkAuthentication; | |
request getGitHubData; | |
] | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment