[
  {
    "path": ".gitignore",
    "content": "websocketd\ngo-*\n"
  },
  {
    "path": "AUTHORS",
    "content": "websocketd authors\n==================\n\nJoe Walnes <joe@walnes.com>\nGareth Jones <gareth.e.jones@gmail.com>\nAjit George <ajit@ajitgeorge.com>\nAlex Sergeyev <abc@alexsergeyev.com>\n"
  },
  {
    "path": "CHANGES",
    "content": "Version 0.4.1 (Jan 24, 2021)\n\n* Minor changes only\n* Updated to Go 1.15.7\n\nVersion 0.3.1  (Jan 28, 2019)\n\n* Minor improvements to websocketd itself\n* Use of go modules, gorilla websockets set to 1.4.0\n* Binaries build code switched to 1.11.5 (improving underlying protocol handlers)\n\nVersion 0.3.0  (??, 2017)\n\n* Migration of underlying websocket server to Gorilla Websocket lib.\n* Binaries build code switched to 1.9.2\n\nVersion 0.2.12  (Feb 17, 2016)\n\n* Update of underlying go standard libraries change how SSL works. SSL3 is no longer supported.\n* Support of commands that do not provide text IO (using them as binary websocket frames)\n* Minor changes in examples and --help output \n\nVersion 0.2.11  (Jul 1, 2015)\n\n* PATH env variable is now passed to process by default\n* new --header* flags could generate custom HTTP headers for all websocketd-generated answers\n* fixed bug causing process to hang when WebSockets client disconnect is detected\n* minor changes for console app (default url building logic and tab char printing)\n* multiple changes of examples.\n\n\nVersion 0.2.10  (Feb 16, 2015)\n\n* fixes for null-origin situations (#75, #96)\n* better bash examples (#103)\n* changelog and checksums for released files (#101, #105)\n\n\nVersion 0.2.9  (May 19, 2014)\n\n* ability to listen multiple IP addresses (#40, #43)\n* proper support for TLS (#17)\n* resource limits enforcement (a.k.a. maxforks feature, #46)\n* passenv option to limit environment variables visible by running commands (#4)\n* fix for problem of closing upgraded websocket connection when script is not found (#29)\n* websocket origin restrictions via command line option (#20)\n* minor update for help flag behavior\n* minor fix for devconsole\n\nVersion 0.2.8  (Jan 11, 2014)\n\n* ..."
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2014, Joe Walnes and the websocketd authors.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met: \n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer. \n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution. \n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "Makefile",
    "content": "# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Self contained Go build file that will download and install (locally) the correct\n# version of Go, and build our programs. Go does not need to be installed on the\n# system (and if it already is, it will be ignored).\n\n# To manually invoke the locally installed Go, use ./go\n\n# Go installation config.\nGO_VER=1.11.5\nSYSTEM_NAME:=$(shell uname -s | tr '[:upper:]' '[:lower:]')\nSYSTEM_ARCH:=$(shell uname -m)\nGO_ARCH:=$(if $(filter x86_64, $(SYSTEM_ARCH)),amd64,386)\nGO_VERSION:=$(GO_VER).$(SYSTEM_NAME)-$(GO_ARCH)\nGO_DOWNLOAD_URL:=https://dl.google.com/go/go$(GO_VERSION).tar.gz\nGO_DIR:=go-$(GO_VER)\n\n# Build websocketd binary\nwebsocketd: $(GO_DIR)/bin/go $(wildcard *.go) $(wildcard libwebsocketd/*.go)\n\t$(GO_DIR)/bin/go build\n\nlocalgo: $(GO_DIR)/bin/go\n\n# Download and unpack Go distribution.\n$(GO_DIR)/bin/go:\n\tmkdir -p $(GO_DIR)\n\trm -f $@\n\t@echo Downloading and unpacking Go $(GO_VERSION) to $(GO_DIR)\n\tcurl -s $(GO_DOWNLOAD_URL) | tar xfz - --strip-components=1 -C $(GO_DIR)\n\n# Clean up binary\nclean:\n\trm -rf websocketd\n\n.PHONY: clean\n\n# Also clean up downloaded Go\nclobber: clean\n\trm -rf $(wildcard go-v*)\n\n.PHONY: clobber\n"
  },
  {
    "path": "README.md",
    "content": "websocketd\n==========\n\n`websocketd` is a small command-line tool that will wrap an existing command-line interface program, and allow it to be accessed via a WebSocket.\n\nWebSocket-capable applications can now be built very easily. As long as you can write an executable program that reads `STDIN` and writes to `STDOUT`, you can build a WebSocket server. Do it in Python, Ruby, Perl, Bash, .NET, C, Go, PHP, Java, Clojure, Scala, Groovy, Expect, Awk, VBScript, Haskell, Lua, R, whatever! No networking libraries necessary.\n\n-[@joewalnes](https://twitter.com/joewalnes)\n\nDetails\n-------\n\nUpon startup, `websocketd` will start a WebSocket server on a specified port, and listen for connections.\n\nUpon a connection, it will fork the appropriate process, and disconnect the process when the WebSocket connection closes (and vice-versa).\n\nAny message sent from the WebSocket client will be piped to the process's `STDIN` stream, followed by a `\\n` newline.\n\nAny text printed by the process to `STDOUT` shall be sent as a WebSocket message whenever a `\\n` newline is encountered.\n\n\nDownload\n--------\n\nIf you're on a Mac, you can install `websocketd` using [Homebrew](http://brew.sh/). Just run `brew install websocketd`. For other operating systems, or if you don't want to use Homebrew, check out the link below.\n\n**[Download for Linux, OS X and Windows](https://github.com/joewalnes/websocketd/wiki/Download-and-install)**\n\n\nQuickstart\n----------\n\nTo get started, we'll create a WebSocket endpoint that will accept connections, then send back messages, counting to 10 with 1 second pause between each one, before disconnecting.\n\nTo show how simple it is, let's do it in Bash!\n\n__count.sh__:\n\n```sh\n#!/bin/bash\nfor ((COUNT = 1; COUNT <= 10; COUNT++)); do\n  echo $COUNT\n  sleep 1\ndone\n```\n\nBefore turning it into a WebSocket server, let's test it from the command line. The beauty of `websocketd` is that servers work equally well in the command line, or in shell scripts, as they do in the server - with no modifications required.\n\n```sh\n$ chmod +x count.sh\n$ ./count.sh\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n```\n\nNow let's turn it into a WebSocket server:\n\n```sh\n$ websocketd --port=8080 ./count.sh\n```\n\nFinally, let's create a web-page to test it.\n\n__count.html__:\n\n```html\n<!DOCTYPE html>\n<pre id=\"log\"></pre>\n<script>\n  // helper function: log message to screen\n  function log(msg) {\n    document.getElementById('log').textContent += msg + '\\n';\n  }\n\n  // setup websocket with callbacks\n  var ws = new WebSocket('ws://localhost:8080/');\n  ws.onopen = function() {\n    log('CONNECT');\n  };\n  ws.onclose = function() {\n    log('DISCONNECT');\n  };\n  ws.onmessage = function(event) {\n    log('MESSAGE: ' + event.data);\n  };\n</script>\n```\nOpen this page in your web-browser. It will even work if you open it directly\nfrom disk using a `file://` URL.\n\nMore Features\n-------------\n\n*   Very simple install. Just [download](https://github.com/joewalnes/websocketd/wiki/Download-and-install) the single executable for Linux, Mac or Windows and run it. Minimal dependencies, no installers, no package managers, no external libraries. Suitable for development and production servers.\n*   Server side scripts can access details about the WebSocket HTTP request (e.g. remote host, query parameters, cookies, path, etc) via standard [CGI environment variables](https://github.com/joewalnes/websocketd/wiki/Environment-variables).\n*   As well as serving websocket daemons it also includes a static file server and classic CGI server for convenience.\n*   Command line help available via `websocketd --help`.\n*   Includes [WebSocket developer console](https://github.com/joewalnes/websocketd/wiki/Developer-console) to make it easy to test your scripts before you've built a JavaScript frontend.\n*   [Examples in many programming languages](https://github.com/joewalnes/websocketd/tree/master/examples) are available to help you getting started.\n\nUser Manual\n-----------\n\n**[More documentation in the user manual](https://github.com/joewalnes/websocketd/wiki)**\n\nExample Projects\n----------------\n\n*   [Plot real time Linux CPU/IO/Mem stats to a HTML5 dashboard using websocketd and vmstat](https://github.com/joewalnes/web-vmstats) _(for Linux)_\n*   [Arbitrary REPL in the browser using websocketd](https://github.com/rowanthorpe/ws-repl)\n*   [Retrieve SQL data from server with LiveCode and webSocketd](https://github.com/samansjukur/wslc)\n*   [List files from a configured folder](https://github.com/dbalakirev/directator) _(for Linux)_\n*   [Listen for gamepad events and report them to the system](https://github.com/experiment322/controlloid-server) _(this + android = gamepad emulator)_\n\nGot more examples? Open a pull request.\n\nMy Other Projects\n-----------------\n\n*   [ReconnectingWebSocket](https://github.com/joewalnes/reconnecting-websocket) - Simplest way to add some robustness to your WebSocket connections.\n*   [Smoothie Charts](http://smoothiecharts.org/) - JavaScript charts for streaming data.\n*   Visit [The Igloo Lab](http://theigloolab.com/) to see and subscribe to other thingies I make.\n\nAnd [follow @joewalnes](https://twitter.com/joewalnes)!\n"
  },
  {
    "path": "config.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/joewalnes/websocketd/libwebsocketd\"\n)\n\ntype Config struct {\n\tAddr              []string // TCP addresses to listen on. e.g. \":1234\", \"1.2.3.4:1234\" or \"[::1]:1234\"\n\tMaxForks          int      // Number of allowable concurrent forks\n\tLogLevel          libwebsocketd.LogLevel\n\tRedirPort         int\n\tCertFile, KeyFile string\n\t*libwebsocketd.Config\n}\n\ntype Arglist []string\n\nfunc (al *Arglist) String() string {\n\treturn fmt.Sprintf(\"%v\", []string(*al))\n}\n\nfunc (al *Arglist) Set(value string) error {\n\t*al = append(*al, value)\n\treturn nil\n}\n\n// Borrowed from net/http/cgi\nvar defaultPassEnv = map[string]string{\n\t\"darwin\":  \"PATH,DYLD_LIBRARY_PATH\",\n\t\"freebsd\": \"PATH,LD_LIBRARY_PATH\",\n\t\"hpux\":    \"PATH,LD_LIBRARY_PATH,SHLIB_PATH\",\n\t\"irix\":    \"PATH,LD_LIBRARY_PATH,LD_LIBRARYN32_PATH,LD_LIBRARY64_PATH\",\n\t\"linux\":   \"PATH,LD_LIBRARY_PATH\",\n\t\"openbsd\": \"PATH,LD_LIBRARY_PATH\",\n\t\"solaris\": \"PATH,LD_LIBRARY_PATH,LD_LIBRARY_PATH_32,LD_LIBRARY_PATH_64\",\n\t\"windows\": \"PATH,SystemRoot,COMSPEC,PATHEXT,WINDIR\",\n}\n\nfunc parseCommandLine() *Config {\n\tvar mainConfig Config\n\tvar config libwebsocketd.Config\n\n\tflag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)\n\tflag.CommandLine.Usage = func() {}\n\n\t// If adding new command line options, also update the help text in help.go.\n\t// The flag library's auto-generate help message isn't pretty enough.\n\n\taddrlist := Arglist(make([]string, 0, 1)) // pre-reserve for 1 address\n\tflag.Var(&addrlist, \"address\", \"Interfaces to bind to (e.g. 127.0.0.1 or [::1]).\")\n\n\t// server config options\n\tportFlag := flag.Int(\"port\", 0, \"HTTP port to listen on\")\n\tversionFlag := flag.Bool(\"version\", false, \"Print version and exit\")\n\tlicenseFlag := flag.Bool(\"license\", false, \"Print license and exit\")\n\tlogLevelFlag := flag.String(\"loglevel\", \"access\", \"Log level, one of: debug, trace, access, info, error, fatal\")\n\tsslFlag := flag.Bool(\"ssl\", false, \"Use TLS on listening socket (see also --sslcert and --sslkey)\")\n\tsslCert := flag.String(\"sslcert\", \"\", \"Should point to certificate PEM file when --ssl is used\")\n\tsslKey := flag.String(\"sslkey\", \"\", \"Should point to certificate private key file when --ssl is used\")\n\tmaxForksFlag := flag.Int(\"maxforks\", 0, \"Max forks, zero means unlimited\")\n\tcloseMsFlag := flag.Uint(\"closems\", 0, \"Time to start sending signals (0 never)\")\n\tredirPortFlag := flag.Int(\"redirport\", 0, \"HTTP port to redirect to canonical --port address\")\n\n\t// lib config options\n\tbinaryFlag := flag.Bool(\"binary\", false, \"Set websocketd to experimental binary mode (default is line by line)\")\n\treverseLookupFlag := flag.Bool(\"reverselookup\", false, \"Perform reverse DNS lookups on remote clients\")\n\tscriptDirFlag := flag.String(\"dir\", \"\", \"Base directory for WebSocket scripts\")\n\tstaticDirFlag := flag.String(\"staticdir\", \"\", \"Serve static content from this directory over HTTP\")\n\tcgiDirFlag := flag.String(\"cgidir\", \"\", \"Serve CGI scripts from this directory over HTTP\")\n\tdevConsoleFlag := flag.Bool(\"devconsole\", false, \"Enable development console (cannot be used in conjunction with --staticdir)\")\n\tpassEnvFlag := flag.String(\"passenv\", defaultPassEnv[runtime.GOOS], \"List of envvars to pass to subprocesses (others will be cleaned out)\")\n\tsameOriginFlag := flag.Bool(\"sameorigin\", false, \"Restrict upgrades if origin and host headers differ\")\n\tallowOriginsFlag := flag.String(\"origin\", \"\", \"Restrict upgrades if origin does not match the list\")\n\n\theaders := Arglist(make([]string, 0))\n\theadersWs := Arglist(make([]string, 0))\n\theadersHttp := Arglist(make([]string, 0))\n\tflag.Var(&headers, \"header\", \"Custom headers for any response.\")\n\tflag.Var(&headersWs, \"header-ws\", \"Custom headers for successful WebSocket upgrade responses.\")\n\tflag.Var(&headersHttp, \"header-http\", \"Custom headers for all but WebSocket upgrade HTTP responses.\")\n\n\terr := flag.CommandLine.Parse(os.Args[1:])\n\tif err != nil {\n\t\tif err == flag.ErrHelp {\n\t\t\tPrintHelp()\n\t\t\tos.Exit(0)\n\t\t} else {\n\t\t\tShortHelp()\n\t\t\tos.Exit(2)\n\t\t}\n\t}\n\n\tport := *portFlag\n\tif port == 0 {\n\t\tif *sslFlag {\n\t\t\tport = 443\n\t\t} else {\n\t\t\tport = 80\n\t\t}\n\t}\n\n\tif socknum := len(addrlist); socknum != 0 {\n\t\tmainConfig.Addr = make([]string, socknum)\n\t\tfor i, addrSingle := range addrlist {\n\t\t\tmainConfig.Addr[i] = fmt.Sprintf(\"%s:%d\", addrSingle, port)\n\t\t}\n\t} else {\n\t\tmainConfig.Addr = []string{fmt.Sprintf(\":%d\", port)}\n\t}\n\tmainConfig.MaxForks = *maxForksFlag\n\tmainConfig.RedirPort = *redirPortFlag\n\tmainConfig.LogLevel = libwebsocketd.LevelFromString(*logLevelFlag)\n\tif mainConfig.LogLevel == libwebsocketd.LogUnknown {\n\t\tfmt.Printf(\"Incorrect loglevel flag '%s'. Use --help to see allowed values.\\n\", *logLevelFlag)\n\t\tShortHelp()\n\t\tos.Exit(1)\n\t}\n\n\tconfig.Headers = []string(headers)\n\tconfig.HeadersWs = []string(headersWs)\n\tconfig.HeadersHTTP = []string(headersHttp)\n\n\tconfig.CloseMs = *closeMsFlag\n\tconfig.Binary = *binaryFlag\n\tconfig.ReverseLookup = *reverseLookupFlag\n\tconfig.Ssl = *sslFlag\n\tconfig.ScriptDir = *scriptDirFlag\n\tconfig.StaticDir = *staticDirFlag\n\tconfig.CgiDir = *cgiDirFlag\n\tconfig.DevConsole = *devConsoleFlag\n\tconfig.StartupTime = time.Now()\n\tconfig.ServerSoftware = fmt.Sprintf(\"websocketd/%s\", Version())\n\tconfig.HandshakeTimeout = time.Millisecond * 1500 // only default for now\n\n\tif len(os.Args) == 1 {\n\t\tfmt.Printf(\"Command line arguments are missing.\\n\")\n\t\tShortHelp()\n\t\tos.Exit(1)\n\t}\n\n\tif *versionFlag {\n\t\tfmt.Printf(\"%s %s\\n\", HelpProcessName(), Version())\n\t\tos.Exit(0)\n\t}\n\n\tif *licenseFlag {\n\t\tfmt.Printf(\"%s %s\\n\", HelpProcessName(), Version())\n\t\tfmt.Printf(\"%s\\n\", libwebsocketd.License)\n\t\tos.Exit(0)\n\t}\n\n\t// Reading SSL options\n\tif config.Ssl {\n\t\tif *sslCert == \"\" || *sslKey == \"\" {\n\t\t\tfmt.Fprintf(os.Stderr, \"Please specify both --sslcert and --sslkey when requesting --ssl.\\n\")\n\t\t\tos.Exit(1)\n\t\t}\n\t} else {\n\t\tif *sslCert != \"\" || *sslKey != \"\" {\n\t\t\tfmt.Fprintf(os.Stderr, \"You should not be using --ssl* flags when there is no --ssl option.\\n\")\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tmainConfig.CertFile = *sslCert\n\tmainConfig.KeyFile = *sslKey\n\n\t// Building config.ParentEnv to avoid calling Environ all the time in the scripts\n\t// (caller is responsible for wiping environment if desired)\n\tconfig.ParentEnv = make([]string, 0)\n\tnewlineCleaner := strings.NewReplacer(\"\\n\", \" \", \"\\r\", \" \")\n\tfor _, key := range strings.Split(*passEnvFlag, \",\") {\n\t\tif key != \"HTTPS\" {\n\t\t\tif v := os.Getenv(key); v != \"\" {\n\t\t\t\t// inevitably adding flavor of libwebsocketd appendEnv func.\n\t\t\t\t// it's slightly nicer than in net/http/cgi implementation\n\t\t\t\tif clean := strings.TrimSpace(newlineCleaner.Replace(v)); clean != \"\" {\n\t\t\t\t\tconfig.ParentEnv = append(config.ParentEnv, fmt.Sprintf(\"%s=%s\", key, clean))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif *allowOriginsFlag != \"\" {\n\t\tconfig.AllowOrigins = strings.Split(*allowOriginsFlag, \",\")\n\t}\n\tconfig.SameOrigin = *sameOriginFlag\n\n\targs := flag.Args()\n\tif len(args) < 1 && config.ScriptDir == \"\" && config.StaticDir == \"\" && config.CgiDir == \"\" {\n\t\tfmt.Fprintf(os.Stderr, \"Please specify COMMAND or provide --dir, --staticdir or --cgidir argument.\\n\")\n\t\tShortHelp()\n\t\tos.Exit(1)\n\t}\n\n\tif len(args) > 0 {\n\t\tif config.ScriptDir != \"\" {\n\t\t\tfmt.Fprintf(os.Stderr, \"Ambiguous. Provided COMMAND and --dir argument. Please only specify just one.\\n\")\n\t\t\tShortHelp()\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif path, err := exec.LookPath(args[0]); err == nil {\n\t\t\tconfig.CommandName = path // This can be command in PATH that we are able to execute\n\t\t\tconfig.CommandArgs = flag.Args()[1:]\n\t\t\tconfig.UsingScriptDir = false\n\t\t} else {\n\t\t\tfmt.Fprintf(os.Stderr, \"Unable to locate specified COMMAND '%s' in OS path.\\n\", args[0])\n\t\t\tShortHelp()\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tif config.ScriptDir != \"\" {\n\t\tscriptDir, err := filepath.Abs(config.ScriptDir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Could not resolve absolute path to dir '%s'.\\n\", config.ScriptDir)\n\t\t\tShortHelp()\n\t\t\tos.Exit(1)\n\t\t}\n\t\tinf, err := os.Stat(scriptDir)\n\t\tif err != nil {\n\t\t\tfmt.Fprintf(os.Stderr, \"Could not find your script dir '%s'.\\n\", config.ScriptDir)\n\t\t\tShortHelp()\n\t\t\tos.Exit(1)\n\t\t}\n\t\tif !inf.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"Did you mean to specify COMMAND instead of --dir '%s'?\\n\", config.ScriptDir)\n\t\t\tShortHelp()\n\t\t\tos.Exit(1)\n\t\t} else {\n\t\t\tconfig.ScriptDir = scriptDir\n\t\t\tconfig.UsingScriptDir = true\n\t\t}\n\t}\n\n\tif config.CgiDir != \"\" {\n\t\tif inf, err := os.Stat(config.CgiDir); err != nil || !inf.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"Your CGI dir '%s' is not pointing to an accessible directory.\\n\", config.CgiDir)\n\t\t\tShortHelp()\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tif config.StaticDir != \"\" {\n\t\tif inf, err := os.Stat(config.StaticDir); err != nil || !inf.IsDir() {\n\t\t\tfmt.Fprintf(os.Stderr, \"Your static dir '%s' is not pointing to an accessible directory.\\n\", config.StaticDir)\n\t\t\tShortHelp()\n\t\t\tos.Exit(1)\n\t\t}\n\t}\n\n\tmainConfig.Config = &config\n\n\treturn &mainConfig\n}\n"
  },
  {
    "path": "examples/bash/README.txt",
    "content": "This examples directory shows some examples written in Bash.\n\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/bash/chat.sh",
    "content": "#!/bin/bash\n\n# Copyright 2013 Jeroen Janssens\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Run a simple chat server: websocketd --devconsole --port 8080 ./chat.sh\n#\n# Please note that this example requires GNU tail, which is not the default\n# tail on OS X. Even though this script properly escapes the variables,\n# please keep in mind that it is in general a bad idea to read\n# untrusted data into variables and pass this onto the command line.\n\necho \"Please enter your name:\"; read USER\necho \"[$(date)] ${USER} joined the chat\" >> chat.log\necho \"[$(date)] Welcome to the chat ${USER}!\"\ntail -n 0 -f chat.log --pid=$$ | grep --line-buffered -v \"] ${USER}>\" &\nwhile read MSG; do echo \"[$(date)] ${USER}> ${MSG}\" >> chat.log; done\n"
  },
  {
    "path": "examples/bash/count.sh",
    "content": "#!/bin/bash\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Simple example script that counts to 10 at ~2Hz, then stops.\nfor ((COUNT = 1; COUNT <= 10; COUNT++))\ndo\n  echo $COUNT\n  sleep 0.5\ndone\n"
  },
  {
    "path": "examples/bash/dump-env.sh",
    "content": "#!/bin/bash\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Standard CGI(ish) environment variables, as defined in\n# http://tools.ietf.org/html/rfc3875\nNAMES=\"\"\"\n  AUTH_TYPE\n  CONTENT_LENGTH\n  CONTENT_TYPE\n  GATEWAY_INTERFACE\n  PATH_INFO\n  PATH_TRANSLATED\n  QUERY_STRING\n  REMOTE_ADDR\n  REMOTE_HOST\n  REMOTE_IDENT\n  REMOTE_PORT\n  REMOTE_USER\n  REQUEST_METHOD\n  REQUEST_URI\n  SCRIPT_NAME\n  SERVER_NAME\n  SERVER_PORT\n  SERVER_PROTOCOL\n  SERVER_SOFTWARE\n  UNIQUE_ID\n  HTTPS\n\"\"\"\n\nfor NAME in ${NAMES}\ndo\n\techo \"${NAME}=${!NAME:-<unset>}\"\ndone\n\n# Additional HTTP headers\nenv | grep '^HTTP_'\n"
  },
  {
    "path": "examples/bash/greeter.sh",
    "content": "#!/bin/bash\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# For each line FOO received on STDIN, respond with \"Hello FOO!\".\nwhile read LINE\ndo\n\techo \"Hello $LINE!\"\ndone"
  },
  {
    "path": "examples/bash/send-receive.sh",
    "content": "#!/bin/bash\n\n\nwhile true; do\n\tcnt=0\n\twhile read -t 0.01 _; do\n\t\t((cnt++))\n\tdone\n\n\techo \"$(date)\" \"($cnt line(s) received)\"\n\tsleep $((RANDOM % 10 + 1)) & wait\ndone\n"
  },
  {
    "path": "examples/c#/.gitignore",
    "content": "# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)\n[Bb]in/\n[Oo]bj/\n\n# mstest test results\nTestResults\n\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Rr]elease/\nbuild/\ntest/\ndeploy/\nx64/\n*_i.c\n*_p.c\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.vspscc\n*.vssscc\n.builds\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n\n# Visual Studio profiler\n*.psess\n*.vsp\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*\n\n# NCrunch\n*ncrunch*/*\n*.ncrunch*\n.*crunch*.local.xml\n\n# Installshield output folder \n[Ee]xpress\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish\n\n# Publish Web Output\n*.Publish.xml\n\n# Windows Azure Build Output\ncsx\n*.build.csdef\n\n# Others\n[Bb]in\n[Oo]bj\nsql\nTestResults\n[Tt]est[Rr]esult*\n*.Cache\nClientBin\n[Ss]tyle[Cc]op.*\n~$*\n*.dbmdl\nGenerated_Code #added for RIA/Silverlight projects\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML"
  },
  {
    "path": "examples/c#/Count/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5\" />\n    </startup>\n</configuration>"
  },
  {
    "path": "examples/c#/Count/Count.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{FD04ED82-2C8C-4D5C-9327-FB73482DD3D1}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>Count</RootNamespace>\n    <AssemblyName>Count</AssemblyName>\n    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "examples/c#/Count/Program.cs",
    "content": "﻿using System;\nusing System.Linq;\nusing System.Threading;\n\nnamespace Count\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            foreach (var i in Enumerable.Range(1, 10))\n            {\n                Console.WriteLine(i);\n                Thread.Sleep(1000);\n            }\n        }\n    }\n}"
  },
  {
    "path": "examples/c#/Count/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"Count\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyProduct(\"Count\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"38cb6838-4839-498f-b09f-d0e67a2e9974\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "examples/c#/Echo/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5\" />\n    </startup>\n</configuration>"
  },
  {
    "path": "examples/c#/Echo/Echo.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <ProjectGuid>{65414388-1058-414C-910F-CBD58E2B064A}</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <AppDesignerFolder>Properties</AppDesignerFolder>\n    <RootNamespace>Echo</RootNamespace>\n    <AssemblyName>Echo</AssemblyName>\n    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\n    <FileAlignment>512</FileAlignment>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <OutputPath>..\\bin\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <OutputPath>..\\bin\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <ErrorReport>prompt</ErrorReport>\n    <WarningLevel>4</WarningLevel>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Xml.Linq\" />\n    <Reference Include=\"System.Data.DataSetExtensions\" />\n    <Reference Include=\"Microsoft.CSharp\" />\n    <Reference Include=\"System.Data\" />\n    <Reference Include=\"System.Xml\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.cs\" />\n    <Compile Include=\"Properties\\AssemblyInfo.cs\" />\n  </ItemGroup>\n  <ItemGroup>\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "examples/c#/Echo/Program.cs",
    "content": "﻿using System;\n\nnamespace Echo\n{\n    class Program\n    {\n        static void Main(string[] args)\n        {\n            while (true)\n            {\n                var msg = Console.ReadLine();\n                Console.WriteLine(msg);\n            }\n        }\n    }\n}"
  },
  {
    "path": "examples/c#/Echo/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Information about an assembly is controlled through the following \n// set of attributes. Change these attribute values to modify the information\n// associated with an assembly.\n[assembly: AssemblyTitle(\"Echo\")]\n[assembly: AssemblyDescription(\"\")]\n[assembly: AssemblyConfiguration(\"\")]\n[assembly: AssemblyProduct(\"Echo\")]\n[assembly: AssemblyTrademark(\"\")]\n[assembly: AssemblyCulture(\"\")]\n\n// Setting ComVisible to false makes the types in this assembly not visible \n// to COM components.  If you need to access a type in this assembly from \n// COM, set the ComVisible attribute to true on that type.\n[assembly: ComVisible(false)]\n\n// The following GUID is for the ID of the typelib if this project is exposed to COM\n[assembly: Guid(\"6199e327-f4cd-438c-a6c5-87861f837fb1\")]\n\n// Version information for an assembly consists of the following four values:\n//\n//      Major Version\n//      Minor Version \n//      Build Number\n//      Revision\n//\n// You can specify all the values or you can default the Build and Revision Numbers \n// by using the '*' as shown below:\n// [assembly: AssemblyVersion(\"1.0.*\")]\n[assembly: AssemblyVersion(\"1.0.0.0\")]\n[assembly: AssemblyFileVersion(\"1.0.0.0\")]\n"
  },
  {
    "path": "examples/c#/Examples.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 2012\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Echo\", \"Echo\\Echo.csproj\", \"{65414388-1058-414C-910F-CBD58E2B064A}\"\nEndProject\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Count\", \"Count\\Count.csproj\", \"{FD04ED82-2C8C-4D5C-9327-FB73482DD3D1}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{65414388-1058-414C-910F-CBD58E2B064A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{65414388-1058-414C-910F-CBD58E2B064A}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{65414388-1058-414C-910F-CBD58E2B064A}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{65414388-1058-414C-910F-CBD58E2B064A}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{FD04ED82-2C8C-4D5C-9327-FB73482DD3D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{FD04ED82-2C8C-4D5C-9327-FB73482DD3D1}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{FD04ED82-2C8C-4D5C-9327-FB73482DD3D1}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{FD04ED82-2C8C-4D5C-9327-FB73482DD3D1}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "examples/c#/README.md",
    "content": "## Running the examples on Windows\n\n1. [download and install](https://github.com/joewalnes/websocketd/wiki/Download-and-install) websocketd (don't forget to add to your PATH)\n2. open and build the **Examples.sln** solution\n3. double click the **run_echo.cmd** to start an echo example, go to http://localhost:8080 to interact with it\n4. double click the **run_count.cmd** to start the count example, go to the [examples/html](https://github.com/joewalnes/websocketd/tree/master/examples/html) folder and double click **count.html** to open in a browser"
  },
  {
    "path": "examples/c#/run_count.cmd",
    "content": "websocketd --port=8080 --devconsole bin\\Count.exe"
  },
  {
    "path": "examples/c#/run_echo.cmd",
    "content": "websocketd --port=8080 --devconsole bin\\Echo.exe"
  },
  {
    "path": "examples/cgi-bin/README.txt",
    "content": "This examples directory shows how websocketd can also serve CGI scripts via HTTP.\n\n$ websocketd --port=1234 --cgidir=examples/cgi-bin\n# Then access http://localhost:1234/dump-env.sh\n\n\nYou can also test the command files by running from the command line.\n\n"
  },
  {
    "path": "examples/cgi-bin/dump-env.sh",
    "content": "#!/bin/sh\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Standard CGI(ish) environment variables, as defined in\n# http://tools.ietf.org/html/rfc3875\nNAMES=\"\"\"\n  AUTH_TYPE\n  CONTENT_LENGTH\n  CONTENT_TYPE\n  GATEWAY_INTERFACE\n  PATH_INFO\n  PATH_TRANSLATED\n  QUERY_STRING\n  REMOTE_ADDR\n  REMOTE_HOST\n  REMOTE_IDENT\n  REMOTE_PORT\n  REMOTE_USER\n  REQUEST_METHOD\n  REQUEST_URI\n  SCRIPT_NAME\n  SERVER_NAME\n  SERVER_PORT\n  SERVER_PROTOCOL\n  SERVER_SOFTWARE\n  UNIQUE_ID\n  HTTPS\n\"\"\"\n\necho \"Content-type: text/plain\"\necho\n\nfor NAME in ${NAMES}\ndo\n\teval \"value=\\${${NAME}}\"\n\tenv -i \"${NAME}=${value:-<unset>}\"\ndone\n\n# Additional HTTP headers\nenv | egrep '^(HTTP|SSL)_'\n"
  },
  {
    "path": "examples/f#/.gitignore",
    "content": "# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)\n[Bb]in/\n[Oo]bj/\n\n# mstest test results\nTestResults\n\n## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n[Dd]ebug/\n[Rr]elease/\nbuild/\ntest/\ndeploy/\nx64/\n*_i.c\n*_p.c\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.vspscc\n*.vssscc\n.builds\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n\n# Visual Studio profiler\n*.psess\n*.vsp\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*\n\n# NCrunch\n*ncrunch*/*\n*.ncrunch*\n.*crunch*.local.xml\n\n# Installshield output folder \n[Ee]xpress\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish\n\n# Publish Web Output\n*.Publish.xml\n\n# Windows Azure Build Output\ncsx\n*.build.csdef\n\n# Others\n[Bb]in\n[Oo]bj\nsql\nTestResults\n[Tt]est[Rr]esult*\n*.Cache\nClientBin\n[Ss]tyle[Cc]op.*\n~$*\n*.dbmdl\nGenerated_Code #added for RIA/Silverlight projects\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML"
  },
  {
    "path": "examples/f#/Count/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5\" />\n    </startup>\n    <runtime>\n      <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n        <dependentAssembly>\n          <assemblyIdentity name=\"FSharp.Core\" publicKeyToken=\"b03f5f7f11d50a3a\" culture=\"neutral\"/>\n          <bindingRedirect oldVersion=\"4.0.0.0\" newVersion=\"4.3.0.0\"/>\n          <bindingRedirect oldVersion=\"2.3.5.0\" newVersion=\"4.3.0.0\"/>\n          <bindingRedirect oldVersion=\"2.0.0.0\" newVersion=\"4.3.0.0\"/>\n          \n        </dependentAssembly>\n      </assemblyBinding>\n    </runtime>\t\n</configuration>"
  },
  {
    "path": "examples/f#/Count/Count.fsproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>367c65fd-485b-45c0-9c0e-7ce455951eae</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>Count</RootNamespace>\n    <AssemblyName>Count</AssemblyName>\n    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\n    <Name>Count</Name>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <Tailcalls>false</Tailcalls>\n    <OutputPath>..\\bin\\</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <WarningLevel>3</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DocumentationFile>..\\bin\\Count.XML</DocumentationFile>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <Tailcalls>true</Tailcalls>\n    <OutputPath>..\\bin\\</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <WarningLevel>3</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DocumentationFile>..\\bin\\Count.XML</DocumentationFile>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"mscorlib\" />\n    <Reference Include=\"FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\">\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Numerics\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.fs\" />\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <PropertyGroup>\n    <MinimumVisualStudioVersion Condition=\"'$(MinimumVisualStudioVersion)' == ''\">11</MinimumVisualStudioVersion>\n  </PropertyGroup>\n  <Import Project=\"$(MSBuildExtensionsPath32)\\..\\Microsoft SDKs\\F#\\3.0\\Framework\\v4.0\\Microsoft.FSharp.Targets\" Condition=\" Exists('$(MSBuildExtensionsPath32)\\..\\Microsoft SDKs\\F#\\3.0\\Framework\\v4.0\\Microsoft.FSharp.Targets')\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "examples/f#/Count/Program.fs",
    "content": "﻿open System\nopen System.Threading\n\n[<EntryPoint>]\nlet main argv = \n    [| 1..10 |] |> Array.iter (Console.WriteLine >> (fun _ -> Thread.Sleep(1000)))\n\n    0 // return an integer exit code\n"
  },
  {
    "path": "examples/f#/Echo/App.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<configuration>\n    <startup> \n        <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5\" />\n    </startup>\n    <runtime>\n      <assemblyBinding xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n        <dependentAssembly>\n          <assemblyIdentity name=\"FSharp.Core\" publicKeyToken=\"b03f5f7f11d50a3a\" culture=\"neutral\"/>\n          <bindingRedirect oldVersion=\"4.0.0.0\" newVersion=\"4.3.0.0\"/>\n          <bindingRedirect oldVersion=\"2.3.5.0\" newVersion=\"4.3.0.0\"/>\n          <bindingRedirect oldVersion=\"2.0.0.0\" newVersion=\"4.3.0.0\"/>\n          \n        </dependentAssembly>\n      </assemblyBinding>\n    </runtime>\t\n</configuration>"
  },
  {
    "path": "examples/f#/Echo/Echo.fsproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"4.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\n  <PropertyGroup>\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\n    <SchemaVersion>2.0</SchemaVersion>\n    <ProjectGuid>6f680332-caa0-447b-a87e-af272ded5701</ProjectGuid>\n    <OutputType>Exe</OutputType>\n    <RootNamespace>Echo</RootNamespace>\n    <AssemblyName>Echo</AssemblyName>\n    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\n    <Name>Echo</Name>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\n    <DebugSymbols>true</DebugSymbols>\n    <DebugType>full</DebugType>\n    <Optimize>false</Optimize>\n    <Tailcalls>false</Tailcalls>\n    <OutputPath>..\\bin</OutputPath>\n    <DefineConstants>DEBUG;TRACE</DefineConstants>\n    <WarningLevel>3</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DocumentationFile>..\\bin\\Echo.XML</DocumentationFile>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\n    <DebugType>pdbonly</DebugType>\n    <Optimize>true</Optimize>\n    <Tailcalls>true</Tailcalls>\n    <OutputPath>..\\bin</OutputPath>\n    <DefineConstants>TRACE</DefineConstants>\n    <WarningLevel>3</WarningLevel>\n    <PlatformTarget>AnyCPU</PlatformTarget>\n    <DocumentationFile>bin\\Release\\Echo.XML</DocumentationFile>\n    <Prefer32Bit>true</Prefer32Bit>\n  </PropertyGroup>\n  <ItemGroup>\n    <Reference Include=\"mscorlib\" />\n    <Reference Include=\"FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a\">\n      <Private>True</Private>\n    </Reference>\n    <Reference Include=\"System\" />\n    <Reference Include=\"System.Core\" />\n    <Reference Include=\"System.Numerics\" />\n  </ItemGroup>\n  <ItemGroup>\n    <Compile Include=\"Program.fs\" />\n    <None Include=\"App.config\" />\n  </ItemGroup>\n  <PropertyGroup>\n    <MinimumVisualStudioVersion Condition=\"'$(MinimumVisualStudioVersion)' == ''\">11</MinimumVisualStudioVersion>\n  </PropertyGroup>\n  <Import Project=\"$(MSBuildExtensionsPath32)\\..\\Microsoft SDKs\\F#\\3.0\\Framework\\v4.0\\Microsoft.FSharp.Targets\" Condition=\" Exists('$(MSBuildExtensionsPath32)\\..\\Microsoft SDKs\\F#\\3.0\\Framework\\v4.0\\Microsoft.FSharp.Targets')\" />\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\n</Project>"
  },
  {
    "path": "examples/f#/Echo/Program.fs",
    "content": "﻿open System\n\n[<EntryPoint>]\nlet main argv = \n    let rec recLoop () =\n        Console.ReadLine() |> Console.WriteLine\n        recLoop()\n\n    recLoop()\n    \n    0 // return an integer exit code\n"
  },
  {
    "path": "examples/f#/Examples.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 2012\nProject(\"{F2A71F9B-5D33-465A-A702-920D77279786}\") = \"Echo\", \"Echo\\Echo.fsproj\", \"{6F680332-CAA0-447B-A87E-AF272DED5701}\"\nEndProject\nProject(\"{F2A71F9B-5D33-465A-A702-920D77279786}\") = \"Count\", \"Count\\Count.fsproj\", \"{367C65FD-485B-45C0-9C0E-7CE455951EAE}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{6F680332-CAA0-447B-A87E-AF272DED5701}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{6F680332-CAA0-447B-A87E-AF272DED5701}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{6F680332-CAA0-447B-A87E-AF272DED5701}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{6F680332-CAA0-447B-A87E-AF272DED5701}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{367C65FD-485B-45C0-9C0E-7CE455951EAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{367C65FD-485B-45C0-9C0E-7CE455951EAE}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{367C65FD-485B-45C0-9C0E-7CE455951EAE}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{367C65FD-485B-45C0-9C0E-7CE455951EAE}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "examples/f#/README.md",
    "content": "## Running the examples on Windows\n\n1. [download and install](https://github.com/joewalnes/websocketd/wiki/Download-and-install) websocketd (don't forget to add to your PATH)\n2. open and build the **Examples.sln** solution\n3. double click the **run_echo.cmd** to start an echo example, go to http://localhost:8080 to interact with it\n4. double click the **run_count.cmd** to start the count example, go to the [examples/html](https://github.com/joewalnes/websocketd/tree/master/examples/html) folder and double click **count.html** to open in a browser"
  },
  {
    "path": "examples/f#/run_count.cmd",
    "content": "websocketd --port=8080 --devconsole bin\\Count.exe"
  },
  {
    "path": "examples/f#/run_echo.cmd",
    "content": "websocketd --port=8080 --devconsole bin\\Echo.exe"
  },
  {
    "path": "examples/hack/README.md",
    "content": "This examples directory shows some examples written in [Hack](https://hacklang.org).\n\n### Requirements :\n\n- [HHVM](https://github.com/facebook/hhvm) 3.30+\n- [HSL ( Hack Standard Library )](https://github.com/hhvm/hsl) 3.30+\n- [HSL Experimental](https://github.com/hhvm/hsl-experimental) 3.30+\n\nYou can also test the command files by running from the command line :\n\n```\n$ hhvm count.hh\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n```\n"
  },
  {
    "path": "examples/hack/count.hh",
    "content": "#!/usr/bin/hhvm\n<?hh // strict\n\nuse namespace HH\\Lib\\Str;\nuse function HH\\Lib\\Experimental\\IO\\request_output;\n\n// Simple example script that counts to 10 at ~2Hz, then stops.\n\n<<__EntryPoint>>\nasync function count_to_ten(): Awaitable<noreturn> {\n  $output = request_output();\n  for ($count = 1; $count <= 10; $count++) {\n    await $output->writeAsync(\n      Str\\format(\"%d\\n\",$count)\n    );\n\n    HH\\Asio\\usleep(500000);\n  }\n\n  // flush output\n  await $output->flushAsync();\n\n  exit(0);\n}\n"
  },
  {
    "path": "examples/hack/dump-env.hh",
    "content": "#!/usr/bin/hhvm\n<?hh // strict\n\nuse namespace HH\\Lib\\Str;\nuse function HH\\Lib\\Experimental\\IO\\request_output;\n\n<<__EntryPoint>>\nasync function dumpEnv(): Awaitable<noreturn> {\n  // Standard CGI(ish) environment variables, as defined in\n  // http://tools.ietf.org/html/rfc3875\n  $names = keyset[\n    'AUTH_TYPE',\n    'CONTENT_LENGTH',\n    'CONTENT_TYPE',\n    'GATEWAY_INTERFACE',\n    'PATH_INFO',\n    'PATH_TRANSLATED',\n    'QUERY_STRING',\n    'REMOTE_ADDR',\n    'REMOTE_HOST',\n    'REMOTE_IDENT',\n    'REMOTE_PORT',\n    'REMOTE_USER',\n    'REQUEST_METHOD',\n    'REQUEST_URI',\n    'SCRIPT_NAME',\n    'SERVER_NAME',\n    'SERVER_PORT',\n    'SERVER_PROTOCOL',\n    'SERVER_SOFTWARE',\n    'UNIQUE_ID',\n    'HTTPS'\n  ];\n\n  /* HH_IGNORE_ERROR[2050] using global variable */\n  $server = dict($_SERVER);\n  \n  $ouput = request_output();\n\n  foreach($names as $name) {\n    await $output->writeAsync(\n      Str\\format(\"%s = %s\\n\", $name, $server[$name] ?? '<unset>')\n    );\n  }\n\n  // Additional HTTP headers\n  foreach($server as $k => $v) {\n     if ($k is string && Str\\starts_with($k, 'HTTP_')) {\n        await $output->writeAsync(\n          Str\\format(\"%s = %s\\n\", $k, $v as string)\n        );\n     }\n  }\n\n  // flush output\n  await $output->flushAsync();\n\n  exit(0);\n}\n"
  },
  {
    "path": "examples/hack/greeter.hh",
    "content": "#!/usr/bin/hhvm\n<?hh // strict\n\nuse namespace HH\\Lib\\Str;\nuse namespace HH\\Lib\\Experimental\\IO;\n\n<<__EntryPoint>>\nasync function greeter(): Awaitable<noreturn> {\n  // For each line FOO received on STDIN, respond with \"Hello FOO!\".\n  $input = IO\\request_input();\n  $output = IO\\request_output();\n  while(!$input->isEndOfFile()) {\n    await $ouput->writeAsync(\n      Str\\format(\"Hello %s!\\n\", await $input->readLineAsync())\n    );\n  }\n    \n  // flush output\n  await $output->flushAsync();\n  \n  exit(0);\n}\n"
  },
  {
    "path": "examples/haskell/README.md",
    "content": "## Haskell examples\n\n### Count\n\nStart the server with\n\n```\n$ websocketd --port=8080 --devconsole --passenv PATH ./count.hs\n```\n\nThe passing of `PATH` was required for me because a typical Haskell installation of `runhaskell` does not go into `/usr/bin` but more like `/usr/local/bin`.\n\n### Greeter\n\nThe greeter server waits for a line of text to be sent, then sends back a greeting in response, and continues to wait for more lines to come.\n\n```\n$ websocketd --port=8080 --devconsole --passenv PATH ./greeter.hs\n```\n"
  },
  {
    "path": "examples/haskell/count.hs",
    "content": "#!/usr/bin/env runhaskell\n\nimport Control.Monad (forM_)\nimport Control.Concurrent (threadDelay)\nimport System.IO (hFlush, stdout)\n\n-- | Count from 1 to 10 with a sleep\nmain :: IO ()\nmain = forM_ [1 :: Int .. 10] $ \\count -> do\n  print count\n  hFlush stdout\n  threadDelay 500000\n"
  },
  {
    "path": "examples/haskell/greeter.hs",
    "content": "#!/usr/bin/env runhaskell\n\nimport Control.Monad (unless)\nimport System.IO (hFlush, stdout, stdin, hIsEOF)\n\n-- | For each line FOO received on STDIN, respond with \"Hello FOO!\".\nmain :: IO ()\nmain = do\n  eof <- hIsEOF stdin\n  unless eof $ do\n    line <- getLine\n    putStrLn $ \"Hello \" ++ line ++ \"!\"\n    hFlush stdout\n    main\n"
  },
  {
    "path": "examples/html/count.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <title>websocketd count example</title>\n    <style>\n      #count {\n        font: bold 150px arial;\n        margin: auto;\n        padding: 10px;\n        text-align: center;\n      }\n    </style>\n  </head>\n  <body>\n\n    <div id=\"count\"></div>\n\n    <script>\n      var ws = new WebSocket('ws://' + (location.host ? location.host : \"localhost:8080\") + \"/\");\n      ws.onopen = function() {\n        document.body.style.backgroundColor = '#cfc';\n      };\n      ws.onclose = function() {\n        document.body.style.backgroundColor = null;\n      };\n      ws.onmessage = function(event) {\n        document.getElementById('count').textContent = event.data;\n      };\n    </script>\n\n  </body>\n</html>\n"
  },
  {
    "path": "examples/java/Count/Count.java",
    "content": "public class Count {\n    public static void main(String[] args) {\n        for(int i = 1; i <= 10; i++) {\n            System.out.println(i);\n            try {\n                Thread.sleep(1000);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n}"
  },
  {
    "path": "examples/java/Count/count.sh",
    "content": "#!/bin/sh\njavac Count.java\njava Count"
  },
  {
    "path": "examples/java/Echo/Echo.java",
    "content": "import java.io.*;\npublic class Echo {\n    public static void main(String[] args) {\n        while(true) {\n            try {\n                BufferedReader in = new BufferedReader(new InputStreamReader(System.in));\n                String message = in.readLine();\n                System.out.println(message);\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n}"
  },
  {
    "path": "examples/java/Echo/echo.sh",
    "content": "#!/bin/sh\njavac Echo.java\njava Echo"
  },
  {
    "path": "examples/java/README.md",
    "content": "## Running the examples on Mac\n\n1. [download and install](https://github.com/joewalnes/websocketd/wiki/Download-and-install) websocketd and add it to your PATH\n2. Echo Server: Run `websocketd --port=8080 --devconsole ./echo.sh` and then go to http://localhost:8080 to interact with it\n3. Count Server: Run `websocketd --port=8080 ./count.sh` to start the server, then go to [examples/html](https://github.com/joewalnes/websocketd/tree/master/examples/html) folder and double click **count.html** to open in a browser"
  },
  {
    "path": "examples/lua/README.md",
    "content": "The examples demonstrate the use of websocketd with lua. There are two examples in the directory both very basic.\n\n1. Greeter.lua simply echos back any input made from the client\n2. json_ws.lua echos back any input from the client *after* converting it into a json string\n\nIt is pretty simple to extend these examples into full fledged applications. All you need is an stdin input loop\n\n```\nlocal input = io.stdin:read()\n\nwhile input do\n-- do anything here\n\n-- update the input\ninput = io.stdin:read()\n\nend\n\n\n```\n\nany thing you `print` goes out to the websocket client\n\nLibraries and third party modules can be used by the standard `require` statement in lua.\n\n## Running the examples\n\n\n\n##### 1. Download\n\n[Install](https://github.com/joewalnes/websocketd/wiki/Download-and-install) websocketd and add it to your `PATH`.\n\n##### 2. Start a server: greeter\n\nRun `websocketd --port=8080 --devconsole lua ./greeter.lua` and then go to `http://localhost:8080` to interact with it\n\n##### 3. Start a server: json_ws\n\nRun `websocketd --port=8080 --devconsole  lua  ./json_ws.lua` and then go to `http://localhost:8080` to interact with it\n\nIf you are using luajit instead of lua you may run the examples like this\n(this assumes that you've got luajit in your path)\n\n`websocketd --port=8080 --devconsole luajit ./json_ws.lua` and then go to `http://localhost:8080`"
  },
  {
    "path": "examples/lua/greeter.lua",
    "content": "local input = io.stdin:read()\nwhile input do\n   print(input)\n   io.stdout:flush()\n   input = io.stdin:read()\nend\n\n"
  },
  {
    "path": "examples/lua/json.lua",
    "content": "--\n-- json.lua\n--\n-- Copyright (c) 2015 rxi\n--\n-- This library is free software; you can redistribute it and/or modify it\n-- under the terms of the MIT license. See LICENSE for details.\n--\n\nlocal json = { _version = \"0.1.0\" }\n\n-------------------------------------------------------------------------------\n-- Encode\n-------------------------------------------------------------------------------\n\nlocal encode\n\nlocal escape_char_map = {\n  [ \"\\\\\" ] = \"\\\\\\\\\",\n  [ \"\\\"\" ] = \"\\\\\\\"\",\n  [ \"\\b\" ] = \"\\\\b\",\n  [ \"\\f\" ] = \"\\\\f\",\n  [ \"\\n\" ] = \"\\\\n\",\n  [ \"\\r\" ] = \"\\\\r\",\n  [ \"\\t\" ] = \"\\\\t\",\n}\n\nlocal escape_char_map_inv = { [ \"\\\\/\" ] = \"/\" }\nfor k, v in pairs(escape_char_map) do\n  escape_char_map_inv[v] = k\nend\n\n\nlocal function escape_char(c)\n  return escape_char_map[c] or string.format(\"\\\\u%04x\", c:byte())\nend\n\n\nlocal function encode_nil(val)\n  return \"null\"\nend \n\n\nlocal function encode_table(val, stack)\n  local res = {}\n  stack = stack or {}\n\n  -- Circular reference?\n  if stack[val] then error(\"circular reference\") end\n\n  stack[val] = true\n\n  if val[1] ~= nil or next(val) == nil then\n    -- Treat as array -- check keys are valid and it is not sparse\n    local n = 0\n    for k in pairs(val) do\n      if type(k) ~= \"number\" then\n        error(\"invalid table: mixed or invalid key types\")\n      end\n      n = n + 1\n    end\n    if n ~= #val then\n      error(\"invalid table: sparse array\")\n    end\n    -- Encode\n    for i, v in ipairs(val) do\n      table.insert(res, encode(v, stack))\n    end\n    stack[val] = nil\n    return \"[\" .. table.concat(res, \",\") .. \"]\"\n\n  else\n    -- Treat as an object\n    for k, v in pairs(val) do\n      if type(k) ~= \"string\" then\n        error(\"invalid table: mixed or invalid key types\")\n      end\n      table.insert(res, encode(k, stack) .. \":\" .. encode(v, stack))\n    end\n    stack[val] = nil\n    return \"{\" .. table.concat(res, \",\") .. \"}\"\n  end\nend\n\n\nlocal function encode_string(val)\n  return '\"' .. val:gsub('[%z\\1-\\31\\\\\"]', escape_char) .. '\"'\nend\n\n\nlocal function encode_number(val)\n  -- Check for NaN, -inf and inf\n  if val ~= val or val <= -math.huge or val >= math.huge then\n    error(\"unexpected number value '\" .. tostring(val) .. \"'\")\n  end\n  return string.format(\"%.14g\", val)\nend\n\n\nlocal type_func_map = {\n  [ \"nil\"     ] = encode_nil,\n  [ \"table\"   ] = encode_table,\n  [ \"string\"  ] = encode_string,\n  [ \"number\"  ] = encode_number,\n  [ \"boolean\" ] = tostring,\n}\n\n\nencode = function(val, stack)\n  local t = type(val)\n  local f = type_func_map[t]\n  if f then\n    return f(val, stack)\n  end\n  error(\"unexpected type '\" .. t .. \"'\")\nend\n\n\nfunction json.encode(val)\n  return ( encode(val) )\nend\n\n\n-------------------------------------------------------------------------------\n-- Decode\n-------------------------------------------------------------------------------\n\nlocal parse\n\nlocal function create_set(...) \n  local res = {}\n  for i = 1, select(\"#\", ...) do\n    res[ select(i, ...) ] = true\n  end\n  return res\nend\n\nlocal space_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\")\nlocal delim_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\", \"]\", \"}\", \",\")\nlocal escape_chars  = create_set(\"\\\\\", \"/\", '\"', \"b\", \"f\", \"n\", \"r\", \"t\", \"u\")\nlocal literals      = create_set(\"true\", \"false\", \"null\")\n\nlocal literal_map = {\n  [ \"true\"  ] = true,\n  [ \"false\" ] = false,\n  [ \"null\"  ] = nil,\n}\n\n\nlocal function next_char(str, idx, set, negate)\n  for i = idx, #str do\n    if set[str:sub(i, i)] ~= negate then\n      return i\n    end\n  end\n  return #str + 1\nend\n\n\nlocal function decode_error(str, idx, msg)\n  local line_count = 1\n  local col_count = 1\n  for i = 1, idx - 1 do\n    col_count = col_count + 1\n    if str:sub(i, i) == \"\\n\" then\n      line_count = line_count + 1\n      col_count = 1\n    end\n  end\n  error( string.format(\"%s at line %d col %d\", msg, line_count, col_count) )\nend\n\n\nlocal function codepoint_to_utf8(n)\n  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa\n  local f = math.floor\n  if n <= 0x7f then\n    return string.char(n)\n  elseif n <= 0x7ff then\n    return string.char(f(n / 64) + 192, n % 64 + 128)\n  elseif n <= 0xffff then\n    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)\n  elseif n <= 0x10ffff then\n    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,\n                       f(n % 4096 / 64) + 128, n % 64 + 128)\n  end\n  error( string.format(\"invalid unicode codepoint '%x'\", n) )\nend\n\n\nlocal function parse_unicode_escape(s)\n  local n1 = tonumber( s:sub(3, 6),  16 )\n  local n2 = tonumber( s:sub(9, 12), 16 )\n  -- Surrogate pair?\n  if n2 then\n    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)\n  else\n    return codepoint_to_utf8(n1)\n  end\nend\n\n\nlocal function parse_string(str, i)\n  local has_unicode_escape = false\n  local has_surrogate_escape = false\n  local has_escape = false\n  local last\n  for j = i + 1, #str do\n    local x = str:byte(j)\n\n    if x < 32 then\n      decode_error(str, j, \"control character in string\")\n    end\n\n    if last == 92 then -- \"\\\\\" (escape char)\n      if x == 117 then -- \"u\" (unicode escape sequence)\n        local hex = str:sub(j + 1, j + 5)\n        if not hex:find(\"%x%x%x%x\") then\n          decode_error(str, j, \"invalid unicode escape in string\")\n        end\n        if hex:find(\"^[dD][89aAbB]\") then\n          has_surrogate_escape = true\n        else\n          has_unicode_escape = true\n        end\n      else\n        local c = string.char(x)\n        if not escape_chars[c] then\n          decode_error(str, j, \"invalid escape char '\" .. c .. \"' in string\")\n        end\n        has_escape = true\n      end\n      last = nil\n\n    elseif x == 34 then -- '\"' (end of string)\n      local s = str:sub(i + 1, j - 1)\n      if has_surrogate_escape then \n        s = s:gsub(\"\\\\u[dD][89aAbB]..\\\\u....\", parse_unicode_escape)\n      end\n      if has_unicode_escape then \n        s = s:gsub(\"\\\\u....\", parse_unicode_escape)\n      end\n      if has_escape then\n        s = s:gsub(\"\\\\.\", escape_char_map_inv)\n      end\n      return s, j + 1\n    \n    else\n      last = x\n    end\n  end\n  decode_error(str, i, \"expected closing quote for string\")\nend\n\n\nlocal function parse_number(str, i)\n  local x = next_char(str, i, delim_chars)\n  local s = str:sub(i, x - 1)\n  local n = tonumber(s)\n  if not n then\n    decode_error(str, i, \"invalid number '\" .. s .. \"'\")\n  end\n  return n, x\nend\n\n\nlocal function parse_literal(str, i)\n  local x = next_char(str, i, delim_chars)\n  local word = str:sub(i, x - 1)\n  if not literals[word] then\n    decode_error(str, i, \"invalid literal '\" .. word .. \"'\")\n  end\n  return literal_map[word], x\nend\n\n\nlocal function parse_array(str, i)\n  local res = {}\n  local n = 1\n  i = i + 1\n  while 1 do\n    local x\n    i = next_char(str, i, space_chars, true)\n    -- Empty / end of array?\n    if str:sub(i, i) == \"]\" then \n      i = i + 1\n      break\n    end\n    -- Read token\n    x, i = parse(str, i)\n    res[n] = x\n    n = n + 1\n    -- Next token \n    i = next_char(str, i, space_chars, true)\n    local chr = str:sub(i, i)\n    i = i + 1\n    if chr == \"]\" then break end\n    if chr ~= \",\" then decode_error(str, i, \"expected ']' or ','\") end\n  end\n  return res, i\nend\n\n\nlocal function parse_object(str, i)\n  local res = {}\n  i = i + 1\n  while 1 do\n    local key, val\n    i = next_char(str, i, space_chars, true)\n    -- Empty / end of object?\n    if str:sub(i, i) == \"}\" then \n      i = i + 1\n      break\n    end\n    -- Read key\n    if str:sub(i, i) ~= '\"' then\n      decode_error(str, i, \"expected string for key\")\n    end\n    key, i = parse(str, i)\n    -- Read ':' delimiter\n    i = next_char(str, i, space_chars, true)\n    if str:sub(i, i) ~= \":\" then\n      decode_error(str, i, \"expected ':' after key\")\n    end\n    i = next_char(str, i + 1, space_chars, true)\n    -- Read value\n    val, i = parse(str, i)\n    -- Set\n    res[key] = val\n    -- Next token\n    i = next_char(str, i, space_chars, true)\n    local chr = str:sub(i, i)\n    i = i + 1\n    if chr == \"}\" then break end\n    if chr ~= \",\" then decode_error(str, i, \"expected '}' or ','\") end\n  end\n  return res, i\nend\n\n\nlocal char_func_map = {\n  [ '\"' ] = parse_string,\n  [ \"0\" ] = parse_number,\n  [ \"1\" ] = parse_number,\n  [ \"2\" ] = parse_number,\n  [ \"3\" ] = parse_number,\n  [ \"4\" ] = parse_number,\n  [ \"5\" ] = parse_number,\n  [ \"6\" ] = parse_number,\n  [ \"7\" ] = parse_number,\n  [ \"8\" ] = parse_number,\n  [ \"9\" ] = parse_number,\n  [ \"-\" ] = parse_number,\n  [ \"t\" ] = parse_literal,\n  [ \"f\" ] = parse_literal,\n  [ \"n\" ] = parse_literal,\n  [ \"[\" ] = parse_array,\n  [ \"{\" ] = parse_object,\n}\n\n\nparse = function(str, idx)\n  local chr = str:sub(idx, idx)\n  local f = char_func_map[chr]\n  if f then\n    return f(str, idx)\n  end\n  decode_error(str, idx, \"unexpected character '\" .. chr .. \"'\")\nend\n\n\nfunction json.decode(str)\n  if type(str) ~= \"string\" then\n    error(\"expected argument of type string, got \" .. type(str))\n  end\n  return ( parse(str, next_char(str, 1, space_chars, true)) )\nend\n\n\nreturn json\n"
  },
  {
    "path": "examples/lua/json_ws.lua",
    "content": "local input = io.stdin:read()\nlocal json = require(\"json\")\nwhile input do\n   print(json.encode({res=\"json\",mess=input}))\n   io.stdout:flush()\n   input = io.stdin:read()\nend\n"
  },
  {
    "path": "examples/nodejs/README.md",
    "content": "## Running the examples on Mac\n\n##### 1. Download\n\n[Install](https://github.com/joewalnes/websocketd/wiki/Download-and-install) websocketd and add it to your `PATH`.\n\n##### 2. Start a server: getter\n\nRun `websocketd --port=8080 --devconsole node greeter.js` and then go to `http://localhost:8080` to interact with it\n\n##### 3. Start a server: counter\n\nRun `websocketd --port=8080 node count.js` to start the server, then go to [examples/html](https://github.com/joewalnes/websocketd/tree/master/examples/html) directory and double click **count.html** to open in a browser\n"
  },
  {
    "path": "examples/nodejs/count.js",
    "content": "(function(){\n\tvar counter = 0;\n\tvar echo = function(){\n\t\tif (counter === 10){\n\t\t\treturn;\n\t\t}\n\n\t\tsetTimeout(function(){\n\t\t\tcounter++;\n\t\t\techo();\n\t\t\tprocess.stdout.write(counter.toString() + \"\\n\");\n\t\t}, 500);\n\t}\n\n\techo();\n})();\n"
  },
  {
    "path": "examples/nodejs/greeter.js",
    "content": "// from node.js sample\n// https://nodejs.org/api/process.html#process_process_stdin\nprocess.stdin.setEncoding('utf8');\n\nprocess.stdin.on('readable', function() {\n  var chunk = process.stdin.read();\n  if (chunk !== null) {\n    process.stdout.write('data: ' + chunk);\n  }\n});"
  },
  {
    "path": "examples/perl/README.txt",
    "content": "This examples directory shows some examples written in Perl.\n\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/perl/count.pl",
    "content": "#!/usr/bin/perl\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\nuse strict;\n\n# Autoflush output\nuse IO::Handle;\nSTDOUT->autoflush(1);\n\nuse Time::HiRes qw(sleep);\n\n# Simple example script that counts to 10 at ~2Hz, then stops.\nfor my $count (1 .. 10) {\n\tprint \"$count\\n\";\n\tsleep 0.5;\n}\n"
  },
  {
    "path": "examples/perl/dump-env.pl",
    "content": "#!/usr/bin/perl\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\nuse strict;\n\n# Autoflush output\nuse IO::Handle;\nSTDOUT->autoflush(1);\n\n# Standard CGI(ish) environment variables, as defined in\n# http://tools.ietf.org/html/rfc3875\nmy @names = qw(\n  AUTH_TYPE\n  CONTENT_LENGTH\n  CONTENT_TYPE\n  GATEWAY_INTERFACE\n  PATH_INFO\n  PATH_TRANSLATED\n  QUERY_STRING\n  REMOTE_ADDR\n  REMOTE_HOST\n  REMOTE_IDENT\n  REMOTE_PORT\n  REMOTE_USER\n  REQUEST_METHOD\n  REQUEST_URI\n  SCRIPT_NAME\n  SERVER_NAME\n  SERVER_PORT\n  SERVER_PROTOCOL\n  SERVER_SOFTWARE\n  UNIQUE_ID\n  HTTPS\n);\n\nfor my $name (@names) {\n\tmy $value = $ENV{$name} || '<unset>';\n\tprint \"$name=$value\\n\";\n}\n\n# Additional HTTP headers\nfor my $name (keys(%ENV)) {\n\tif ($name =~ /^HTTP_/) {\n\t\tmy $value = $ENV{$name};\n\t\tprint \"$name=$value\\n\";\n\t}\n}\n"
  },
  {
    "path": "examples/perl/greeter.pl",
    "content": "#!/usr/bin/perl\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\nuse strict;\n\n# Autoflush output\nuse IO::Handle;\nSTDOUT->autoflush(1);\n\n# For each line FOO received on STDIN, respond with \"Hello FOO!\".\nwhile (<>) {\n  chomp; # remove \\n\n  print \"Hello $_!\\n\";\n}\n"
  },
  {
    "path": "examples/php/README.txt",
    "content": "This examples directory shows some examples written in PHP.\n\nThis relies on the CLI verson of PHP being installed and in the path.\nSee http://www.php.net/manual/en/features.commandline.introduction.php\n\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/php/count.php",
    "content": "#!/usr/bin/php\n<?php\n\n// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Simple example script that counts to 10 at ~2Hz, then stops.\n\nfor ($count = 1; $count <= 10; $count++) {\n\techo $count . \"\\n\";\n\tusleep(500000);\n}\n\n?>"
  },
  {
    "path": "examples/php/dump-env.php",
    "content": "#!/usr/bin/php\n<?php\n\n// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Standard CGI(ish) environment variables, as defined in\n// http://tools.ietf.org/html/rfc3875\n$names = array(\n  'AUTH_TYPE',\n  'CONTENT_LENGTH',\n  'CONTENT_TYPE',\n  'GATEWAY_INTERFACE',\n  'PATH_INFO',\n  'PATH_TRANSLATED',\n  'QUERY_STRING',\n  'REMOTE_ADDR',\n  'REMOTE_HOST',\n  'REMOTE_IDENT',\n  'REMOTE_PORT',\n  'REMOTE_USER',\n  'REQUEST_METHOD',\n  'REQUEST_URI',\n  'SCRIPT_NAME',\n  'SERVER_NAME',\n  'SERVER_PORT',\n  'SERVER_PROTOCOL',\n  'SERVER_SOFTWARE',\n  'UNIQUE_ID',\n  'HTTPS'\n);\n\nforeach ($names as $name) {\n  $value = isset($_SERVER[$name]) ? $_SERVER[$name] : '<unset>';\n\techo $name . '=' . $value . \"\\n\";\n}\n\n// Additional HTTP headers\nforeach ($_SERVER as $name => $value) {\n  if (strpos($name, 'HTTP_') === 0) {\n    echo $name . '=' . $value . \"\\n\";\n  }\n}\n"
  },
  {
    "path": "examples/php/greeter.php",
    "content": "#!/usr/bin/php\n<?php\n\n// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// For each line FOO received on STDIN, respond with \"Hello FOO!\".\n$stdin = fopen('php://stdin', 'r');\nwhile ($line = fgets($stdin)) {\n\techo 'Hello ' . trim($line) . \"!\\n\";\n}\n\n?>"
  },
  {
    "path": "examples/python/README.txt",
    "content": "This examples directory shows some examples written in Python.\n\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/python/count.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\nfrom sys import stdout\nfrom time import sleep\n\n# Simple example script that counts to 10 at ~2Hz, then stops.\nfor count in range(0, 10):\n  print(count + 1)\n  stdout.flush() # Remember to flush\n  sleep(0.5)\n"
  },
  {
    "path": "examples/python/dump-env.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\nimport os\nfrom sys import stdout\n\n# Standard CGI(ish) environment variables, as defined in\n# http://tools.ietf.org/html/rfc3875\nvar_names = [\n  'AUTH_TYPE',\n  'CONTENT_LENGTH',\n  'CONTENT_TYPE',\n  'GATEWAY_INTERFACE',\n  'PATH_INFO',\n  'PATH_TRANSLATED',\n  'QUERY_STRING',\n  'REMOTE_ADDR',\n  'REMOTE_HOST',\n  'REMOTE_IDENT',\n  'REMOTE_PORT',\n  'REMOTE_USER',\n  'REQUEST_METHOD',\n  'REQUEST_URI',\n  'SCRIPT_NAME',\n  'SERVER_NAME',\n  'SERVER_PORT',\n  'SERVER_PROTOCOL',\n  'SERVER_SOFTWARE',\n  'UNIQUE_ID',\n  'HTTPS'\n]\nfor var_name in var_names:\n  print('%s=%s' % (var_name, os.environ.get(var_name, '<unset>')))\n  stdout.flush() # Remember to flush\n\n# Additional HTTP headers\nfor var_name in os.environ:\n  if var_name.startswith('HTTP_'):\n    print('%s=%s' % (var_name, os.environ[var_name]))\n    stdout.flush() # Remember to flush\n"
  },
  {
    "path": "examples/python/greeter.py",
    "content": "#!/usr/bin/python\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\nfrom sys import stdin, stdout\n\n# For each line FOO received on STDIN, respond with \"Hello FOO!\".\nwhile True:\n  line = stdin.readline().strip()\n  print('Hello %s!' % line)\n  stdout.flush() # Remember to flush\n"
  },
  {
    "path": "examples/qjs/request-reply.js",
    "content": "#!/usr/bin/env -S qjs --module\nimport * as std from \"std\";\n\nlet line;\nwhile ((line = std.in.getline()) != null) {\n  console.log(\"RCVD: \" + line)\n  std.out.flush();\n}\n"
  },
  {
    "path": "examples/ruby/README.txt",
    "content": "This examples directory shows some examples written in Ruby.\n\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/ruby/count.rb",
    "content": "#!/usr/bin/ruby\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Autoflush output\nSTDOUT.sync = true\n\n# Simple example script that counts to 10 at ~2Hz, then stops.\n(1..10).each do |count| \n\tputs count\n\tsleep(0.5)\nend\n"
  },
  {
    "path": "examples/ruby/dump-env.rb",
    "content": "#!/usr/bin/ruby\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Autoflush output\nSTDOUT.sync = true\n\n# Standard CGI(ish) environment variables, as defined in\n# http://tools.ietf.org/html/rfc3875\nnames = [\n  'AUTH_TYPE',\n  'CONTENT_LENGTH',\n  'CONTENT_TYPE',\n  'GATEWAY_INTERFACE',\n  'PATH_INFO',\n  'PATH_TRANSLATED',\n  'QUERY_STRING',\n  'REMOTE_ADDR',\n  'REMOTE_HOST',\n  'REMOTE_IDENT',\n  'REMOTE_PORT',\n  'REMOTE_USER',\n  'REQUEST_METHOD',\n  'REQUEST_URI',\n  'SCRIPT_NAME',\n  'SERVER_NAME',\n  'SERVER_PORT',\n  'SERVER_PROTOCOL',\n  'SERVER_SOFTWARE',\n  'UNIQUE_ID',\n  'HTTPS',\n]\n\nnames.each do |name|\n  value = ENV[name] || '<unset>'\n  puts \"#{name}=#{value}\"\nend\n\n# Additional HTTP headers\nENV.each do |name,value|\n  puts \"#{name}=#{value}\" if name.start_with?('HTTP_')\nend\n"
  },
  {
    "path": "examples/ruby/greeter.rb",
    "content": "#!/usr/bin/ruby\n\n# Copyright 2013 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Autoflush output\nSTDOUT.sync = true\n\n# For each line FOO received on STDIN, respond with \"Hello FOO!\".\nwhile 1\n  line = STDIN.readline.strip\n  puts \"Hello #{line}!\"\nend\n"
  },
  {
    "path": "examples/rust/README.txt",
    "content": "This examples directory shows some examples written in Rust.\n\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/rust/count.rs",
    "content": "use std::io::{self, Write};\nuse std::{thread, time};\n\n// Simple example script that counts to 10 at ~2Hz, then stops.\nfn main() {\n  for i in 1..11 {\n    println!(\"{}\", i);\n    io::stdout().flush().ok().expect(\"Could not flush stdout\");\n    thread::sleep(time::Duration::from_millis(500));\n  }\n}\n"
  },
  {
    "path": "examples/rust/dump-env.rs",
    "content": "// Standard CGI(ish) environment variables, as defined in\n// http://tools.ietf.org/html/rfc3875\n\nuse std::env;\n\nconst NAMES: &'static [&'static str] = &[\n  \"AUTH_TYPE\",\n  \"CONTENT_LENGTH\",\n  \"CONTENT_TYPE\",\n  \"GATEWAY_INTERFACE\",\n  \"PATH_INFO\",\n  \"PATH_TRANSLATED\",\n  \"QUERY_STRING\",\n  \"REMOTE_ADDR\",\n  \"REMOTE_HOST\",\n  \"REMOTE_IDENT\",\n  \"REMOTE_PORT\",\n  \"REMOTE_USER\",\n  \"REQUEST_METHOD\",\n  \"REQUEST_URI\",\n  \"SCRIPT_NAME\",\n  \"SERVER_NAME\",\n  \"SERVER_PORT\",\n  \"SERVER_PROTOCOL\",\n  \"SERVER_SOFTWARE\",\n  \"UNIQUE_ID\",\n  \"HTTPS\",\n];\n\nfn main() {\n  for key in NAMES {\n    let value = env::var(key).unwrap_or(String::from(\"<unset>\"));\n    println!(\"{}={}\", key, value);\n  }\n  for (key, value) in env::vars() {\n    if key.starts_with(\"HTTP_\") {\n      println!(\"{}={}\", key, value);\n    }\n  }\n}\n"
  },
  {
    "path": "examples/rust/greeter.rs",
    "content": "use std::io::{self, Write};\n\n// For each line FOO received on STDIN, respond with \"Hello FOO!\".\nfn main() {\n  loop {\n    let mut msg = String::new();\n    io::stdin()\n      .read_line(&mut msg)\n      .expect(\"Failed to read line\");\n    let msg = msg.trim();\n    println!(\"Hello {}!\", msg);\n    io::stdout().flush().ok().expect(\"Could not flush stdout\");\n  }\n}\n"
  },
  {
    "path": "examples/swift/README.md",
    "content": "## Swift examples\n\n### Count\n\nRun the following line and open \"html/count.html\" from the websocketd examples directory.\n\n```\n$ websocketd --port=8080 count.swift\n```\n\n### Greeter\n\nRun the following line and open \"http://localhost:8080\" in your browser to interact with the greeter server.\n\n```\n$ websocketd --port=8080 --devconsole greeter.swift \n```\n"
  },
  {
    "path": "examples/swift/count.swift",
    "content": "#!/usr/bin/env xcrun -sdk macosx swift\n\nimport AppKit\n\nfor index in 1...10 {\n  print(index)\n  \n  // Flush output\n  fflush(__stdoutp)\n  \n  NSThread.sleepForTimeInterval(0.5)\n}"
  },
  {
    "path": "examples/swift/greeter.swift",
    "content": "#!/usr/bin/env xcrun -sdk macosx swift\n\nimport Foundation\n\nwhile(true){\n  var stdin = NSFileHandle.fileHandleWithStandardInput().availableData\n  var line  = NSString(data: stdin, encoding: NSUTF8StringEncoding)!\n  var name  = line.stringByTrimmingCharactersInSet(NSCharacterSet.newlineCharacterSet())\n  print(\"Hello \\(name)!\")\n  fflush(__stdoutp)\n}\n"
  },
  {
    "path": "examples/windows-jscript/README.txt",
    "content": "This examples directory shows some examples written in JScript\r\nthat can be run using Windows Script Hosting.\r\n\r\nNote that each .js file, also requires a .cmd file to launch it.\r\nThe WebSocket should connect to ws://..../[example].cmd.\r\n\r\nhttp://en.wikipedia.org/wiki/Windows_Script_Host\r\n\r\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/windows-jscript/count.cmd",
    "content": "@echo off\r\ncscript /nologo %0\\..\\count.js\r\n"
  },
  {
    "path": "examples/windows-jscript/count.js",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\r\n// All rights reserved.\r\n// Use of this source code is governed by a BSD-style\r\n// license that can be found in the LICENSE file.\r\n\r\n// Simple example script that counts to 10 at ~2Hz, then stops.\r\nfor (var i = 1; i <= 10; i++) {\r\n  WScript.echo(i);\r\n  WScript.sleep(500);\r\n}\r\n"
  },
  {
    "path": "examples/windows-jscript/dump-env.cmd",
    "content": "@echo off\r\ncscript /nologo %0\\..\\dump-env.js\r\n"
  },
  {
    "path": "examples/windows-jscript/dump-env.js",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\r\n// All rights reserved.\r\n// Use of this source code is governed by a BSD-style\r\n// license that can be found in the LICENSE file.\r\n\r\n\r\n// Standard CGI(ish) environment variables, as defined in\r\n// http://tools.ietf.org/html/rfc3875\r\nvar names = [\r\n  'AUTH_TYPE',\r\n  'CONTENT_LENGTH',\r\n  'CONTENT_TYPE',\r\n  'GATEWAY_INTERFACE',\r\n  'PATH_INFO',\r\n  'PATH_TRANSLATED',\r\n  'QUERY_STRING',\r\n  'REMOTE_ADDR',\r\n  'REMOTE_HOST',\r\n  'REMOTE_IDENT',\r\n  'REMOTE_PORT',\r\n  'REMOTE_USER',\r\n  'REQUEST_METHOD',\r\n  'REQUEST_URI',\r\n  'SCRIPT_NAME',\r\n  'SERVER_NAME',\r\n  'SERVER_PORT',\r\n  'SERVER_PROTOCOL',\r\n  'SERVER_SOFTWARE',\r\n  'UNIQUE_ID',\r\n  'HTTPS'\r\n];\r\n\r\nvar shell = WScript.CreateObject(\"WScript.Shell\");\r\nvar env = shell.Environment('PROCESS');\r\n\r\nfor (var i = 0; i < names.length; i++) {\r\n  var name = names[i];\r\n  var value = env(name) || '<unset>';\r\n  WScript.echo(name + '=' + value);\r\n}\r\n\r\nfor(var en = new Enumerator(env); !en.atEnd(); en.moveNext()) {\r\n  var item = en.item();\r\n  if (item.indexOf('HTTP_') == 0) {\r\n    WScript.Echo(item);\r\n  }\r\n}"
  },
  {
    "path": "examples/windows-jscript/greeter.cmd",
    "content": "@echo off\r\ncscript /nologo %0\\..\\greeter.js\r\n"
  },
  {
    "path": "examples/windows-jscript/greeter.js",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\r\n// All rights reserved.\r\n// Use of this source code is governed by a BSD-style\r\n// license that can be found in the LICENSE file.\r\n\r\n\r\n// For each line FOO received on STDIN, respond with \"Hello FOO!\".\r\nwhile (true) {\r\n  var input= WScript.stdIn.readLine();\r\n  WScript.echo('Hello ' + input+ '!');\r\n}\r\n"
  },
  {
    "path": "examples/windows-vbscript/README.txt",
    "content": "This examples directory shows some examples written in VBScript\r\nthat can be run using Windows Script Hosting.\r\n\r\nNote that each .vbs file, also requires a .cmd file to launch it.\r\nThe WebSocket should connect to ws://..../[example].cmd.\r\n\r\nhttp://en.wikipedia.org/wiki/Windows_Script_Host\r\n\r\nYou can also test the command files by running from the command line."
  },
  {
    "path": "examples/windows-vbscript/count.cmd",
    "content": "@echo off\r\ncscript /nologo %0\\..\\count.vbs\r\n"
  },
  {
    "path": "examples/windows-vbscript/count.vbs",
    "content": "' Copyright 2013 Joe Walnes and the websocketd team.\r\n' All rights reserved.\r\n' Use of this source code is governed by a BSD-style\r\n' license that can be found in the LICENSE file.\r\n\r\n\r\n' Simple example script that counts to 10 at ~2Hz, then stops.\r\nfor i = 1 to 10\r\n  WScript.echo i\r\n  WScript.sleep 500\r\nnext\r\n"
  },
  {
    "path": "examples/windows-vbscript/dump-env.cmd",
    "content": "@echo off\r\ncscript /nologo %0\\..\\dump-env.vbs\r\n"
  },
  {
    "path": "examples/windows-vbscript/dump-env.vbs",
    "content": "' Copyright 2013 Joe Walnes and the websocketd team.\r\n' All rights reserved.\r\n' Use of this source code is governed by a BSD-style\r\n' license that can be found in the LICENSE file.\r\n\r\n\r\n' Standard CGI(ish) environment variables, as defined in\r\n' http://tools.ietf.org/html/rfc3875\r\nnames = Array(_\r\n  \"AUTH_TYPE\", _\r\n  \"CONTENT_LENGTH\", _\r\n  \"CONTENT_TYPE\", _\r\n  \"GATEWAY_INTERFACE\", _\r\n  \"PATH_INFO\", _\r\n  \"PATH_TRANSLATED\", _\r\n  \"QUERY_STRING\", _\r\n  \"REMOTE_ADDR\", _\r\n  \"REMOTE_HOST\", _\r\n  \"REMOTE_IDENT\", _\r\n  \"REMOTE_PORT\", _\r\n  \"REMOTE_USER\", _\r\n  \"REQUEST_METHOD\", _\r\n  \"REQUEST_URI\", _\r\n  \"SCRIPT_NAME\", _\r\n  \"SERVER_NAME\", _\r\n  \"SERVER_PORT\", _\r\n  \"SERVER_PROTOCOL\", _\r\n  \"SERVER_SOFTWARE\", _\r\n  \"UNIQUE_ID\", _\r\n  \"HTTPS\"_\r\n)\r\n\r\nset shell = WScript.CreateObject(\"WScript.Shell\")\r\nset env = shell.Environment(\"PROCESS\")\r\n\r\nfor each name in names\r\n  value = env(name)\r\n  if value = \"\" then\r\n    value = \"<unset>\"\r\n  end if\r\n  WScript.echo name & \"=\" & value\r\nnext\r\n\r\nfor each item in env\r\n  if instr(1, item, \"HTTP_\", 1) = 1 then\r\n    WScript.Echo item\r\n  end if\r\nnext\r\n"
  },
  {
    "path": "examples/windows-vbscript/greeter.cmd",
    "content": "@echo off\r\ncscript /nologo %0\\..\\greeter.vbs\r\n"
  },
  {
    "path": "examples/windows-vbscript/greeter.vbs",
    "content": "' Copyright 2013 Joe Walnes and the websocketd team.\r\n' All rights reserved.\r\n' Use of this source code is governed by a BSD-style\r\n' license that can be found in the LICENSE file.\r\n\r\n\r\n' For each line FOO received on STDIN, respond with \"Hello FOO!\".\r\nwhile true\r\n  line = WScript.stdIn.readLine\r\n  WScript.echo \"Hello \" & line & \"!\"\r\nwend\r\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/joewalnes/websocketd\n\ngo 1.15\n\nrequire github.com/gorilla/websocket v1.4.0\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=\ngithub.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\n"
  },
  {
    "path": "help.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nconst (\n\thelp = `\n{{binary}} ({{version}})\n\n{{binary}} is a command line tool that will allow any executable program\nthat accepts input on stdin and produces output on stdout to be turned into\na WebSocket server.\n\nUsage:\n\n  Export a single executable program a WebSocket server:\n    {{binary}} [options] COMMAND [command args]\n\n  Or, export an entire directory of executables as WebSocket endpoints:\n    {{binary}} [options] --dir=SOMEDIR\n\nOptions:\n\n  --port=PORT                    HTTP port to listen on.\n\n  --address=ADDRESS              Address to bind to (multiple options allowed)\n                                 Use square brackets to specify IPv6 address.\n                                 Default: \"\" (all)\n\n  --sameorigin={true,false}      Restrict (HTTP 403) protocol upgrades if the\n                                 Origin header does not match to requested HTTP\n                                 Host. Default: false.\n\n  --origin=host[:port][,host[:port]...]\n                                 Restrict (HTTP 403) protocol upgrades if the\n                                 Origin header does not match to one of the host\n                                 and port combinations listed. If the port is not\n                                 specified, any port number will match.\n                                 Default: \"\" (allow any origin)\n\n  --ssl                          Listen for HTTPS socket instead of HTTP.\n  --sslcert=FILE                 All three options must be used or all of\n  --sslkey=FILE                  them should be omitted.\n\n  --redirport=PORT               Open alternative port and redirect HTTP traffic\n                                 from it to canonical address (mostly useful\n                                 for HTTPS-only configurations to redirect HTTP\n                                 traffic)\n\n  --passenv VAR[,VAR...]         Lists environment variables allowed to be\n                                 passed to executed scripts. Does not work for\n                                 Windows since all the variables are kept there.\n\n  --binary={true,false}          Switches communication to binary, process reads\n                                 send to browser as blobs and all reads from the\n                                 browser are immediately flushed to the process.\n                                 Default: false\n\n  --reverselookup={true,false}   Perform DNS reverse lookups on remote clients.\n                                 Default: false\n\n  --dir=DIR                      Allow all scripts in the local directory\n                                 to be accessed as WebSockets. If using this,\n                                 option, then the standard program and args\n                                 options should not be specified.\n\n  --staticdir=DIR                Serve static files in this directory over HTTP.\n\n  --cgidir=DIR                   Serve CGI scripts in this directory over HTTP.\n\n  --maxforks=N                   Limit number of processes that websocketd is\n                                 able to execute with WS and CGI handlers.\n                                 When maxforks reached the server will be\n                                 rejecting requests that require executing\n                                 another process (unlimited when 0 or negative).\n                                 Default: 0\n\n  --closems=milliseconds         Specifies additional time process needs to gracefully\n                                 finish before websocketd will send termination signals\n                                 to it. Default: 0 (signals sent after 100ms, 250ms,\n                                 and 500ms of waiting)\n\n  --header=\"...\"                 Set custom HTTP header to each answer. For\n                                 example: --header=\"Server: someserver/0.0.1\"\n\n  --header-ws=\"....\"             Same as --header, just applies to only those\n                                 responses that indicate upgrade of TCP connection\n                                 to a WebSockets protocol.\n\n  --header-http=\"....\"           Same as --header, just applies to only to plain\n                                 HTTP responses that do not indicate WebSockets\n                                 upgrade\n\n\n  --help                         Print help and exit.\n\n  --version                      Print version and exit.\n\n  --license                      Print license and exit.\n\n  --devconsole                   Enable interactive development console.\n                                 This enables you to access the websocketd\n                                 server with a web-browser and use a\n                                 user interface to quickly test WebSocket\n                                 endpoints. For example, to test an\n                                 endpoint at ws://[host]/foo, you can\n                                 visit http://[host]/foo in your browser.\n                                 This flag cannot be used in conjunction\n                                 with --staticdir or --cgidir.\n\n  --loglevel=LEVEL               Log level to use (default access).\n                                 From most to least verbose:\n                                 debug, trace, access, info, error, fatal\n\nFull documentation at http://websocketd.com/\n\nCopyright 2013 Joe Walnes and the websocketd team. All rights reserved.\nBSD license: Run '{{binary}} --license' for details.\n`\n\tshort = `\nUsage:\n\n  Export a single executable program a WebSocket server:\n    {{binary}} [options] COMMAND [command args]\n\n  Or, export an entire directory of executables as WebSocket endpoints:\n    {{binary}} [options] --dir=SOMEDIR\n\n  Or, show extended help message using:\n    {{binary}} --help\n`\n)\n\nfunc get_help_message(content string) string {\n\tmsg := strings.Trim(content, \" \\n\")\n\tmsg = strings.Replace(msg, \"{{binary}}\", HelpProcessName(), -1)\n\treturn strings.Replace(msg, \"{{version}}\", Version(), -1)\n}\n\nfunc HelpProcessName() string {\n\tbinary := os.Args[0]\n\tif strings.Contains(binary, \"/go-build\") { // this was run using \"go run\", let's use something appropriate\n\t\tbinary = \"websocketd\"\n\t} else {\n\t\tbinary = filepath.Base(binary)\n\t}\n\treturn binary\n}\n\nfunc PrintHelp() {\n\tfmt.Fprintf(os.Stderr, \"%s\\n\", get_help_message(help))\n}\n\nfunc ShortHelp() {\n\t// Shown after some error\n\tfmt.Fprintf(os.Stderr, \"\\n%s\\n\", get_help_message(short))\n}\n"
  },
  {
    "path": "libwebsocketd/config.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"time\"\n)\n\ntype Config struct {\n\t// base initiaization fields\n\tStartupTime    time.Time // Server startup time (used for dev console caching).\n\tCommandName    string    // Command to execute.\n\tCommandArgs    []string  // Additional args to pass to command.\n\tServerSoftware string    // Value to pass to SERVER_SOFTWARE environment variable (e.g. websocketd/1.2.3).\n\tCloseMs        uint      // Milliseconds to start sending signals\n\n\tHandshakeTimeout time.Duration // time to finish handshake (default 1500ms)\n\n\t// settings\n\tBinary         bool     // Use binary communication (send data in chunks they are read from process)\n\tReverseLookup  bool     // Perform reverse DNS lookups on hostnames (useful, but slower).\n\tSsl            bool     // websocketd works with --ssl which means TLS is in use\n\tScriptDir      string   // Base directory for websocket scripts.\n\tUsingScriptDir bool     // Are we running with a script dir.\n\tStaticDir      string   // If set, static files will be served from this dir over HTTP.\n\tCgiDir         string   // If set, CGI scripts will be served from this dir over HTTP.\n\tDevConsole     bool     // Enable dev console. This disables StaticDir and CgiDir.\n\tAllowOrigins   []string // List of allowed origin addresses for websocket upgrade.\n\tSameOrigin     bool     // If set, requires websocket upgrades to be performed from same origin only.\n\tHeaders        []string\n\tHeadersWs      []string\n\tHeadersHTTP    []string\n\n\t// created environment\n\tEnv       []string // Additional environment variables to pass to process (\"key=value\").\n\tParentEnv []string // Variables kept from os.Environ() before sanitizing it for subprocess.\n}\n"
  },
  {
    "path": "libwebsocketd/console.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\n// Although this isn't particularly elegant, it's the simplest\n// way to embed the console content into the binary.\n\n// Note that the console is served by a single HTML file containing\n// all CSS and JS inline.\n// We can get by without jQuery or Bootstrap for this one ;).\n\nconst (\n\tdefaultConsoleContent = `\n\n<!--\nwebsocketd console\n\nFull documentation at http://websocketd.com/\n\n{{license}}\n-->\n\n<!DOCTYPE html>\n<meta charset=\"utf8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>websocketd console</title>\n\n<style>\n\t.template {\n\t\tdisplay: none !important;\n\t}\n\tbody, input {\n\t\tfont-family: dejavu sans mono, Menlo, Monaco, Consolas, Lucida Console, tahoma, arial;\n\t\tfont-size: 13px;\n\t}\n\tbody {\n\t\tmargin: 0;\n\t}\n\t.header {\n\t\tbackground-color: #efefef;\n\t\tpadding: 2px;\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t\tright: 0;\n\t\theight: 32px;\n\t}\n\t.header button {\n\t\tfont-size: 19px;\n\t\twidth: 30px;\n\t\tmargin: 2px 2px 0 2px;\n\t\tpadding: 0;\n\t\tfloat: left;\n\t}\n\t.header .url-holder {\n\t\tposition: absolute;\n\t\tleft: 38px;\n\t\ttop: 4px;\n\t\tright: 14px;\n\t\tbottom: 9px;\n\t}\n\t.header .url {\n\t\tborder: 1px solid #999;\n\t\tbackground-color: #fff;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tborder-radius: 2px;\n\t\tpadding-left: 4px;\n\t\tpadding-right: 4px;\n\t}\n\t.messages {\n\t\toverflow-y: scroll;\n\t\tposition: absolute;\n\t\tleft: 0;\n\t\tright: 0;\n\t\ttop: 36px;\n\t\tbottom: 0;\n\t\tborder-top: 1px solid #ccc;\n\t}\n\t.message {\n\t\tborder-bottom: 1px solid #bbb;\n\t\tpadding: 2px;\n\t}\n\t.message-type {\n\t\tfont-weight: bold;\n\t\tposition: absolute;\n\t\twidth: 80px;\n\t\tdisplay: block;\n\t}\n\t.message-data {\n\t\tmargin-left: 90px;\n\t\tdisplay: block;\n\t\tword-wrap: break-word;\n\t\twhite-space: pre;\n\t}\n\t.type-input,\n\t.type-send {\n\t\tbackground-color: #ffe;\n\t}\n\t.type-onmessage {\n\t\tbackground-color: #eef;\n\t}\n\t.type-open,\n\t.type-onopen {\n\t\tbackground-color: #efe;\n\t}\n\t.type-close,\n\t.type-onclose {\n\t\tbackground-color: #fee;\n\t}\n\t.type-onerror,\n\t.type-exception {\n\t\tbackground-color: #333;\n\t\tcolor: #f99;\n\t}\n\t.type-send .message-type,\n\t.type-onmessage .message-type {\n\t\topacity: 0.2;\n\t}\n\t.type-input .message-type {\n\t\tcolor: #090;\n\t}\n\t.send-input {\n\t\twidth: 100%;\n\t\tborder: 0;\n\t\tpadding: 0;\n\t\tmargin: -1px;\n\t\tbackground-color: inherit;\n\t}\n\t.send-input:focus {\n\t\toutline: none;\n\t}\n</style>\n\n<header class=\"header\">\n\t<button class=\"disconnect\" title=\"Disconnect\" style=\"display:none\">&times;</button>\n\t<button class=\"connect\" title=\"Connect\" style=\"display:none\">&#x2714;</button>\n\t<div class=\"url-holder\">\n\t\t<input class=\"url\" type=\"text\" value=\"{{addr}}\" spellcheck=\"false\">\n\t</div>\n</header>\n\n<section class=\"messages\">\n\t<div class=\"message template\">\n\t\t<span class=\"message-type\"></span>\n\t\t<span class=\"message-data\"></span>\n\t</div>\n\t<div class=\"message type-input\">\n\t\t<span class=\"message-type\">send &#xbb;</span>\n\t\t<span class=\"message-data\"><input type=\"text\" class=\"send-input\" spellcheck=\"false\"></span>\n\t</div>\n</section>\n\n<script>\n\n\tvar ws = null;\n\n\tfunction ready() {\n\t\tselect('.connect').style.display = 'block';\n\t\tselect('.disconnect').style.display = 'none';\n\n\t\tselect('.connect').addEventListener('click', function() {\n\t\t\tconnect(select('.url').value);\n\t\t});\n\t\tselect('.disconnect').addEventListener('click', function() {\n\t\t\tdisconnect();\n\t\t});\n\n\t\tselect('.url').focus();\n\t\tselect('.url').addEventListener('keydown', function(ev) {\n\t\t\tvar code = ev.which || ev.keyCode;\n\t\t\t// Enter key pressed\n\t\t\tif (code  == 13) { \t\t\t\n\t\t\t\tupdatePageUrl();\n\t\t\t\tconnect(select('.url').value);\n\t\t\t}\n\t\t});\n\t\tselect('.url').addEventListener('change', updatePageUrl);\n\n\t\tselect('.send-input').addEventListener('keydown', function(ev) {\n\t\t\tvar code = ev.which || ev.keyCode;\n\t\t\t// Enter key pressed\n\t\t\tif (code == 13) { \n\t\t\t\tvar msg = select('.send-input').value;\n\t\t\t\tselect('.send-input').value = '';\n\t\t\t\tsend(msg);\n\t\t\t}\n\t\t\t// Up key pressed\n\t\t\tif (code == 38) {\n\t\t\t\tmoveThroughSendHistory(1);\n\t\t\t}\n\t\t\t// Down key pressed\n\t\t\tif (code == 40) {\n\t\t\t\tmoveThroughSendHistory(-1);\n\t\t\t}\n\t\t});\n\t\twindow.addEventListener('popstate', updateWebSocketUrl);\n\t\tupdateWebSocketUrl();\n\t}\n\n\tfunction updatePageUrl() {\n\t\tvar match = select('.url').value.match(new RegExp('^(ws)(s)?://([^/]*)(/.*)$'));\n\t\tif (match) {\n\t\t\tvar pageUrlSuffix = match[4];\n\t\t\tif (history.state != pageUrlSuffix) {\n\t\t\t\thistory.pushState(pageUrlSuffix, pageUrlSuffix, pageUrlSuffix);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction updateWebSocketUrl() {\n\t\tvar match = location.href.match(new RegExp('^(http)(s)?://([^/]*)(/.*)$'));\n\t\tif (match) {\n\t\t\tvar wsUrl = 'ws' + (match[2] || '') + '://' + match[3] + match[4];\n\t\t\tselect('.url').value = wsUrl;\n\t\t}\n\t}\n\n\tfunction appendMessage(type, data) {\n\t\tvar template = select('.message.template');\n\t\tvar el = template.parentElement.insertBefore(template.cloneNode(true), select('.message.type-input'));\n\t\tel.classList.remove('template');\n\t\tel.classList.add('type-' + type.toLowerCase());\n\t\tel.querySelector('.message-type').textContent = type;\n\t\tel.querySelector('.message-data').textContent = data || '';\n\t\tel.querySelector('.message-data').innerHTML += '&nbsp;';\n\t\tel.scrollIntoView(true);\n\t}\n\n\tfunction connect(url) {\n\t\tfunction action() {\n\t\t\tappendMessage('open', url);\n\t\t\ttry {\n\t\t\t\tws = new WebSocket(url);\n\t\t\t} catch (ex) {\n\t\t\t\tappendMessage('exception', 'Cannot connect: ' + ex);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tselect('.connect').style.display = 'none';\n\t\t\tselect('.disconnect').style.display = 'block';\n\n\t\t\tws.addEventListener('open', function(ev) {\n\t\t\t\tappendMessage('onopen');\n\t\t\t});\n\t\t\tws.addEventListener('close', function(ev) {\n\t\t\t\tselect('.connect').style.display = 'block';\n\t\t\t\tselect('.disconnect').style.display = 'none';\n\t\t\t\tappendMessage('onclose', '[Clean: ' + ev.wasClean + ', Code: ' + ev.code + ', Reason: ' + (ev.reason || 'none') + ']');\n\t\t\t\tws = null;\n\t\t\t\tselect('.url').focus();\n\t\t\t});\n\t\t\tws.addEventListener('message', function(ev) {\n\t\t\t\tif (typeof(ev.data) == \"object\") { \n\t\t\t\t\tvar rd = new FileReader();\n\t\t\t\t\trd.onload = function(ev){\n\t\t\t\t\t\tappendMessage('onmessage', \"BLOB: \"+rd.result);\n\t\t\t\t\t};\n\t\t\t\t\trd.readAsBinaryString(ev.data);\n\t\t\t\t} else {\n\t\t\t\t\tappendMessage('onmessage', ev.data);\n\t\t\t\t}\n\t\t\t});\n\t\t\tws.addEventListener('error', function(ev) {\n\t\t\t\tappendMessage('onerror');\n\t\t\t});\n\n\t\t\tselect('.send-input').focus();\n\t\t}\n\n\t\tif (ws) {\n\t\t\tws.addEventListener('close', function(ev) {\n\t\t\t\taction();\n\t\t\t});\n\t\t\tdisconnect();\n\t\t} else {\n\t\t\taction();\n\t\t}\n\t}\n\n\tfunction disconnect() {\n\t\tif (ws) {\n\t\t\tappendMessage('close');\n\t\t\tws.close();\n\t\t}\n\t}\n\n\tfunction send(msg) {\n\t\tappendToSendHistory(msg);\n\t\tappendMessage('send', msg);\n\t\tif (ws) {\n\t\t\ttry {\n\t\t\t\tws.send(msg);\n\t\t\t} catch (ex) {\n\t\t\t\tappendMessage('exception', 'Cannot send: ' + ex);\n\t\t\t}\n\t\t} else {\n\t\t\tappendMessage('exception', 'Cannot send: Not connected');\n\t\t}\n\t}\n\n\tfunction select(selector) {\n\t\treturn document.querySelector(selector);\n\t}\n\n\tvar maxSendHistorySize = 100;\n\t\tcurrentSendHistoryPosition = -1,\n\t\tsendHistoryRollback = '';\n\n\tfunction appendToSendHistory(msg) {\n\t\tcurrentSendHistoryPosition = -1;\n\t\tsendHistoryRollback = '';\n\t\tvar sendHistory = JSON.parse(localStorage['websocketdconsole.sendhistory'] || '[]');\n\t\tif (sendHistory[0] !== msg) {\n\t\t\tsendHistory.unshift(msg);\n\t\t\twhile (sendHistory.length > maxSendHistorySize) {\n\t\t\t\tsendHistory.pop();\n\t\t\t}\n\t\t\tlocalStorage['websocketdconsole.sendhistory'] = JSON.stringify(sendHistory);\n\t\t}\n\t}\n\n\tfunction moveThroughSendHistory(offset) {\n\t\tif (currentSendHistoryPosition == -1) {\n\t\t\tsendHistoryRollback = select('.send-input').value;\n\t\t}\n\t\tvar sendHistory = JSON.parse(localStorage['websocketdconsole.sendhistory'] || '[]');\n\t\tcurrentSendHistoryPosition += offset;\n\t\tcurrentSendHistoryPosition = Math.max(-1, Math.min(sendHistory.length - 1, currentSendHistoryPosition));\n\n\t\tvar el = select('.send-input');\n\t\tel.value = currentSendHistoryPosition == -1\n\t\t\t? sendHistoryRollback\n\t\t\t: sendHistory[currentSendHistoryPosition];\n\t\tsetTimeout(function() {\n\t\t\tel.setSelectionRange(el.value.length, el.value.length);\n\t\t}, 0);\n\t}\n\n\tdocument.addEventListener(\"DOMContentLoaded\", ready, false);\n\n</script>\n\n`\n)\n\nvar ConsoleContent = defaultConsoleContent\n"
  },
  {
    "path": "libwebsocketd/endpoint.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\ntype Endpoint interface {\n\tStartReading()\n\tTerminate()\n\tOutput() chan []byte\n\tSend([]byte) bool\n}\n\nfunc PipeEndpoints(e1, e2 Endpoint) {\n\te1.StartReading()\n\te2.StartReading()\n\n\tdefer e1.Terminate()\n\tdefer e2.Terminate()\n\tfor {\n\t\tselect {\n\t\tcase msgOne, ok := <-e1.Output():\n\t\t\tif !ok || !e2.Send(msgOne) {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase msgTwo, ok := <-e2.Output():\n\t\t\tif !ok || !e1.Send(msgTwo) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "libwebsocketd/endpoint_test.go",
    "content": "package libwebsocketd\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n)\n\nvar eol_tests = []string{\n\t\"\", \"\\n\", \"\\r\\n\", \"ok\\n\", \"ok\\n\",\n\t\"quite long string for our test\\n\",\n\t\"quite long string for our test\\r\\n\",\n}\n\nvar eol_answers = []string{\n\t\"\", \"\", \"\", \"ok\", \"ok\",\n\t\"quite long string for our test\", \"quite long string for our test\",\n}\n\nfunc TestTrimEOL(t *testing.T) {\n\tfor n := 0; n < len(eol_tests); n++ {\n\t\tansw := trimEOL([]byte(eol_tests[n]))\n\t\tif string(answ) != eol_answers[n] {\n\t\t\tt.Errorf(\"Answer '%s' did not match predicted '%s'\", answ, eol_answers[n])\n\t\t}\n\t}\n}\n\nfunc BenchmarkTrimEOL(b *testing.B) {\n\tfor n := 0; n < b.N; n++ {\n\t\ttrimEOL([]byte(eol_tests[n%len(eol_tests)]))\n\t}\n}\n\ntype TestEndpoint struct {\n\tlimit  int\n\tprefix string\n\tc      chan []byte\n\tresult []string\n}\n\nfunc (e *TestEndpoint) StartReading() {\n\tgo func() {\n\t\tfor i := 0; i < e.limit; i++ {\n\t\t\te.c <- []byte(e.prefix + strconv.Itoa(i))\n\t\t}\n\t\ttime.Sleep(time.Millisecond) // should be enough for smaller channel to catch up with long one\n\t\tclose(e.c)\n\t}()\n}\n\nfunc (e *TestEndpoint) Terminate() {\n}\n\nfunc (e *TestEndpoint) Output() chan []byte {\n\treturn e.c\n}\n\nfunc (e *TestEndpoint) Send(msg []byte) bool {\n\te.result = append(e.result, string(msg))\n\treturn true\n}\n\nfunc TestEndpointPipe(t *testing.T) {\n\tone := &TestEndpoint{2, \"one:\", make(chan []byte), make([]string, 0)}\n\ttwo := &TestEndpoint{4, \"two:\", make(chan []byte), make([]string, 0)}\n\tPipeEndpoints(one, two)\n\tif len(one.result) != 4 || len(two.result) != 2 {\n\t\tt.Errorf(\"Invalid lengths, should be 4 and 2: %v %v\", one.result, two.result)\n\t} else if one.result[0] != \"two:0\" || two.result[0] != \"one:0\" {\n\t\tt.Errorf(\"Invalid first results, should be two:0 and one:0: %#v %#v\", one.result[0], two.result[0])\n\t}\n}\n"
  },
  {
    "path": "libwebsocketd/env.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n)\n\nconst (\n\tgatewayInterface = \"websocketd-CGI/0.1\"\n)\n\nvar headerNewlineToSpace = strings.NewReplacer(\"\\n\", \" \", \"\\r\", \" \")\nvar headerDashToUnderscore = strings.NewReplacer(\"-\", \"_\")\n\nfunc createEnv(handler *WebsocketdHandler, req *http.Request, log *LogScope) []string {\n\theaders := req.Header\n\n\turl := req.URL\n\n\tserverName, serverPort, err := tellHostPort(req.Host, handler.server.Config.Ssl)\n\tif err != nil {\n\t\t// This does mean that we cannot detect port from Host: header... Just keep going with \"\", guessing is bad.\n\t\tlog.Debug(\"env\", \"Host port detection error: %s\", err)\n\t\tserverPort = \"\"\n\t}\n\n\tstandardEnvCount := 20\n\tif handler.server.Config.Ssl {\n\t\tstandardEnvCount += 1\n\t}\n\n\tparentLen := len(handler.server.Config.ParentEnv)\n\tenv := make([]string, 0, len(headers)+standardEnvCount+parentLen+len(handler.server.Config.Env))\n\n\t// This variable could be rewritten from outside\n\tenv = appendEnv(env, \"SERVER_SOFTWARE\", handler.server.Config.ServerSoftware)\n\n\tparentStarts := len(env)\n\tenv = append(env, handler.server.Config.ParentEnv...)\n\n\t// IMPORTANT ---> Adding a header? Make sure standardEnvCount (above) is up to date.\n\n\t// Standard CGI specification headers.\n\t// As defined in http://tools.ietf.org/html/rfc3875\n\tenv = appendEnv(env, \"REMOTE_ADDR\", handler.RemoteInfo.Addr)\n\tenv = appendEnv(env, \"REMOTE_HOST\", handler.RemoteInfo.Host)\n\tenv = appendEnv(env, \"SERVER_NAME\", serverName)\n\tenv = appendEnv(env, \"SERVER_PORT\", serverPort)\n\tenv = appendEnv(env, \"SERVER_PROTOCOL\", req.Proto)\n\tenv = appendEnv(env, \"GATEWAY_INTERFACE\", gatewayInterface)\n\tenv = appendEnv(env, \"REQUEST_METHOD\", req.Method)\n\tenv = appendEnv(env, \"SCRIPT_NAME\", handler.URLInfo.ScriptPath)\n\tenv = appendEnv(env, \"PATH_INFO\", handler.URLInfo.PathInfo)\n\tenv = appendEnv(env, \"PATH_TRANSLATED\", url.Path)\n\tenv = appendEnv(env, \"QUERY_STRING\", url.RawQuery)\n\n\t// Not supported, but we explicitly clear them so we don't get leaks from parent environment.\n\tenv = appendEnv(env, \"AUTH_TYPE\", \"\")\n\tenv = appendEnv(env, \"CONTENT_LENGTH\", \"\")\n\tenv = appendEnv(env, \"CONTENT_TYPE\", \"\")\n\tenv = appendEnv(env, \"REMOTE_IDENT\", \"\")\n\tenv = appendEnv(env, \"REMOTE_USER\", \"\")\n\n\t// Non standard, but commonly used headers.\n\tenv = appendEnv(env, \"UNIQUE_ID\", handler.Id) // Based on Apache mod_unique_id.\n\tenv = appendEnv(env, \"REMOTE_PORT\", handler.RemoteInfo.Port)\n\tenv = appendEnv(env, \"REQUEST_URI\", url.RequestURI()) // e.g. /foo/blah?a=b\n\n\t// The following variables are part of the CGI specification, but are optional\n\t// and not set by websocketd:\n\t//\n\t//   AUTH_TYPE, REMOTE_USER, REMOTE_IDENT\n\t//     -- Authentication left to the underlying programs.\n\t//\n\t//   CONTENT_LENGTH, CONTENT_TYPE\n\t//     -- makes no sense for WebSocket connections.\n\t//\n\t//   SSL_*\n\t//     -- SSL variables are not supported, HTTPS=on added for websocketd running with --ssl\n\n\tif handler.server.Config.Ssl {\n\t\tenv = appendEnv(env, \"HTTPS\", \"on\")\n\t}\n\n\tif log.MinLevel == LogDebug {\n\t\tfor i, v := range env {\n\t\t\tif i >= parentStarts && i < parentLen+parentStarts {\n\t\t\t\tlog.Debug(\"env\", \"Parent envvar: %v\", v)\n\t\t\t} else {\n\t\t\t\tlog.Debug(\"env\", \"Std. variable: %v\", v)\n\t\t\t}\n\t\t}\n\t}\n\n\tfor k, hdrs := range headers {\n\t\theader := fmt.Sprintf(\"HTTP_%s\", headerDashToUnderscore.Replace(k))\n\t\tenv = appendEnv(env, header, hdrs...)\n\t\tlog.Debug(\"env\", \"Header variable %s\", env[len(env)-1])\n\t}\n\n\tfor _, v := range handler.server.Config.Env {\n\t\tenv = append(env, v)\n\t\tlog.Debug(\"env\", \"External variable: %s\", v)\n\t}\n\n\treturn env\n}\n\n// Adapted from net/http/header.go\nfunc appendEnv(env []string, k string, v ...string) []string {\n\tif len(v) == 0 {\n\t\treturn env\n\t}\n\n\tvCleaned := make([]string, 0, len(v))\n\tfor _, val := range v {\n\t\tvCleaned = append(vCleaned, strings.TrimSpace(headerNewlineToSpace.Replace(val)))\n\t}\n\treturn append(env, fmt.Sprintf(\"%s=%s\",\n\t\tstrings.ToUpper(k),\n\t\tstrings.Join(vCleaned, \", \")))\n}\n"
  },
  {
    "path": "libwebsocketd/handler.go",
    "content": "package libwebsocketd\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar ScriptNotFoundError = errors.New(\"script not found\")\n\n// WebsocketdHandler is a single request information and processing structure, it handles WS requests out of all that daemon can handle (static, cgi, devconsole)\ntype WebsocketdHandler struct {\n\tserver *WebsocketdServer\n\n\tId string\n\t*RemoteInfo\n\t*URLInfo // TODO: I cannot find where it's used except in one single place as URLInfo.FilePath\n\tEnv      []string\n\n\tcommand string\n}\n\n// NewWebsocketdHandler constructs the struct and parses all required things in it...\nfunc NewWebsocketdHandler(s *WebsocketdServer, req *http.Request, log *LogScope) (wsh *WebsocketdHandler, err error) {\n\twsh = &WebsocketdHandler{server: s, Id: generateId()}\n\tlog.Associate(\"id\", wsh.Id)\n\n\twsh.RemoteInfo, err = GetRemoteInfo(req.RemoteAddr, s.Config.ReverseLookup)\n\tif err != nil {\n\t\tlog.Error(\"session\", \"Could not understand remote address '%s': %s\", req.RemoteAddr, err)\n\t\treturn nil, err\n\t}\n\tlog.Associate(\"remote\", wsh.RemoteInfo.Host)\n\n\twsh.URLInfo, err = GetURLInfo(req.URL.Path, s.Config)\n\tif err != nil {\n\t\tlog.Access(\"session\", \"NOT FOUND: %s\", err)\n\t\treturn nil, err\n\t}\n\n\twsh.command = s.Config.CommandName\n\tif s.Config.UsingScriptDir {\n\t\twsh.command = wsh.URLInfo.FilePath\n\t}\n\tlog.Associate(\"command\", wsh.command)\n\n\twsh.Env = createEnv(wsh, req, log)\n\n\treturn wsh, nil\n}\n\nfunc (wsh *WebsocketdHandler) accept(ws *websocket.Conn, log *LogScope) {\n\tdefer ws.Close()\n\n\tlog.Access(\"session\", \"CONNECT\")\n\tdefer log.Access(\"session\", \"DISCONNECT\")\n\n\tlaunched, err := launchCmd(wsh.command, wsh.server.Config.CommandArgs, wsh.Env)\n\tif err != nil {\n\t\tlog.Error(\"process\", \"Could not launch process %s %s (%s)\", wsh.command, strings.Join(wsh.server.Config.CommandArgs, \" \"), err)\n\t\treturn\n\t}\n\n\tlog.Associate(\"pid\", strconv.Itoa(launched.cmd.Process.Pid))\n\n\tbinary := wsh.server.Config.Binary\n\tprocess := NewProcessEndpoint(launched, binary, log)\n\tif cms := wsh.server.Config.CloseMs; cms != 0 {\n\t\tprocess.closetime += time.Duration(cms) * time.Millisecond\n\t}\n\twsEndpoint := NewWebSocketEndpoint(ws, binary, log)\n\n\tPipeEndpoints(process, wsEndpoint)\n}\n\n// RemoteInfo holds information about remote http client\ntype RemoteInfo struct {\n\tAddr, Host, Port string\n}\n\n// GetRemoteInfo creates RemoteInfo structure and fills its fields appropriately\nfunc GetRemoteInfo(remote string, doLookup bool) (*RemoteInfo, error) {\n\taddr, port, err := net.SplitHostPort(remote)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar host string\n\tif doLookup {\n\t\thosts, err := net.LookupAddr(addr)\n\t\tif err != nil || len(hosts) == 0 {\n\t\t\thost = addr\n\t\t} else {\n\t\t\thost = hosts[0]\n\t\t}\n\t} else {\n\t\thost = addr\n\t}\n\n\treturn &RemoteInfo{Addr: addr, Host: host, Port: port}, nil\n}\n\n// URLInfo - structure carrying information about current request and it's mapping to filesystem\ntype URLInfo struct {\n\tScriptPath string\n\tPathInfo   string\n\tFilePath   string\n}\n\n// GetURLInfo is a function that parses path and provides URL info according to libwebsocketd.Config fields\nfunc GetURLInfo(path string, config *Config) (*URLInfo, error) {\n\tif !config.UsingScriptDir {\n\t\treturn &URLInfo{\"/\", path, \"\"}, nil\n\t}\n\n\tparts := strings.Split(path[1:], \"/\")\n\turlInfo := &URLInfo{}\n\n\tfor i, part := range parts {\n\t\turlInfo.ScriptPath = strings.Join([]string{urlInfo.ScriptPath, part}, \"/\")\n\t\turlInfo.FilePath = filepath.Join(config.ScriptDir, urlInfo.ScriptPath)\n\t\tisLastPart := i == len(parts)-1\n\t\tstatInfo, err := os.Stat(urlInfo.FilePath)\n\n\t\t// not a valid path\n\t\tif err != nil {\n\t\t\treturn nil, ScriptNotFoundError\n\t\t}\n\n\t\t// at the end of url but is a dir\n\t\tif isLastPart && statInfo.IsDir() {\n\t\t\treturn nil, ScriptNotFoundError\n\t\t}\n\n\t\t// we've hit a dir, carry on looking\n\t\tif statInfo.IsDir() {\n\t\t\tcontinue\n\t\t}\n\n\t\t// no extra args\n\t\tif isLastPart {\n\t\t\treturn urlInfo, nil\n\t\t}\n\n\t\t// build path info from extra parts of url\n\t\turlInfo.PathInfo = \"/\" + strings.Join(parts[i+1:], \"/\")\n\t\treturn urlInfo, nil\n\t}\n\tpanic(fmt.Sprintf(\"GetURLInfo cannot parse path %#v\", path))\n}\n\nfunc generateId() string {\n\treturn strconv.FormatInt(time.Now().UnixNano(), 10)\n}\n"
  },
  {
    "path": "libwebsocketd/handler_test.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n)\n\nfunc TestParsePathWithScriptDir(t *testing.T) {\n\tbaseDir, _ := ioutil.TempDir(\"\", \"websockets\")\n\tscriptDir := filepath.Join(baseDir, \"foo\", \"bar\")\n\tscriptPath := filepath.Join(scriptDir, \"baz.sh\")\n\n\tdefer os.RemoveAll(baseDir)\n\n\tif err := os.MkdirAll(scriptDir, os.ModePerm); err != nil {\n\t\tt.Error(\"could not create \", scriptDir)\n\t}\n\tif _, err := os.Create(scriptPath); err != nil {\n\t\tt.Error(\"could not create \", scriptPath)\n\t}\n\n\tconfig := new(Config)\n\tconfig.UsingScriptDir = true\n\tconfig.ScriptDir = baseDir\n\n\tvar res *URLInfo\n\tvar err error\n\n\t// simple url\n\tres, err = GetURLInfo(\"/foo/bar/baz.sh\", config)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif res.ScriptPath != \"/foo/bar/baz.sh\" {\n\t\tt.Error(\"scriptPath\")\n\t}\n\tif res.PathInfo != \"\" {\n\t\tt.Error(\"GetURLInfo\")\n\t}\n\tif res.FilePath != scriptPath {\n\t\tt.Error(\"filePath\")\n\t}\n\n\t// url with extra path info\n\tres, err = GetURLInfo(\"/foo/bar/baz.sh/some/extra/stuff\", config)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif res.ScriptPath != \"/foo/bar/baz.sh\" {\n\t\tt.Error(\"scriptPath\")\n\t}\n\tif res.PathInfo != \"/some/extra/stuff\" {\n\t\tt.Error(\"GetURLInfo\")\n\t}\n\tif res.FilePath != scriptPath {\n\t\tt.Error(\"filePath\")\n\t}\n\n\t// non-existing file\n\t_, err = GetURLInfo(\"/foo/bar/bang.sh\", config)\n\tif err == nil {\n\t\tt.Error(\"non-existing file should fail\")\n\t}\n\tif err != ScriptNotFoundError {\n\t\tt.Error(\"should fail with script not found\")\n\t}\n\n\t// non-existing dir\n\t_, err = GetURLInfo(\"/hoohar/bang.sh\", config)\n\tif err == nil {\n\t\tt.Error(\"non-existing dir should fail\")\n\t}\n\tif err != ScriptNotFoundError {\n\t\tt.Error(\"should fail with script not found\")\n\t}\n}\n\nfunc TestParsePathExplicitScript(t *testing.T) {\n\tconfig := new(Config)\n\tconfig.UsingScriptDir = false\n\n\tres, err := GetURLInfo(\"/some/path\", config)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif res.ScriptPath != \"/\" {\n\t\tt.Error(\"scriptPath\")\n\t}\n\tif res.PathInfo != \"/some/path\" {\n\t\tt.Error(\"GetURLInfo\")\n\t}\n\tif res.FilePath != \"\" {\n\t\tt.Error(\"filePath\")\n\t}\n}\n"
  },
  {
    "path": "libwebsocketd/http.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/http/cgi\"\n\t\"net/textproto\"\n\t\"net/url\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\nvar ForkNotAllowedError = errors.New(\"too many forks active\")\n\n// WebsocketdServer presents http.Handler interface for requests libwebsocketd is handling.\ntype WebsocketdServer struct {\n\tConfig *Config\n\tLog    *LogScope\n\tforks  chan byte\n}\n\n// NewWebsocketdServer creates WebsocketdServer struct with pre-determined config, logscope and maxforks limit\nfunc NewWebsocketdServer(config *Config, log *LogScope, maxforks int) *WebsocketdServer {\n\tmux := &WebsocketdServer{\n\t\tConfig: config,\n\t\tLog:    log,\n\t}\n\tif maxforks > 0 {\n\t\tmux.forks = make(chan byte, maxforks)\n\t}\n\treturn mux\n}\n\nfunc splitMimeHeader(s string) (string, string) {\n\tp := strings.IndexByte(s, ':')\n\tif p < 0 {\n\t\treturn s, \"\"\n\t}\n\tkey := textproto.CanonicalMIMEHeaderKey(s[:p])\n\n\tfor p = p + 1; p < len(s); p++ {\n\t\tif s[p] != ' ' {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn key, s[p:]\n}\n\nfunc pushHeaders(h http.Header, hdrs []string) {\n\tfor _, hstr := range hdrs {\n\t\th.Add(splitMimeHeader(hstr))\n\t}\n}\n\n// ServeHTTP muxes between WebSocket handler, CGI handler, DevConsole, Static HTML or 404.\nfunc (h *WebsocketdServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {\n\tlog := h.Log.NewLevel(h.Log.LogFunc)\n\tlog.Associate(\"url\", h.TellURL(\"http\", req.Host, req.RequestURI))\n\n\tif h.Config.CommandName != \"\" || h.Config.UsingScriptDir {\n\t\thdrs := req.Header\n\t\tupgradeRe := regexp.MustCompile(`(?i)(^|[,\\s])Upgrade($|[,\\s])`)\n\t\t// WebSocket, limited to size of h.forks\n\t\tif strings.ToLower(hdrs.Get(\"Upgrade\")) == \"websocket\" && upgradeRe.MatchString(hdrs.Get(\"Connection\")) {\n\t\t\tif h.noteForkCreated() == nil {\n\t\t\t\tdefer h.noteForkCompled()\n\n\t\t\t\t// start figuring out if we even need to upgrade\n\t\t\t\thandler, err := NewWebsocketdHandler(h, req, log)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif err == ScriptNotFoundError {\n\t\t\t\t\t\tlog.Access(\"session\", \"NOT FOUND: %s\", err)\n\t\t\t\t\t\thttp.Error(w, \"404 Not Found\", 404)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.Access(\"session\", \"INTERNAL ERROR: %s\", err)\n\t\t\t\t\t\thttp.Error(w, \"500 Internal Server Error\", 500)\n\t\t\t\t\t}\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tvar headers http.Header\n\t\t\t\tif len(h.Config.Headers)+len(h.Config.HeadersWs) > 0 {\n\t\t\t\t\theaders = http.Header(make(map[string][]string))\n\t\t\t\t\tpushHeaders(headers, h.Config.Headers)\n\t\t\t\t\tpushHeaders(headers, h.Config.HeadersWs)\n\t\t\t\t}\n\n\t\t\t\tupgrader := &websocket.Upgrader{\n\t\t\t\t\tHandshakeTimeout: h.Config.HandshakeTimeout,\n\t\t\t\t\tCheckOrigin: func(r *http.Request) bool {\n\t\t\t\t\t\t// backporting previous checkorigin for use in gorilla/websocket for now\n\t\t\t\t\t\terr := checkOrigin(req, h.Config, log)\n\t\t\t\t\t\treturn err == nil\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tconn, err := upgrader.Upgrade(w, req, headers)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Access(\"session\", \"Unable to Upgrade: %s\", err)\n\t\t\t\t\thttp.Error(w, \"500 Internal Error\", 500)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// old func was used in x/net/websocket style, we reuse it here for gorilla/websocket\n\t\t\t\thandler.accept(conn, log)\n\t\t\t\treturn\n\n\t\t\t} else {\n\t\t\t\tlog.Error(\"http\", \"Max of possible forks already active, upgrade rejected\")\n\t\t\t\thttp.Error(w, \"429 Too Many Requests\", http.StatusTooManyRequests)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\tpushHeaders(w.Header(), h.Config.HeadersHTTP)\n\n\t// Dev console (if enabled)\n\tif h.Config.DevConsole {\n\t\tlog.Access(\"http\", \"DEVCONSOLE\")\n\t\tcontent := ConsoleContent\n\t\tcontent = strings.Replace(content, \"{{license}}\", License, -1)\n\t\tcontent = strings.Replace(content, \"{{addr}}\", h.TellURL(\"ws\", req.Host, req.RequestURI), -1)\n\t\thttp.ServeContent(w, req, \".html\", h.Config.StartupTime, strings.NewReader(content))\n\t\treturn\n\t}\n\n\t// CGI scripts, limited to size of h.forks\n\tif h.Config.CgiDir != \"\" {\n\t\tfilePath := path.Join(h.Config.CgiDir, fmt.Sprintf(\".%s\", filepath.FromSlash(req.URL.Path)))\n\t\tif fi, err := os.Stat(filePath); err == nil && !fi.IsDir() {\n\n\t\t\tlog.Associate(\"cgiscript\", filePath)\n\t\t\tif h.noteForkCreated() == nil {\n\t\t\t\tdefer h.noteForkCompled()\n\n\t\t\t\t// Make variables to supplement cgi... Environ it uses will show empty list.\n\t\t\t\tenvlen := len(h.Config.ParentEnv)\n\t\t\t\tcgienv := make([]string, envlen+1)\n\t\t\t\tif envlen > 0 {\n\t\t\t\t\tcopy(cgienv, h.Config.ParentEnv)\n\t\t\t\t}\n\t\t\t\tcgienv[envlen] = \"SERVER_SOFTWARE=\" + h.Config.ServerSoftware\n\t\t\t\tcgiHandler := &cgi.Handler{\n\t\t\t\t\tPath: filePath,\n\t\t\t\t\tEnv: []string{\n\t\t\t\t\t\t\"SERVER_SOFTWARE=\" + h.Config.ServerSoftware,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\tlog.Access(\"http\", \"CGI\")\n\t\t\t\tcgiHandler.ServeHTTP(w, req)\n\t\t\t} else {\n\t\t\t\tlog.Error(\"http\", \"Fork not allowed since maxforks amount has been reached. CGI was not run.\")\n\t\t\t\thttp.Error(w, \"429 Too Many Requests\", http.StatusTooManyRequests)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Static files\n\tif h.Config.StaticDir != \"\" {\n\t\thandler := http.FileServer(http.Dir(h.Config.StaticDir))\n\t\tlog.Access(\"http\", \"STATIC\")\n\t\thandler.ServeHTTP(w, req)\n\t\treturn\n\t}\n\n\t// 404\n\tlog.Access(\"http\", \"NOT FOUND\")\n\thttp.NotFound(w, req)\n}\n\nvar canonicalHostname string\n\n// TellURL is a helper function that changes http to https or ws to wss in case if SSL is used\nfunc (h *WebsocketdServer) TellURL(scheme, host, path string) string {\n\tif len(host) > 0 && host[0] == ':' {\n\t\tif canonicalHostname == \"\" {\n\t\t\tvar err error\n\t\t\tcanonicalHostname, err = os.Hostname()\n\t\t\tif err != nil {\n\t\t\t\tcanonicalHostname = \"UNKNOWN\"\n\t\t\t}\n\t\t}\n\t\thost = canonicalHostname + host\n\t}\n\tif h.Config.Ssl {\n\t\treturn scheme + \"s://\" + host + path\n\t}\n\treturn scheme + \"://\" + host + path\n}\n\nfunc (h *WebsocketdServer) noteForkCreated() error {\n\t// note that forks can be nil since the construct could've been created by\n\t// someone who is not using NewWebsocketdServer\n\tif h.forks != nil {\n\t\tselect {\n\t\tcase h.forks <- 1:\n\t\t\treturn nil\n\t\tdefault:\n\t\t\treturn ForkNotAllowedError\n\t\t}\n\t} else {\n\t\treturn nil\n\t}\n}\n\nfunc (h *WebsocketdServer) noteForkCompled() {\n\tif h.forks != nil { // see comment in noteForkCreated\n\t\tselect {\n\t\tcase <-h.forks:\n\t\t\treturn\n\t\tdefault:\n\t\t\t// This could only happen if the completion handler called more times than creation handler above\n\t\t\t// Code should be audited to not allow this to happen, it's desired to have test that would\n\t\t\t// make sure this is impossible but it is not exist yet.\n\t\t\tpanic(\"Cannot deplet number of allowed forks, something is not right in code!\")\n\t\t}\n\t}\n}\n\nfunc checkOrigin(req *http.Request, config *Config, log *LogScope) (err error) {\n\t// CONVERT GORILLA:\n\t// this is origin checking function, it's called from wshandshake which is from ServeHTTP main handler\n\t// should be trivial to reuse in gorilla's upgrader.CheckOrigin function.\n\t// Only difference is to parse request and fetching passed Origin header out of it instead of using\n\t// pre-parsed wsconf.Origin\n\n\t// check for origin to be correct in future\n\t// handshaker triggers answering with 403 if error was returned\n\t// We keep behavior of original handshaker that populates this field\n\torigin := req.Header.Get(\"Origin\")\n\tif origin == \"\" || (origin == \"null\" && config.AllowOrigins == nil) {\n\t\t// we don't want to trust string \"null\" if there is any\n\t\t// enforcements are active\n\t\torigin = \"file:\"\n\t}\n\n\toriginParsed, err := url.ParseRequestURI(origin)\n\tif err != nil {\n\t\tlog.Access(\"session\", \"Origin parsing error: %s\", err)\n\t\treturn err\n\t}\n\n\tlog.Associate(\"origin\", originParsed.String())\n\n\t// If some origin restrictions are present:\n\tif config.SameOrigin || config.AllowOrigins != nil {\n\t\toriginServer, originPort, err := tellHostPort(originParsed.Host, originParsed.Scheme == \"https\")\n\t\tif err != nil {\n\t\t\tlog.Access(\"session\", \"Origin hostname parsing error: %s\", err)\n\t\t\treturn err\n\t\t}\n\t\tif config.SameOrigin {\n\t\t\tlocalServer, localPort, err := tellHostPort(req.Host, req.TLS != nil)\n\t\t\tif err != nil {\n\t\t\t\tlog.Access(\"session\", \"Request hostname parsing error: %s\", err)\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif originServer != localServer || originPort != localPort {\n\t\t\t\tlog.Access(\"session\", \"Same origin policy mismatch\")\n\t\t\t\treturn fmt.Errorf(\"same origin policy violated\")\n\t\t\t}\n\t\t}\n\t\tif config.AllowOrigins != nil {\n\t\t\tmatchFound := false\n\t\t\tfor _, allowed := range config.AllowOrigins {\n\t\t\t\tif pos := strings.Index(allowed, \"://\"); pos > 0 {\n\t\t\t\t\t// allowed schema has to match\n\t\t\t\t\tallowedURL, err := url.Parse(allowed)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue // pass bad URLs in origin list\n\t\t\t\t\t}\n\t\t\t\t\tif allowedURL.Scheme != originParsed.Scheme {\n\t\t\t\t\t\tcontinue // mismatch\n\t\t\t\t\t}\n\t\t\t\t\tallowed = allowed[pos+3:]\n\t\t\t\t}\n\t\t\t\tallowServer, allowPort, err := tellHostPort(allowed, false)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue // unparseable\n\t\t\t\t}\n\t\t\t\tif allowPort == \"80\" && allowed[len(allowed)-3:] != \":80\" {\n\t\t\t\t\t// any port is allowed, host names need to match\n\t\t\t\t\tmatchFound = allowServer == originServer\n\t\t\t\t} else {\n\t\t\t\t\t// exact match of host names and ports\n\t\t\t\t\tmatchFound = allowServer == originServer && allowPort == originPort\n\t\t\t\t}\n\t\t\t\tif matchFound {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !matchFound {\n\t\t\t\tlog.Access(\"session\", \"Origin is not listed in allowed list\")\n\t\t\t\treturn fmt.Errorf(\"origin list matches were not found\")\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc tellHostPort(host string, ssl bool) (server, port string, err error) {\n\tserver, port, err = net.SplitHostPort(host)\n\tif err != nil {\n\t\tif addrerr, ok := err.(*net.AddrError); ok && strings.Contains(addrerr.Err, \"missing port\") {\n\t\t\tserver = host\n\t\t\tif ssl {\n\t\t\t\tport = \"443\"\n\t\t\t} else {\n\t\t\t\tport = \"80\"\n\t\t\t}\n\t\t\terr = nil\n\t\t}\n\t}\n\treturn server, port, err\n}\n"
  },
  {
    "path": "libwebsocketd/http_test.go",
    "content": "package libwebsocketd\n\nimport (\n\t\"bufio\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"strings\"\n\t\"testing\"\n)\n\nvar tellHostPortTests = []struct {\n\tsrc          string\n\tssl          bool\n\tserver, port string\n}{\n\t{\"localhost\", false, \"localhost\", \"80\"},\n\t{\"localhost:8080\", false, \"localhost\", \"8080\"},\n\t{\"localhost\", true, \"localhost\", \"443\"},\n\t{\"localhost:8080\", true, \"localhost\", \"8080\"},\n}\n\nfunc TestTellHostPort(t *testing.T) {\n\tfor _, testcase := range tellHostPortTests {\n\t\ts, p, e := tellHostPort(testcase.src, testcase.ssl)\n\t\tif testcase.server == \"\" {\n\t\t\tif e == nil {\n\t\t\t\tt.Errorf(\"test case for %#v failed, error was not returned\", testcase.src)\n\t\t\t}\n\t\t} else if e != nil {\n\t\t\tt.Errorf(\"test case for %#v failed, error should not happen\", testcase.src)\n\t\t}\n\t\tif testcase.server != s || testcase.port != p {\n\t\t\tt.Errorf(\"test case for %#v failed, server or port mismatch to expected values (%s:%s)\", testcase.src, s, p)\n\t\t}\n\t}\n}\n\nvar NoOriginsAllowed = []string{}\nvar NoOriginList []string = nil\n\nconst (\n\tReqHTTPS = iota\n\tReqHTTP\n\tOriginMustBeSame\n\tOriginCouldDiffer\n\tReturnsPass\n\tReturnsError\n)\n\nvar CheckOriginTests = []struct {\n\thost    string\n\treqtls  int\n\torigin  string\n\tsame    int\n\tallowed []string\n\tgetsErr int\n\tname    string\n}{\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, NoOriginList, ReturnsPass, \"any origin allowed\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginMustBeSame, NoOriginList, ReturnsError, \"same origin mismatch\"},\n\t{\"server.example.com\", ReqHTTP, \"http://server.example.com\", OriginMustBeSame, NoOriginList, ReturnsPass, \"same origin match\"},\n\t{\"server.example.com\", ReqHTTP, \"https://server.example.com\", OriginMustBeSame, NoOriginList, ReturnsError, \"same origin schema mismatch 1\"},\n\t{\"server.example.com\", ReqHTTPS, \"http://server.example.com\", OriginMustBeSame, NoOriginList, ReturnsError, \"same origin schema mismatch 2\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, NoOriginsAllowed, ReturnsError, \"no origins allowed\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, []string{\"server.example.com\"}, ReturnsError, \"no origin allowed matches (junk prefix)\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, []string{\"example.com.t\"}, ReturnsError, \"no origin allowed matches (junk suffix)\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, []string{\"example.com\"}, ReturnsPass, \"origin allowed clean match\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com:81\", OriginCouldDiffer, []string{\"example.com\"}, ReturnsPass, \"origin allowed any port match\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, []string{\"example.com:80\"}, ReturnsPass, \"origin allowed port match\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, []string{\"example.com:81\"}, ReturnsError, \"origin allowed port mismatch\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com\", OriginCouldDiffer, []string{\"example.com:81\"}, ReturnsError, \"origin allowed port mismatch\"},\n\t{\"server.example.com\", ReqHTTP, \"http://example.com:81\", OriginCouldDiffer, []string{\"example.com:81\"}, ReturnsPass, \"origin allowed port 81 match\"},\n\t{\"server.example.com\", ReqHTTP, \"null\", OriginCouldDiffer, NoOriginList, ReturnsPass, \"any origin allowed, even null\"},\n\t{\"server.example.com\", ReqHTTP, \"\", OriginCouldDiffer, NoOriginList, ReturnsPass, \"any origin allowed, even empty\"},\n}\n\n// CONVERT GORILLA\n// as method for origin checking changes to handle things without websocket.Config the test\n// should be altered too\n\nfunc TestCheckOrigin(t *testing.T) {\n\tfor _, testcase := range CheckOriginTests {\n\t\tbr := bufio.NewReader(strings.NewReader(fmt.Sprintf(`GET /chat HTTP/1.1\nHost: %s\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\nOrigin: %s\nSec-WebSocket-Version: 13\n\n`, testcase.host, testcase.origin)))\n\n\t\treq, err := http.ReadRequest(br)\n\t\tif err != nil {\n\t\t\tt.Fatal(\"request\", err)\n\t\t}\n\n\t\tlog := new(LogScope)\n\t\tlog.LogFunc = func(*LogScope, LogLevel, string, string, string, ...interface{}) {}\n\n\t\tconfig := new(Config)\n\n\t\tif testcase.reqtls == ReqHTTPS { // Fake TLS\n\t\t\tconfig.Ssl = true\n\t\t\treq.TLS = &tls.ConnectionState{}\n\t\t}\n\t\tif testcase.same == OriginMustBeSame {\n\t\t\tconfig.SameOrigin = true\n\t\t}\n\t\tif testcase.allowed != nil {\n\t\t\tconfig.AllowOrigins = testcase.allowed\n\t\t}\n\n\t\terr = checkOrigin(req, config, log)\n\t\tif testcase.getsErr == ReturnsError && err == nil {\n\t\t\tt.Errorf(\"Test case %#v did not get an error\", testcase.name)\n\t\t} else if testcase.getsErr == ReturnsPass && err != nil {\n\t\t\tt.Errorf(\"Test case %#v got error while expected to pass\", testcase.name)\n\t\t}\n\t}\n}\n\nvar mimetest = [][3]string{\n\t{\"Content-Type: text/plain\", \"Content-Type\", \"text/plain\"},\n\t{\"Content-Type:    \", \"Content-Type\", \"\"},\n}\n\nfunc TestSplitMimeHeader(t *testing.T) {\n\tfor _, tst := range mimetest {\n\t\ts, v := splitMimeHeader(tst[0])\n\t\tif tst[1] != s || tst[2] != v {\n\t\t\tt.Errorf(\"%v and %v  are not same as expexted %v and %v\", s, v, tst[1], tst[2])\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "libwebsocketd/launcher.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"io\"\n\t\"os/exec\"\n)\n\ntype LaunchedProcess struct {\n\tcmd    *exec.Cmd\n\tstdin  io.WriteCloser\n\tstdout io.ReadCloser\n\tstderr io.ReadCloser\n}\n\nfunc launchCmd(commandName string, commandArgs []string, env []string) (*LaunchedProcess, error) {\n\tcmd := exec.Command(commandName, commandArgs...)\n\tcmd.Env = env\n\n\tstdout, err := cmd.StdoutPipe()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstderr, err := cmd.StderrPipe()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tstdin, err := cmd.StdinPipe()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = cmd.Start()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &LaunchedProcess{cmd, stdin, stdout, stderr}, err\n}\n"
  },
  {
    "path": "libwebsocketd/license.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nconst (\n\tlicense = `\nCopyright (c) 2013, Joe Walnes and the websocketd authors.\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n1. Redistributions of source code must retain the above copyright notice, this\n   list of conditions and the following disclaimer.\n2. Redistributions in binary form must reproduce the above copyright notice,\n   this list of conditions and the following disclaimer in the documentation\n   and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\nANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n`\n\tLicense = license\n)\n"
  },
  {
    "path": "libwebsocketd/logscope.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype LogLevel int\n\nconst (\n\tLogDebug = iota\n\tLogTrace\n\tLogAccess\n\tLogInfo\n\tLogError\n\tLogFatal\n\n\tLogNone    = 126\n\tLogUnknown = 127\n)\n\ntype LogFunc func(logScope *LogScope, level LogLevel, levelName string, category string, msg string, args ...interface{})\n\ntype LogScope struct {\n\tParent     *LogScope   // Parent scope\n\tMinLevel   LogLevel    // Minimum log level to write out.\n\tMutex      *sync.Mutex // Should be shared across all LogScopes that write to the same destination.\n\tAssociated []AssocPair // Additional data associated with scope\n\tLogFunc    LogFunc\n}\n\ntype AssocPair struct {\n\tKey   string\n\tValue string\n}\n\nfunc (l *LogScope) Associate(key string, value string) {\n\tl.Associated = append(l.Associated, AssocPair{key, value})\n}\n\nfunc (l *LogScope) Debug(category string, msg string, args ...interface{}) {\n\tl.LogFunc(l, LogDebug, \"DEBUG\", category, msg, args...)\n}\n\nfunc (l *LogScope) Trace(category string, msg string, args ...interface{}) {\n\tl.LogFunc(l, LogTrace, \"TRACE\", category, msg, args...)\n}\n\nfunc (l *LogScope) Access(category string, msg string, args ...interface{}) {\n\tl.LogFunc(l, LogAccess, \"ACCESS\", category, msg, args...)\n}\n\nfunc (l *LogScope) Info(category string, msg string, args ...interface{}) {\n\tl.LogFunc(l, LogInfo, \"INFO\", category, msg, args...)\n}\n\nfunc (l *LogScope) Error(category string, msg string, args ...interface{}) {\n\tl.LogFunc(l, LogError, \"ERROR\", category, msg, args...)\n}\n\nfunc (l *LogScope) Fatal(category string, msg string, args ...interface{}) {\n\tl.LogFunc(l, LogFatal, \"FATAL\", category, msg, args...)\n}\n\nfunc (parent *LogScope) NewLevel(logFunc LogFunc) *LogScope {\n\treturn &LogScope{\n\t\tParent:     parent,\n\t\tMinLevel:   parent.MinLevel,\n\t\tMutex:      parent.Mutex,\n\t\tAssociated: make([]AssocPair, 0),\n\t\tLogFunc:    logFunc}\n}\n\nfunc RootLogScope(minLevel LogLevel, logFunc LogFunc) *LogScope {\n\treturn &LogScope{\n\t\tParent:     nil,\n\t\tMinLevel:   minLevel,\n\t\tMutex:      &sync.Mutex{},\n\t\tAssociated: make([]AssocPair, 0),\n\t\tLogFunc:    logFunc}\n}\n\nfunc Timestamp() string {\n\treturn time.Now().Format(time.RFC1123Z)\n}\n\nfunc LevelFromString(s string) LogLevel {\n\tswitch s {\n\tcase \"debug\":\n\t\treturn LogDebug\n\tcase \"trace\":\n\t\treturn LogTrace\n\tcase \"access\":\n\t\treturn LogAccess\n\tcase \"info\":\n\t\treturn LogInfo\n\tcase \"error\":\n\t\treturn LogError\n\tcase \"fatal\":\n\t\treturn LogFatal\n\tcase \"none\":\n\t\treturn LogNone\n\tdefault:\n\t\treturn LogUnknown\n\t}\n}\n"
  },
  {
    "path": "libwebsocketd/process_endpoint.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"syscall\"\n\t\"time\"\n)\n\ntype ProcessEndpoint struct {\n\tprocess   *LaunchedProcess\n\tclosetime time.Duration\n\toutput    chan []byte\n\tlog       *LogScope\n\tbin       bool\n}\n\nfunc NewProcessEndpoint(process *LaunchedProcess, bin bool, log *LogScope) *ProcessEndpoint {\n\treturn &ProcessEndpoint{\n\t\tprocess: process,\n\t\toutput:  make(chan []byte),\n\t\tlog:     log,\n\t\tbin:     bin,\n\t}\n}\n\nfunc (pe *ProcessEndpoint) Terminate() {\n\tterminated := make(chan struct{})\n\tgo func() { pe.process.cmd.Wait(); terminated <- struct{}{} }()\n\n\t// for some processes this is enough to finish them...\n\tpe.process.stdin.Close()\n\n\t// a bit verbose to create good debugging trail\n\tselect {\n\tcase <-terminated:\n\t\tpe.log.Debug(\"process\", \"Process %v terminated after stdin was closed\", pe.process.cmd.Process.Pid)\n\t\treturn // means process finished\n\tcase <-time.After(100*time.Millisecond + pe.closetime):\n\t}\n\n\terr := pe.process.cmd.Process.Signal(syscall.SIGINT)\n\tif err != nil {\n\t\t// process is done without this, great!\n\t\tpe.log.Error(\"process\", \"SIGINT unsuccessful to %v: %s\", pe.process.cmd.Process.Pid, err)\n\t}\n\n\tselect {\n\tcase <-terminated:\n\t\tpe.log.Debug(\"process\", \"Process %v terminated after SIGINT\", pe.process.cmd.Process.Pid)\n\t\treturn // means process finished\n\tcase <-time.After(250*time.Millisecond + pe.closetime):\n\t}\n\n\terr = pe.process.cmd.Process.Signal(syscall.SIGTERM)\n\tif err != nil {\n\t\t// process is done without this, great!\n\t\tpe.log.Error(\"process\", \"SIGTERM unsuccessful to %v: %s\", pe.process.cmd.Process.Pid, err)\n\t}\n\n\tselect {\n\tcase <-terminated:\n\t\tpe.log.Debug(\"process\", \"Process %v terminated after SIGTERM\", pe.process.cmd.Process.Pid)\n\t\treturn // means process finished\n\tcase <-time.After(500*time.Millisecond + pe.closetime):\n\t}\n\n\terr = pe.process.cmd.Process.Kill()\n\tif err != nil {\n\t\tpe.log.Error(\"process\", \"SIGKILL unsuccessful to %v: %s\", pe.process.cmd.Process.Pid, err)\n\t\treturn\n\t}\n\n\tselect {\n\tcase <-terminated:\n\t\tpe.log.Debug(\"process\", \"Process %v terminated after SIGKILL\", pe.process.cmd.Process.Pid)\n\t\treturn // means process finished\n\tcase <-time.After(1000 * time.Millisecond):\n\t}\n\n\tpe.log.Error(\"process\", \"SIGKILL did not terminate %v!\", pe.process.cmd.Process.Pid)\n}\n\nfunc (pe *ProcessEndpoint) Output() chan []byte {\n\treturn pe.output\n}\n\nfunc (pe *ProcessEndpoint) Send(msg []byte) bool {\n\tpe.process.stdin.Write(msg)\n\treturn true\n}\n\nfunc (pe *ProcessEndpoint) StartReading() {\n\tgo pe.log_stderr()\n\tif pe.bin {\n\t\tgo pe.process_binout()\n\t} else {\n\t\tgo pe.process_txtout()\n\t}\n}\n\nfunc (pe *ProcessEndpoint) process_txtout() {\n\tbufin := bufio.NewReader(pe.process.stdout)\n\tfor {\n\t\tbuf, err := bufin.ReadBytes('\\n')\n\t\tif err != nil {\n\t\t\tif err != io.EOF {\n\t\t\t\tpe.log.Error(\"process\", \"Unexpected error while reading STDOUT from process: %s\", err)\n\t\t\t} else {\n\t\t\t\tpe.log.Debug(\"process\", \"Process STDOUT closed\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tpe.output <- trimEOL(buf)\n\t}\n\tclose(pe.output)\n}\n\nfunc (pe *ProcessEndpoint) process_binout() {\n\tbuf := make([]byte, 10*1024*1024)\n\tfor {\n\t\tn, err := pe.process.stdout.Read(buf)\n\t\tif err != nil {\n\t\t\tif err != io.EOF {\n\t\t\t\tpe.log.Error(\"process\", \"Unexpected error while reading STDOUT from process: %s\", err)\n\t\t\t} else {\n\t\t\t\tpe.log.Debug(\"process\", \"Process STDOUT closed\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tpe.output <- append(make([]byte, 0, n), buf[:n]...) // cloned buffer\n\t}\n\tclose(pe.output)\n}\n\nfunc (pe *ProcessEndpoint) log_stderr() {\n\tbufstderr := bufio.NewReader(pe.process.stderr)\n\tfor {\n\t\tbuf, err := bufstderr.ReadSlice('\\n')\n\t\tif err != nil {\n\t\t\tif err != io.EOF {\n\t\t\t\tpe.log.Error(\"process\", \"Unexpected error while reading STDERR from process: %s\", err)\n\t\t\t} else {\n\t\t\t\tpe.log.Debug(\"process\", \"Process STDERR closed\")\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tpe.log.Error(\"stderr\", \"%s\", string(trimEOL(buf)))\n\t}\n}\n\n// trimEOL cuts unixy style \\n and windowsy style \\r\\n suffix from the string\nfunc trimEOL(b []byte) []byte {\n\tlns := len(b)\n\tif lns > 0 && b[lns-1] == '\\n' {\n\t\tlns--\n\t\tif lns > 0 && b[lns-1] == '\\r' {\n\t\t\tlns--\n\t\t}\n\t}\n\treturn b[:lns]\n}\n"
  },
  {
    "path": "libwebsocketd/websocket_endpoint.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage libwebsocketd\n\nimport (\n\t\"io\"\n\t\"io/ioutil\"\n\n\t\"github.com/gorilla/websocket\"\n)\n\n// CONVERT GORILLA\n// This file should be altered to use gorilla's websocket connection type and proper\n// message dispatching methods\n\ntype WebSocketEndpoint struct {\n\tws     *websocket.Conn\n\toutput chan []byte\n\tlog    *LogScope\n\tmtype  int\n}\n\nfunc NewWebSocketEndpoint(ws *websocket.Conn, bin bool, log *LogScope) *WebSocketEndpoint {\n\tendpoint := &WebSocketEndpoint{\n\t\tws:     ws,\n\t\toutput: make(chan []byte),\n\t\tlog:    log,\n\t\tmtype:  websocket.TextMessage,\n\t}\n\tif bin {\n\t\tendpoint.mtype = websocket.BinaryMessage\n\t}\n\treturn endpoint\n}\n\nfunc (we *WebSocketEndpoint) Terminate() {\n\twe.log.Trace(\"websocket\", \"Terminated websocket connection\")\n}\n\nfunc (we *WebSocketEndpoint) Output() chan []byte {\n\treturn we.output\n}\n\nfunc (we *WebSocketEndpoint) Send(msg []byte) bool {\n\tw, err := we.ws.NextWriter(we.mtype)\n\tif err == nil {\n\t\t_, err = w.Write(msg)\n\t}\n\tw.Close() // could need error handling\n\n\tif err != nil {\n\t\twe.log.Trace(\"websocket\", \"Cannot send: %s\", err)\n\t\treturn false\n\t}\n\n\treturn true\n}\n\nfunc (we *WebSocketEndpoint) StartReading() {\n\tgo we.read_frames()\n}\n\nfunc (we *WebSocketEndpoint) read_frames() {\n\tfor {\n\t\tmtype, rd, err := we.ws.NextReader()\n\t\tif err != nil {\n\t\t\twe.log.Debug(\"websocket\", \"Cannot receive: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\tif mtype != we.mtype {\n\t\t\twe.log.Debug(\"websocket\", \"Received message of type that we did not expect... Ignoring...\")\n\t\t}\n\n\t\tp, err := ioutil.ReadAll(rd)\n\t\tif err != nil && err != io.EOF {\n\t\t\twe.log.Debug(\"websocket\", \"Cannot read received message: %s\", err)\n\t\t\tbreak\n\t\t}\n\t\tswitch mtype {\n\t\tcase websocket.TextMessage:\n\t\t\twe.output <- append(p, '\\n')\n\t\tcase websocket.BinaryMessage:\n\t\t\twe.output <- p\n\t\tdefault:\n\t\t\twe.log.Debug(\"websocket\", \"Received message of unknown type: %d\", mtype)\n\t\t}\n\t}\n\tclose(we.output)\n}\n"
  },
  {
    "path": "main.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/joewalnes/websocketd/libwebsocketd\"\n)\n\nfunc logfunc(l *libwebsocketd.LogScope, level libwebsocketd.LogLevel, levelName string, category string, msg string, args ...interface{}) {\n\tif level < l.MinLevel {\n\t\treturn\n\t}\n\tfullMsg := fmt.Sprintf(msg, args...)\n\n\tassocDump := \"\"\n\tfor index, pair := range l.Associated {\n\t\tif index > 0 {\n\t\t\tassocDump += \" \"\n\t\t}\n\t\tassocDump += fmt.Sprintf(\"%s:'%s'\", pair.Key, pair.Value)\n\t}\n\n\tl.Mutex.Lock()\n\tfmt.Printf(\"%s | %-6s | %-10s | %s | %s\\n\", libwebsocketd.Timestamp(), levelName, category, assocDump, fullMsg)\n\tl.Mutex.Unlock()\n}\n\nfunc main() {\n\tconfig := parseCommandLine()\n\n\tlog := libwebsocketd.RootLogScope(config.LogLevel, logfunc)\n\n\tif config.DevConsole {\n\t\tif config.StaticDir != \"\" {\n\t\t\tlog.Fatal(\"server\", \"Invalid parameters: --devconsole cannot be used with --staticdir. Pick one.\")\n\t\t\tos.Exit(4)\n\t\t}\n\t\tif config.CgiDir != \"\" {\n\t\t\tlog.Fatal(\"server\", \"Invalid parameters: --devconsole cannot be used with --cgidir. Pick one.\")\n\t\t\tos.Exit(4)\n\t\t}\n\t}\n\n\tif runtime.GOOS != \"windows\" { // windows relies on env variables to find its libs... e.g. socket stuff\n\t\tos.Clearenv() // it's ok to wipe it clean, we already read env variables from passenv into config\n\t}\n\thandler := libwebsocketd.NewWebsocketdServer(config.Config, log, config.MaxForks)\n\thttp.Handle(\"/\", handler)\n\n\tif config.UsingScriptDir {\n\t\tlog.Info(\"server\", \"Serving from directory      : %s\", config.ScriptDir)\n\t} else if config.CommandName != \"\" {\n\t\tlog.Info(\"server\", \"Serving using application   : %s %s\", config.CommandName, strings.Join(config.CommandArgs, \" \"))\n\t}\n\tif config.StaticDir != \"\" {\n\t\tlog.Info(\"server\", \"Serving static content from : %s\", config.StaticDir)\n\t}\n\tif config.CgiDir != \"\" {\n\t\tlog.Info(\"server\", \"Serving CGI scripts from    : %s\", config.CgiDir)\n\t}\n\n\trejects := make(chan error, 1)\n\tfor _, addrSingle := range config.Addr {\n\t\tlog.Info(\"server\", \"Starting WebSocket server   : %s\", handler.TellURL(\"ws\", addrSingle, \"/\"))\n\t\tif config.DevConsole {\n\t\t\tlog.Info(\"server\", \"Developer console enabled   : %s\", handler.TellURL(\"http\", addrSingle, \"/\"))\n\t\t} else if config.StaticDir != \"\" || config.CgiDir != \"\" {\n\t\t\tlog.Info(\"server\", \"Serving CGI or static files : %s\", handler.TellURL(\"http\", addrSingle, \"/\"))\n\t\t}\n\t\t// ListenAndServe is blocking function. Let's run it in\n\t\t// go routine, reporting result to control channel.\n\t\t// Since it's blocking it'll never return non-error.\n\n\t\tgo func(addr string) {\n\t\t\tif config.Ssl {\n\t\t\t\trejects <- http.ListenAndServeTLS(addr, config.CertFile, config.KeyFile, nil)\n\t\t\t} else {\n\t\t\t\trejects <- http.ListenAndServe(addr, nil)\n\t\t\t}\n\t\t}(addrSingle)\n\n\t\tif config.RedirPort != 0 {\n\t\t\tgo func(addr string) {\n\t\t\t\tpos := strings.IndexByte(addr, ':')\n\t\t\t\trediraddr := addr[:pos] + \":\" + strconv.Itoa(config.RedirPort) // it would be silly to optimize this one\n\t\t\t\tredir := &http.Server{Addr: rediraddr, Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\t\t// redirect to same hostname as in request but different port and probably schema\n\t\t\t\t\turi := \"https://\"\n\t\t\t\t\tif !config.Ssl {\n\t\t\t\t\t\turi = \"http://\"\n\t\t\t\t\t}\n\t\t\t\t\tif cpos := strings.IndexByte(r.Host, ':'); cpos > 0 {\n\t\t\t\t\t\turi += r.Host[:strings.IndexByte(r.Host, ':')] + addr[pos:] + \"/\"\n\t\t\t\t\t} else {\n\t\t\t\t\t\turi += r.Host + addr[pos:] + \"/\"\n\t\t\t\t\t}\n\n\t\t\t\t\thttp.Redirect(w, r, uri, http.StatusMovedPermanently)\n\t\t\t\t})}\n\t\t\t\tlog.Info(\"server\", \"Starting redirect server   : http://%s/\", rediraddr)\n\t\t\t\trejects <- redir.ListenAndServe()\n\t\t\t}(addrSingle)\n\t\t}\n\t}\n\terr := <-rejects\n\tif err != nil {\n\t\tlog.Fatal(\"server\", \"Can't start server: %s\", err)\n\t\tos.Exit(3)\n\t}\n}\n"
  },
  {
    "path": "release/.gitignore",
    "content": "bin\ngo-local\nout\ngo-path\nwebsocketd.exe\n"
  },
  {
    "path": "release/Makefile",
    "content": "# Copyright 2013-2019 Joe Walnes and the websocketd team.\n# All rights reserved.\n# Use of this source code is governed by a BSD-style\n# license that can be found in the LICENSE file.\n\n# Uses Semantic Versioning scheme - http://semver.org/\nVERSION_MAJOR=0\nVERSION_MINOR=4\n\n# Last part of version number (patch) is incremented automatically from Git tags\nLAST_PATCH_VERSION:=$(shell git ls-remote git@github.com:joewalnes/websocketd.git \\\n\t\t| grep -e 'refs/tags/v[0-9\\.]*$$' \\\n\t\t| sed -e 's|^.*refs/tags/v||' \\\n\t\t| grep -e \"^$(VERSION_MAJOR)\\.$(VERSION_MINOR)\\.[0-9][0-9]*$$\" \\\n\t\t| sed -e 's/^.*\\.//' \\\n\t\t| sort -n \\\n\t\t| tail -n 1)\n\n\n\nVERSION_PATCH:=$(or $(LAST_PATCH_VERSION),0)\nRELEASE_VERSION=$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)\n\nGO_VERSION=1.15.7\nPLATFORMS=linux_amd64 linux_386 linux_arm linux_arm64 darwin_amd64 freebsd_amd64 freebsd_386 windows_386 windows_amd64 openbsd_386 openbsd_amd64 solaris_amd64\n\n\n# Would NOT WORK on: ARM, WINDOWS\n\nUNAME_SYS=$(shell uname -s | tr A-Z a-z)\nUNAME_ARCH=$(shell uname -m)\nifeq ($(UNAME_ARCH),x86_64)\n\tUNAME_ARCH=amd64\nelse\n\tUNAME_ARCH=386 \nendif\n\nifeq ($(UNAME_SYS),gnu)\n\tUNAME_SYS=linux\nelse ifeq ($(UNAME_SYS),sunos)\n\tUNAME_SYS=solaris\nendif\n\n\n\nGO_DOWNLOAD_URL=https://dl.google.com/go/go$(GO_VERSION).$(UNAME_SYS)-$(UNAME_ARCH).tar.gz\nGO_DIR=../go-$(GO_VERSION)\n\n# Prevent any global environment polluting the builds\nFLAGS_linux_amd64   = GOOS=linux   GOARCH=amd64\nFLAGS_linux_386     = GOOS=linux   GOARCH=386\nFLAGS_linux_arm     = GOOS=linux   GOARCH=arm   GOARM=5 # ARM5 support for Raspberry Pi\nFLAGS_linux_arm64   = GOOS=linux   GOARCH=arm64  # no need for GOARM= (which is technically 8)\nFLAGS_darwin_amd64  = GOOS=darwin  GOARCH=amd64 CGO_ENABLED=0\nFLAGS_darwin_386    = GOOS=darwin  GOARCH=386   CGO_ENABLED=0\nFLAGS_freebsd_amd64 = GOOS=freebsd GOARCH=amd64 CGO_ENABLED=0\nFLAGS_freebsd_386   = GOOS=freebsd GOARCH=386   CGO_ENABLED=0\nFLAGS_windows_386   = GOOS=windows GOARCH=386   CGO_ENABLED=0\nFLAGS_windows_amd64 = GOOS=windows GOARCH=amd64 CGO_ENABLED=0\nFLAGS_openbsd_386   = GOOS=openbsd GOARCH=386   CGO_ENABLED=0\nFLAGS_openbsd_amd64 = GOOS=openbsd GOARCH=amd64 CGO_ENABLED=0\nFLAGS_solaris_amd64 = GOOS=solaris GOARCH=amd64 CGO_ENABLED=0\n\nEXTENSION_windows_386 = .exe\nEXTENSION_windows_amd64 = .exe\n\nall: build\n\nlocalgo: $(GO_DIR)/bin/go\n\n$(GO_DIR)/bin/go:\n\tmkdir -p $(GO_DIR)\n\trm -f $@\n\t@echo Downloading and unpacking Go $(GO_VERSION) to $(GO_DIR)\n\tcurl -s $(GO_DOWNLOAD_URL) | tar xzf - --strip-components=1 -C $(GO_DIR)\n\n\n# Cross-compile final applications\nout/$(RELEASE_VERSION)/%/websocketd: ../*.go ../libwebsocketd/*.go $(GO_DIR)/bin/go\n\trm -f $@\n\tmkdir -p $(dir $@)\n\t$(FLAGS_$*) $(GO_DIR)/bin/go build -ldflags \"-X main.version=$(RELEASE_VERSION)\" -o out/$(RELEASE_VERSION)/$*/websocketd ..\n\nout/$(RELEASE_VERSION)/%/websocketd.exe: ../*.go ../libwebsocketd/*.go $(GO_DIR)/bin/go\n\trm -f $@\n\tmkdir -p $(dir $@)\n\t$(FLAGS_$*) $(GO_DIR)/bin/go build -ldflags \"-X main.version=$(RELEASE_VERSION)\" -o out/$(RELEASE_VERSION)/$*/websocketd.exe ..\n\nout/$(RELEASE_VERSION)/websocketd-$(RELEASE_VERSION)-%.zip: out/$(RELEASE_VERSION)/%/websocketd\n\trm -f $@\n\tzip -j $@ $< ../{README.md,LICENSE,CHANGES}\n\nout/$(RELEASE_VERSION)/websocketd-$(RELEASE_VERSION)-windows_%.zip: out/$(RELEASE_VERSION)/windows_%/websocketd.exe\n\trm -f $@\n\tzip -j $@ $< ../{README.md,LICENSE,CHANGES}\n\n\nBINARIES = $(foreach PLATFORM,$(PLATFORMS),out/$(RELEASE_VERSION)/$(PLATFORM)/websocketd$(EXTENSION_$(PLATFORM)))\nZIPS = $(foreach PLATFORM,$(PLATFORMS),out/$(RELEASE_VERSION)/websocketd-$(RELEASE_VERSION)-$(PLATFORM).zip)\nDEBS = out/$(RELEASE_VERSION)/websocketd-$(RELEASE_VERSION)_i386.deb out/$(RELEASE_VERSION)/websocketd-$(RELEASE_VERSION)_amd64.deb\nRPMS = out/$(RELEASE_VERSION)/websocketd.$(RELEASE_VERSION).i386.rpm out/$(RELEASE_VERSION)/websocketd.$(RELEASE_VERSION).x86_64.rpm\n\nbinaries: $(BINARIES)\n\nbuild: out/$(RELEASE_VERSION)/CHECKSUMS\n\nout/$(RELEASE_VERSION)/CHECKSUMS: $(BINARIES) $(ZIPS) $(DEBS) $(RPMS)\n\tsha256sum $^ | sed -e 's/out\\/$(RELEASE_VERSION)\\///' >$@\n\n\n\nBASEFPM=--description \"WebSockets server that converts STDIO scripts to powerful HTML5 applications.\" --url http://websocketd.com/ --license MIT  --vendor \"websocketd team <joe@walnes.com>\" --maintainer \"abc@alexsergeyev.com\"\n\nDEBFPM=\"\"\nRPMFPM=--rpm-os linux\n\ndeb: $(DEBS)\n\nrpm: $(RPMS)\n\n\nout/$(RELEASE_VERSION)/websocketd-$(RELEASE_VERSION)_i386.deb: $(GO_UNPACKED) out/$(RELEASE_VERSION)/linux_386/websocketd\n\tmkdir -p out/$(RELEASE_VERSION)/deb32/{usr/bin,usr/share/man/man1,usr/share/doc/websocketd-$(RELEASE_VERSION)}\n\tcp out/$(RELEASE_VERSION)/linux_386/websocketd out/$(RELEASE_VERSION)/deb32/usr/bin/\n\tcp ../{LICENSE,AUTHORS,CHANGES,README.md} out/$(RELEASE_VERSION)/deb32/usr/share/doc/websocketd-$(RELEASE_VERSION)\n\tcat websocketd.man | gzip > out/$(RELEASE_VERSION)/deb32/usr/share/man/man1/websocket.1.gz\n\tfpm -f -s dir -t deb -a i386 -n websocketd -v $(RELEASE_VERSION) -C out/$(RELEASE_VERSION)/deb32/ -p out/$(RELEASE_VERSION)/websocketd-VERSION_ARCH.deb --deb-no-default-config-files $(BASEFPM) $(DEB_FPM) usr/\n\trm -rf out/$(RELEASE_VERSION)/deb32/\n\nout/$(RELEASE_VERSION)/websocketd-$(RELEASE_VERSION)_amd64.deb:  $(GO_UNPACKED) out/$(RELEASE_VERSION)/linux_amd64/websocketd\n\tmkdir -p out/$(RELEASE_VERSION)/deb64/{usr/bin,usr/share/man/man1,usr/share/doc/websocketd-$(RELEASE_VERSION)}\n\tcp out/$(RELEASE_VERSION)/linux_amd64/websocketd out/$(RELEASE_VERSION)/deb64/usr/bin/\n\tcp ../{LICENSE,AUTHORS,CHANGES,README.md} out/$(RELEASE_VERSION)/deb64/usr/share/doc/websocketd-$(RELEASE_VERSION)\n\tcat websocketd.man | gzip > out/$(RELEASE_VERSION)/deb64/usr/share/man/man1/websocket.1.gz\n\tfpm -f -s dir -t deb -a amd64 -n websocketd -v $(RELEASE_VERSION) -C out/$(RELEASE_VERSION)/deb64/ -p out/$(RELEASE_VERSION)/websocketd-VERSION_ARCH.deb --deb-no-default-config-files $(BASEFPM) $(DEB_FPM) usr/\n\trm -rf out/$(RELEASE_VERSION)/deb64/\n\nout/$(RELEASE_VERSION)/websocketd.$(RELEASE_VERSION).x86_64.rpm: $(GO_UNPACKED) out/$(RELEASE_VERSION)/linux_amd64/websocketd\n\tmkdir -p out/$(RELEASE_VERSION)/rpm64/{usr/bin,etc/default,usr/share/man/man1,usr/share/doc/websocketd-$(RELEASE_VERSION)}\n\tcp out/$(RELEASE_VERSION)/linux_amd64/websocketd out/$(RELEASE_VERSION)/rpm64/usr/bin/\n\tcp ../{LICENSE,AUTHORS,CHANGES,README.md} out/$(RELEASE_VERSION)/rpm64/usr/share/doc/websocketd-$(RELEASE_VERSION)\n\tcat websocketd.man | gzip > out/$(RELEASE_VERSION)/rpm64/usr/share/man/man1/websocket.1.gz\n\tfpm -f -s dir -t rpm -a x86_64 -n websocketd -v $(RELEASE_VERSION) -C out/$(RELEASE_VERSION)/rpm64/ -p out/$(RELEASE_VERSION)/websocketd.VERSION.ARCH.rpm $(BASEFPM) $(RPMFPM) usr/\n\trm -rf out/$(RELEASE_VERSION)/rpm64/\n\nout/$(RELEASE_VERSION)/websocketd.$(RELEASE_VERSION).i386.rpm: $(GO_UNPACKED) out/$(RELEASE_VERSION)/linux_386/websocketd\n\tmkdir -p out/$(RELEASE_VERSION)/rpm32/{usr/bin,etc/default,usr/share/man/man1,usr/share/doc/websocketd-$(RELEASE_VERSION)}\n\tcp out/$(RELEASE_VERSION)/linux_386/websocketd out/$(RELEASE_VERSION)/rpm32/usr/bin/\n\tcp ../{LICENSE,AUTHORS,CHANGES,README.md} out/$(RELEASE_VERSION)/rpm32/usr/share/doc/websocketd-$(RELEASE_VERSION)\n\tcat websocketd.man | gzip > out/$(RELEASE_VERSION)/rpm32/usr/share/man/man1/websocket.1.gz\n\tfpm -f -s dir -t rpm -a i386 -n websocketd -v $(RELEASE_VERSION) -C out/$(RELEASE_VERSION)/rpm32/ -p out/$(RELEASE_VERSION)/websocketd.VERSION.ARCH.rpm $(BASEFPM) $(RPMFPM) usr/\n\trm -rf out/$(RELEASE_VERSION)/rpm32/\n\n\n# Clean up\nclobber: clean\n\trm -rf $(GO_DIR)\n\n\nclean:\n\trm -rf out\n\n.PHONY: all build deb rpm localgo clobber clean\n"
  },
  {
    "path": "release/README",
    "content": "Release scripts for websocketd\n==============================\n\nPerform a fully automated repeatable release of websocketd.\n\nThis is three-stage process in normal release cycle that's performed by\nrepository maintainers.\n\n* Update CHANGES and Tag\n* Build\n* Release\n\nThose that do not have permissions to push tags could still use this build\nchain to build their own customized versions or packages.\n\n## Step 1: Update CHANGES and Tag\n\nFirst edit and commit CHANGES file. List all significant updates between releases.\n\nAnnotated tags require for release:\n\n    git tag -a vx.x.x\n\nTo see currently tagged version run tag with -l option.\n\n## Step 2: Build\n\nrelease/Makefile contains all required to download pre-verified for build release of Go and cross-compile\nwebsocketd for all platforms that we release for.\n\n    cd release\n    make\n\nIs generally recommended but other build options are available:\n\n* go-compile: build cross-platform golang pakages\n* binaries: only create websocketd binaries, do not generate zip distributions\n* deb, rpm: only create particular kind of packages\n* clean-go: remove build files for go\n* clean-out: remove built binaries, zip and linux packages\n* clean: remove everything (go and release files)\n\nBuilding requires to have gcc and other build tools installed. Building rpm/deb files would fail without fpm (\"gem install fpm\" itself requires ruby devel tools).\n\nCreate CHECKSUMS.asc file using gpg and key that other developers shared with you:\n\n    gpg -u KEYID --clearsign CHECKSUMS\n\n(key has to be installed in your gpg environment first)\n\n\n## Step 3: Release\n\nIn order to make release official following steps required:\n\n    # push tags (assuming joewalnes repo is origin):\n    go push --tags origin master:master\n\nUpload files to github release (to be automated). Use these instructions for now: https://github.com/blog/1547-release-your-software\n\n\n"
  },
  {
    "path": "release/websocketd.man",
    "content": ".\\\" Manpage for websocketd.\n.\\\" Contact abc@alexsergeyev.com to correct errors or typos.\n.TH websocketd 8 \"28 Sep 2014\" \"0.0\" \"websocketd man page\"\n.SH NAME\nwebsocketd \\- turns any program that uses STDIN/STDOUT into a WebSocket server.\n.SH SYNOPSIS\nwebsocketd [options] COMMAND [command args]\n\nor\n\nwebsocketd [options] --dir=SOMEDIR\n.SH DESCRIPTION\n\\fBwebsocketd\\fR is a command line tool that will allow any executable program\nthat accepts input on stdin and produces output on stdout to be turned into\na WebSocket server.\n\nTo learn more about websocketd visit \\fIhttp://websocketd.com\\fR and project WIKI\non GitHub!\n.SH OPTIONS\nA summary of the options supported by websocketd is included below.\n.PP\n\\-\\-port=PORT\n.RS 4\nHTTP port to listen on.\n.RE\n.PP\n\\-\\-address=ADDRESS\n.RS 4\nAddress to bind to (multiple options allowed). Use square brackets to specify IPv6 address. Default: \"\" (all)\n.RE\n.PP\n\\-\\-sameorigin={true,false}\n.RS 4\nRestrict (HTTP 403) protocol upgrades if the Origin header does not match to requested HTTP Host. Default: false.\n.RE\n.PP\n--origin=host[:port][,host[:port]...]\n.RS 4\nRestrict (HTTP 403) protocol upgrades if the Origin header does not match to one of the host and port combinations listed. If the port is not specified, any port number will match.  Default: \"\" (allow any origin)\n.RE\n.PP\n\\-\\-ssl \\-\\-sslcert=FILE \\-\\-sslkey=FILE\n.RS 4\nListen for HTTPS socket instead of HTTP. All three options must be used or all of them should be omitted.\n.RE\n.PP\n\\-\\-passenv VAR[,VAR...]\n.RS 4\nLists environment variables allowed to be passed to executed scripts.\n.RE\n.PP\n\\-\\-reverselookup={true,false}\n.RS 4\nPerform DNS reverse lookups on remote clients. Default: true\n.RE\n.PP\n\\-\\-dir=DIR\n.RS 4\nAllow all scripts in the local directory to be accessed as WebSockets. If using this, option, then the standard program and args options should not be specified.\n.RE\n.PP\n\\-\\-staticdir=DIR\n.RS 4\nServe static files in this directory over HTTP.\n.RE\n.PP\n\\-\\-cgidir=DIR\n.RS 4\nServe CGI scripts in this directory over HTTP.\n.RE\n.PP\n\\-\\-help\n.RS 4\nPrint help and exit.\n.RE\n.PP\n\\-\\-version\n.RS 4\nPrint version and exit.\n.RE\n.PP\n\\-\\-license\n.RS 4\nPrint license and exit.\n.RE\n.PP\n\\-\\-devconsole\n.RS 4\nEnable interactive development console. This enables you to access the websocketd server with a web-browser and use a user interface to quickly test WebSocket endpoints. For example, to test an endpoint at ws://[host]/foo, you can visit http://[host]/foo in your browser. This flag cannot be used in conjunction with \\-\\-staticdir or \\-\\-cgidir.\n.RE\n.PP\n\\-\\-loglevel=LEVEL\n.RS 4\nLog level to use (default access). From most to least verbose: debug, trace, access, info, error, fatal\n.RE\n.SH SEE ALSO\n.RS 2\n* full documentation at \\fIhttp://websocketd.com\\fR\n.RE\n.RS 2\n* project source at \\fIhttps://github.com/joewalnes/websocketd\\fR\n.RE\n.SH BUGS\nThe only known condition so far is that certain applications in programming languages that enforce implicit STDOUT buffering (Perl, Python, etc.) would be producing unexpected data passing\ndelays when run under \\fBwebsocketd\\fR. Such issues could be solved by editing the source code of those applications (prohibiting buffering) or modifying their environment to trick them\ninto autoflush mode (e.g. pseudo-terminal wrapper \"unbuffer\").\n\nActive issues in development are discussed on GitHub: \\fIhttps://github.com/joewalnes/websocketd/issues\\fR.\n\nPlease use that page to share your concerns and ideas about \\fBwebsocketd\\fR, authors would greatly appreciate your help!\n.SH AUTHOR\nCopyright 2013-2014 Joe Walnes and the websocketd team. All rights reserved.\n\nBSD license: Run 'websocketd \\-\\-license' for details.\n"
  },
  {
    "path": "version.go",
    "content": "// Copyright 2013 Joe Walnes and the websocketd team.\n// All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n)\n\n// This value can be set for releases at build time using:\n//   go {build|run} -ldflags \"-X main.version 1.2.3 -X main.buildinfo timestamp-@githubuser-platform\".\n// If unset, Version() shall return \"DEVBUILD\".\nvar version string = \"DEVBUILD\"\nvar buildinfo string = \"--\"\n\nfunc Version() string {\n\treturn fmt.Sprintf(\"%s (%s %s-%s) %s\", version, runtime.Version(), runtime.GOOS, runtime.GOARCH, buildinfo)\n}\n"
  }
]