{"id":13466801,"url":"https://github.com/jstrieb/quickserv","last_synced_at":"2025-04-07T19:17:07.437Z","repository":{"id":36030563,"uuid":"188740773","full_name":"jstrieb/quickserv","owner":"jstrieb","description":"Dangerously user-friendly web server for quick prototyping and hackathons","archived":false,"fork":false,"pushed_at":"2022-11-25T19:40:45.000Z","size":1425,"stargazers_count":328,"open_issues_count":1,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-31T16:21:33.710Z","etag":null,"topics":["cgi","cgi-bin","cross-platform","go","golang","hci","http","http-server","web","webserver","webserver-setup"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jstrieb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-05-26T23:16:11.000Z","updated_at":"2025-02-19T20:49:11.000Z","dependencies_parsed_at":"2023-01-16T12:04:30.464Z","dependency_job_id":null,"html_url":"https://github.com/jstrieb/quickserv","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstrieb%2Fquickserv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstrieb%2Fquickserv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstrieb%2Fquickserv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jstrieb%2Fquickserv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jstrieb","download_url":"https://codeload.github.com/jstrieb/quickserv/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247713258,"owners_count":20983683,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cgi","cgi-bin","cross-platform","go","golang","hci","http","http-server","web","webserver","webserver-setup"],"created_at":"2024-07-31T15:00:50.178Z","updated_at":"2025-04-07T19:17:07.387Z","avatar_url":"https://github.com/jstrieb.png","language":"Go","readme":"\u003ca href=\"https://github.com/jstrieb/quickserv\"\u003e\u003cimg src=\"https://github.com/jstrieb/quickserv/blob/master/favicon.svg?raw=true\" width=\"125\" height=\"125\" align=\"left\"\u003e\u003c/a\u003e\n\n# **QuickServ** \n\n**Quick**, no-setup web **Serv**er\n\n\n# About\n\nQuickServ makes creating web applications [*dangerously*](#disclaimer) easy, no\nmatter what programming language you use. \n\nQuickServ is a dependency-free, statically-linked, single-file web server that:\n\n- Has sensible defaults \n- Prints helpful error messages directly to the console\n- Runs on any modern computer, with no setup or installation\n- Needs no configuration\n- Knows which files to run server-side, and which to serve plain\n- Works with any programming language that can `read` and `write`\n- Doesn't require understanding the intricacies of HTTP\n- Enables Cross Origin Request Sharing (CORS) by default\n- Works with or without the command line \n\nQuickServ brings the heady fun of the 1990s Internet to the 2020s. It is\ninspired by the [Common Gateway Interface\n(CGI)](https://en.wikipedia.org/wiki/Common_Gateway_Interface), but is much\neasier to set up and use. Unlike CGI, it works out of the box with no searching\nfor obscure log files, no learning how HTTP headers work, no fiddling with\npermission bits, no worrying about CORS, no wondering where to put your scripts,\nand no struggling with Apache `mod_cgi` configurations. \n\nUnlike with CGI, you don't have to know what anything from the previous\nparagraph means to use QuickServ.\n\n\u003c!-- I promise I'm not jaded about CGI or anything ;) --\u003e\n\nIt is perfect for:\n\n- Building hackathon projects without learning a web framework\n- Creating internal tools\n- Prototyping web applications using any language\n- Attaching web interfaces to scripts\n- Controlling hardware with Raspberry Pis on your local network\n- Trying out web development without being overwhelmed\n\n[QuickServ should not be used on the public Internet. It should only be used on\nprivate networks.](#disclaimer) \n\n\n# Get Started\n\nUsing QuickServ is as easy as downloading the program, dragging it to your\nproject folder, and double clicking to run. It automatically detects which files\nto execute, and which to serve directly to the user. \n\n## Windows\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to view details\u003c/summary\u003e\n\n1. [Download for\n   Windows](https://github.com/jstrieb/quickserv/releases/latest/download/quickserv_windows_x64.exe).\n\n1. Make a project folder and add files to it. For example, if Python is\n   installed, create a file called `test.py` in the project folder containing:\n\n   ``` python\n   #!python\n\n   # Put your code here. For example:\n   import random\n   print(random.randint(0, 420))\n   ```\n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/windows_2.png\" width=\"75%\" align=\"center\"\u003e\n   \u003c/div\u003e\n\n   Since `test.py` starts with `#!something`, where `something test.py` is the\n   command to execute the file, QuickServ will know to run it. If QuickServ is\n   not running your file, make sure to add this to the beginning. \n   \n   On Windows, QuickServ also knows to automatically run files that end in\n   `.exe` and `.bat`. Any other file type needs to start with `#!something` if\n   it should be run.\n\n1. Move the downloaded `quickserv_windows_x64.exe` file to the project folder.\n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/windows_3.png\" width=\"75%\" align=\"center\"\u003e\n   \u003c/div\u003e\n\n1. Double click `quickserv_windows_x64.exe` in the project folder to start\n   QuickServ. Allow access through Windows Defender if prompted.\n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/windows_4_1.png\" width=\"75%\" align=\"center\"\u003e\u003cbr\u003e\u003cbr\u003e\n\n   \u003cimg src=\"doc/windows_4_2.png\" width=\"90%\" align=\"center\"\u003e\n   \u003c/div\u003e\n\n1. Go to \u003chttp://127.0.0.1:42069\u003e (or the address shown by QuickServ) to connect\n   to your web application. In the example, to run `test.py`, go to\n   \u003chttp://127.0.0.1:42069/test.py\u003e.\n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/windows_5.png\" width=\"75%\" align=\"center\"\u003e\n   \u003c/div\u003e\n\n\u003c/details\u003e\n\n## Mac\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to view details\u003c/summary\u003e\n\n1. Download the right version for your computer. If necessary, [check what type\n   of processor your Mac\n   has](https://www.howtogeek.com/706226/how-to-check-if-your-mac-is-using-an-intel-or-apple-silicon-processor/).\n   You will have to unzip the files after you download them.\n   - [Download for\n     Intel](https://github.com/jstrieb/quickserv/releases/latest/download/quickserv_macos_x64.zip).\n   - [Download for Apple\n     Silicon](https://github.com/jstrieb/quickserv/releases/latest/download/quickserv_macos_arm64.zip).\n\n1. Make a project folder and add files to it. For example, if Python is\n   installed, create a file called `test.py` in the project folder containing:\n\n   ``` python\n   #!python\n\n   # Put your code here. For example:\n   import random\n   print(random.randint(0, 420))\n   ```\n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/macos_2_2.png\" width=\"75%\" align=\"center\"\u003e\n   \u003c/div\u003e\n   \n   If you are making the file with TextEdit, you will need to go into `Format \u003e\n   Make Plain Text` to save the file in the correct format. \n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/macos_2_1.png\" width=\"75%\" align=\"center\"\u003e\n   \u003c/div\u003e\n\n   Since `test.py` starts with `#!something`, where `something test.py` is the\n   command to execute the file, QuickServ will know to run it. If QuickServ is\n   not running your file, make sure to add this to the beginning. \n   \n   On Mac, QuickServ also knows to automatically run files that have been\n   compiled. Any other file type needs to start with `#!something` if it should\n   be run.\n\n1. Move the downloaded `quickserv_macos_x64` or `quickserv_macos_arm64` file to\n   the project folder.\n\n1. Right click `quickserv_macos_x64` or `quickserv_macos_arm64` in the project\n   folder and select \"Open.\" Then, press \"Open\" in the confirmation dialog box. \n   After running it this way once, you will be able to start QuickServ by simply\n   double-clicking the file.\n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/macos_4_1.png\" width=\"75%\" align=\"center\"\u003e\u003cbr\u003e\u003cbr\u003e\n   \u003cimg src=\"doc/macos_4_2.png\" width=\"30%\" align=\"center\"\u003e\u003cbr\u003e\u003cbr\u003e\n   \u003cimg src=\"doc/macos_4_3.png\" width=\"75%\" align=\"center\"\u003e\n   \u003c/div\u003e\n\n1. Go to \u003chttp://127.0.0.1:42069\u003e (or the address shown by QuickServ) to connect\n   to your web application. In the example, to run `test.py`, go to\n   \u003chttp://127.0.0.1:42069/test.py\u003e.\n\n   \u003cdiv align=\"center\"\u003e\n   \u003cimg src=\"doc/macos_5.png\" width=\"75%\" align=\"center\"\u003e\n   \u003c/div\u003e\n\n\u003c/details\u003e\n\n## Raspberry Pi\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to view details\u003c/summary\u003e\n\nIt's easiest to install and run via the command line. [Open the\nTerminal](https://projects.raspberrypi.org/en/projects/raspberry-pi-using/8).\n\nEnter the following commands. A password may be required for the first commands. \n\n``` bash\n# Download\nsudo curl \\\n    --location \\\n    --output /usr/local/bin/quickserv \\\n    https://github.com/jstrieb/quickserv/releases/latest/download/quickserv_raspi_arm\n\n# Make executable\nsudo chmod +x /usr/local/bin/quickserv\n\n# Make a project folder\nmkdir -p my/project/folder\n\n# Go to project folder\ncd my/project/folder\n\n# Add a test file \ncat \u003c\u003cEOF \u003e test.py\n#!python3\n\n# Put your code here. For example:\nimport random\nprint(random.randint(0, 420))\nEOF\n\n# Run QuickServ\nquickserv\n```\n\nGo to \u003chttp://127.0.0.1:42069\u003e (or the address shown by QuickServ) to connect to\nyour web application. For example, to run `test.py`, go to\n\u003chttp://127.0.0.1:42069/test.py\u003e.\n\n\u003c/details\u003e\n\n## Other Operating Systems\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to view details\u003c/summary\u003e\n\nClicking to run executables does not have consistent behavior across Linux\ndistros, so it's easiest to install and run via the command line. Depending \non  your computer's architecture, it may be necessary to change the filename \nat the end of the `curl` HTTP request URL below.\n\nSee all download options on the [releases\npage](https://github.com/jstrieb/quickserv/releases/latest).\n\n``` bash\n# Download\nsudo curl \\\n    --location \\\n    --output /usr/local/bin/quickserv \\\n    https://github.com/jstrieb/quickserv/releases/latest/download/quickserv_linux_x64\n\n# Make executable\nsudo chmod +x /usr/local/bin/quickserv\n\n# Make a project folder\nmkdir -p /my/project/folder\n\n# Go to project folder\ncd /my/project/folder\n\n# Add a test file \ncat \u003c\u003cEOF \u003e test.py\n#!python3\n\n# Put your code here. For example:\nimport random\nprint(random.randint(0, 420))\nEOF\n\n# Run QuickServ\nquickserv\n```\n\nGo to \u003chttp://127.0.0.1:42069\u003e (or the address shown by QuickServ) to connect to\nyour web application. For example, to run `test.py`, go to\n\u003chttp://127.0.0.1:42069/test.py\u003e.\n\nAlternatively, use the instructions below to compile from source.\n\n\u003c/details\u003e\n\n## Compile From Source\n\n\u003cdetails\u003e\n\n\u003csummary\u003eClick to view details\u003c/summary\u003e\n\nCompile and install from source using the following command. A version of Go\ngreater than 1.16 is required because of the dependency on embedded filesystems.\n\n``` bash\ngo install github.com/jstrieb/quickserv@latest\n```\n\nThen create your project folder, populate it, and run QuickServ.\n\n``` bash\n# Make a project folder\nmkdir -p /my/project/folder\n\n# Go to project folder\ncd /my/project/folder\n\n# Add a test file \ncat \u003c\u003cEOF \u003e test.py\n#!python3\n\n# Put your code here. For example:\nimport random\nprint(random.randint(0, 420))\nEOF\n\n# Run QuickServ\nquickserv\n```\n\n\u003c/details\u003e\n\n\n# Tutorial\n\nTo demonstrate key features of QuickServ, we will build a simple web\napplication to perform addition. The code will not follow best practices, but\nit will show how little is needed to get started building with QuickServ.\n\nFirst, we create create a project folder and drag the QuickServ executable into\nthe folder, as in the [getting started](#get-started) steps.\n\nNext, inside the folder, we save the following text as `index.html`:\n\n``` html\n\u003cform action=\"/calculate\"\u003e\n\u003cinput name=\"first\" type=\"number\"\u003e + \u003cinput name=\"second\" type=\"number\"\u003e = ???\n\u003cbr\u003e\n\u003cbutton\u003eCalculate\u003c/button\u003e\n\u003c/form\u003e\n```\n\nThis code submits two variables to the `/calculate` page. In the browser, it\nlooks like this:\n\n\u003cdiv align=\"center\"\u003e\n\u003cimg src=\"doc/add.png\" width=\"75%\" align=\"center\"\u003e\n\u003c/div\u003e\n\nThen, we create a folder called `calculate` inside the project folder. Inside\nthe `calculate` folder, we save the following code as `index.py`. The name\n`index.whatever` tells QuickServ to run this file when a user visits\n`http://website/calculate` instead of needing them to visit\n`http://website/calculate/index.py`.\n\nPay special attention to the code comments. They highlight a number of\nimportant QuickServ features.\n\n``` python\n#!python3\n\n# Each QuickServ script must begin with a line like the one above so that\n# QuickServ knows how to run the file. This line tells QuickServ that I would\n# type `python3 this_file.py` to run this file at the command prompt. For\n# example, if you wanted to do `julia this_file.py` instead, then you would\n# make the first line of `this_file.py` be `#!julia`.\n#\n# Since we just want QuickServ to show the HTML code to the user and not run\n# it, index.html does not begin with this. The first line is only required when\n# QuickServ has to run the code.\n\nimport argparse\n\n# All HTML form values get turned into command line arguments. The names are\n# formatted like \"--name\" and the value comes right after the name.\nparser = argparse.ArgumentParser()\nparser.add_argument(\"--first\", type=int, required=True)\nparser.add_argument(\"--second\", type=int, required=True)\nargs = parser.parse_args()\n\n# Print the result -- anything printed out goes right to the user. In this\n# case, the output is text. But you can print anything and QuickServ will guess\n# the file type. Even printing the contents of image and video files works.\nprint(args.first + args.second)\n```\n\nNow double click QuickServ in your project folder and try it out in your\nbrowser. That's it!\n\nSee the examples linked in the next section for more QuickServ demonstrations.\nRead more details in the [How it Works](#how-it-works) section, and in the code\nitself. The [Advanced](#advanced) section has additional information about the\nenvironment QuickServ sets up for executables it runs.\n\n\n# Examples\n\nAll examples are located in the `examples` folder, which is a Git submodule\nconnected to the\n[jstrieb/quickserv-examples](https://github.com/jstrieb/quickserv-examples)\nrepo. Go to that repo for more information on how to run the examples.\n\nSome highlights:\n\n\u003ctable\u003e\n\n\u003ctr\u003e\n\u003cth\u003eSummary\u003c/th\u003e\n\u003cth\u003eLanguage\u003c/th\u003e\n\u003cth\u003eWindows\u003c/th\u003e\n\u003cth\u003eMac\u003c/th\u003e\n\u003cth\u003eLinux\u003c/th\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/youtube-gif\"\u003eYouTube to GIF\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003eShell\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✗\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/bloom-filter\"\u003eCreate Bloom filters\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003eC\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/movie-list\"\u003eSynchronized movie list\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003eTypeScript\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/brainfuck\"\u003eBrainfuck interpreter\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003ex86-64 Assembly\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✗\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✗\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/mandelbrot\"\u003eMandelbrot zoom\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003eShell\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/crypto-dashboard\"\u003eCryptocurrency dashboard\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003eGo\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/pdf-tools\"\u003ePDF tools\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003ePython\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✗\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e\u003ca href=\"https://github.com/jstrieb/quickserv-examples/blob/master/typeset-markdown\"\u003eTypeset Markdown as PDF\u003c/a\u003e\u003c/td\u003e\n\u003ctd\u003eStandard ML\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003ctd\u003e\u003cdiv align=\"center\"\u003e✓\u003c/div\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003c/table\u003e\n\n\n# How It Works\n\nAll of the QuickServ code lives in\n[`quickserv.go`](https://github.com/jstrieb/quickserv/blob/master/quickserv.go).\nThis well-commented file is about 700 lines long, and should take an experienced\nprogrammer with no Golang familiarity at most an hour to read.\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to view details\u003c/summary\u003e\n\nQuickServ has two main parts. The first is an initialization procedure, run\nexactly once at startup. The second is a handler function, called every time a\nuser makes an HTTP request to the server.\n\n## Initialization Routine\n\nWhen QuickServ starts up, it checks for command-line configuration flags, opens\na log file if one is passed with `--logfile` (otherwise it logs to the standard\noutput), and changes directories if a working directory is passed with `--dir`.\nNote that the log file path is relative to the current working directory, not\nrelative to the one passed with `--dir`.\n\nNext, QuickServ scans the working directory for files to run. It prints all of\nthe files that will be executed. This behavior is useful for determining if\nQuickServ recognizes a script as executable. It also prints helpful information\nfor the user such as the web address to visit to access the server, and what\nfolder the server is running in, as well as how to stop it.\n\nIf any part of the initialization fails, an error is reported. In the event of a\nfatal error, QuickServ waits for user input before quitting. This way, a user\nwho double-clicks the executable (as opposed to starting it from the command\nline) does not have a window appear and then immediately disappear, flashing too\nquickly for the error to be read.\n\nError messages are purposefully written with as little technical jargon as\npossible, though some is unavoidable. Likely causes for the errors are also\nincluded in error messages, so that they are easier for users to identify and\nfix.\n\nAs the last step in the initialization procedure, QuickServ starts a web server\nwith a single handler function for all requests. The server listens on the\ndefault port of `42069`, or on a random port if a user specified the\n`--random-port` command-line flag. A random port would be desirable if the user\nhas to show a project built with QuickServ to someone humorless, for example.\n\n## Request Handler\n\nWhen a user visits a web page, QuickServ handles the request by calling the lone\nhandler function.\n\nFirst, this function tries to open the file the user requested. If it cannot\nfind or open the file, it tries to serve a default version of the file. For\nexample, there is an embedded, default `favicon.ico` that gets served. If there\nis no default file matching the path, it lets the built-in Go fileserver handle\nthe error and respond with a 404 error code.\n\nIf the file the user requested is present, it checks whether it is a directory.\nIf it is a directory, QuickServ looks inside for a file named `index.xxx` where\n`xxx` is any file extension. If an index file is found, the index is served (and\npossibly executed) as if it were the original page requested. Otherwise, the\nuser must have requested a directory without a default index, so QuickServ\nresponds with a listing of the other files in the directory.\n\nIf the file the user requested is present and not a directory (_i.e._, it is a\nregular file), QuickServ checks whether or not it is executable. If so, it\nexecutes the file it found. If not, it returns the raw file contents to the\nuser. In both cases, QuickServ will guess what filetype (and therefore which\n`mimetype`) to use for the response.\n\nThe technique for determining if a file is executable depends on the runtime\noperating system. On Windows, any file with a `.bat` or `.exe` extension is\nconsidered executable. On non-Windows systems, any file with the executable\npermission bit set is considered executable. On all systems, a file is\nexecutable if it has a valid pseudo-shebang at the beginning. The shebang must\nbe on the very first line, must begin with `#!`, and must be a valid command.\nFor example, both of the following are acceptable, assuming `python3` is\ninstalled and on the `PATH`:\n\n- `#!/usr/bin/python3`\n- `#!python3`\n\nTo execute a file, QuickServ either runs the file itself (if it is an `.exe` or\nhas the executable bit set), or it passes the file's path as the first argument\nto the executable listed in its shebang. The request body is passed to the\nprogram on standard input, and everything printed by the program on standard\noutput is used as the response body. Executed programs are neither responsible\nfor writing—nor able to write—HTTP response headers. \n\nAll parsed HTTP form variables (if the `Content-Type` is\n`x-www-form-urlencoded`) are also passed as command line arguments when the\nprogram is executed. This way, the user does not need to parse the variables\nthemselves.\n\nWhatever the executed program prints on standard error is logged by QuickServ,\nwhich means it gets printed in the console window by default. This is handy for\ndebugging. If the program terminates with a non-zero exit code, QuickServ\nresponds with a 500 internal server error. Otherwise it returns with a 200.\n\nIf the request is a URL-encoded POST request with form data, QuickServ\nURL-decodes all of the characters except for three symbols: `%`, `\u0026`, and `=`.\nThe user is responsible for substituting these. Note that it is important to\nalways URL-decode `%` last in the program that processes the form data.\n\n\u003c/details\u003e\n\n\n# Disclaimer\n\nDo not run QuickServ on the public Internet. Only run it on private networks.\n\nQuickServ is not designed for production use. It was not created to be fast or\nsecure. Using QuickServ in production puts your users and yourself at risk,\nplease do not do it.\n\nQuickServ lets people build dangerously insecure things. It does not sanitize\nany inputs or outputs. It uses one process per request, and is susceptible to a\ndenial of service attack. Its security model presumes web users are trustworthy.\nThese characteristics make prototyping easier, but are not safe on the public\nInternet.\n\nTo deter using QuickServ in production, it runs on port `42069`. Hopefully that\nmakes everyone think twice before entering it into a reverse proxy or port\nforward config. For a more professional demo, the command-line flag\n`--random-port` will instead use a random port, determined at runtime.\n\nQuickServ is similar to the ancient CGI protocol. There are many\nwell-articulated, well-established [reasons that CGI is bad in\nproduction](https://www.embedthis.com/blog/posts/stop-using-cgi/stop-using-cgi.html),\nand they all apply to QuickServ in production.\n\n\n# Advanced\n\n## Command Line Options\n\nQuickServ has advanced options configured via command line flags. These\nchange how and where QuickServ runs, as well as where it saves its output.\n\n```\nUsage: \nquickserv [options]\n\nOptions:\n  --dir string\n        Folder to serve files from. (default \".\")\n  --logfile string\n        Log file path. Stdout if unspecified. (default \"-\")\n  --no-pause\n        Don't pause before exiting after fatal error.\n  --random-port\n        Use a random port instead of 42069.\n```\n\n## HTTP Headers \u0026 Environment Variables\n\nIn imitation of CGI, HTTP headers are passed to the executed program as\nenvironment variables. A header called `Header-Name` will be set as the\nenvironment variable `HTTP_HEADER_NAME`. \n\nThere is also a `REQUEST_TYPE` variable that specifies whether the request was\n`GET`, `POST`, *etc*.\n\n## Read From Standard Input\n\nHTTP requests with a body pass the body to the executed program on standard\ninput. In most cases, the request body is passed verbatim. This is not the case\nfor HTML forms.\n\nHTML form data can either be read from command line arguments, as in the\ntutorial, or parsed from standard input. Variables take the form\n\n```\nname=value\u0026othername=othervalue\n```\n\nThe simple addition example from the [tutorial](#tutorial) can be rewritten to\nparse HTTP form values from the standard input instead of from the command line\narguments.\n\n``` python\n#!python3\n\nimport sys\n\n\n# In the form input, \"=\" and \"\u0026\" determine where variables start and end. So if\n# they are literally included in the variable name or value, they must be\n# specially decoded. This code replaces every instance of the text on the left\n# with the text on the right to do the decoding:\n#     %3D -\u003e =\n#     %26 -\u003e \u0026\n#     %25 -\u003e %\n#\n# NOTE: Order matters! \"%\" must be decoded last. If not, it can mess with\n# decoding the others, since their encoded version uses \"%\"\ndef decode_characters(text):\n    text = text.replace(\"%3D\", \"=\")\n    text = text.replace(\"%26\", \"\u0026\")\n    text = text.replace(\"%25\", \"%\")\n    return text\n\nfirst = second = 0\n\n# Read all of the input into a variable. We are expecting the raw data to look\n# like:\n#       first=123\u0026second=456\ndata = sys.stdin.read()\n\n# The raw data looks like the above, so split it into pairs at each \"\u0026\"\npairs = data.split(\"\u0026\")\nfor pair in pairs:\n    # Each pair looks like the following, so split at each \"=\":\n    #       name=value\n    name, value = pair.split(\"=\")\n\n    # Decode any special characters (=, \u0026, %) now that we have split the\n    # variables up. This isn't necessary here since we're expecting numbers and\n    # not expecting any of those characters. But it matters a lot when a user\n    # could submit text with those characters\n    name = decode_characters(name)\n    value = decode_characters(value)\n\n    # If the name is what we're looking for, store the value for adding\n    if name == \"first\":\n        first = int(value)\n    elif name == \"second\":\n        second = int(value)\n\n# Print the result -- anything printed out goes right to the user. In this\n# case, the output is text. But you can print anything and QuickServ will try and\n# guess the file type.\nprint(first + second)\n```\n\n\n# Project Status \u0026 Contributing\n\nThis project is actively developed and maintained. If there are no recent\ncommits, it means that everything is running smoothly!\n\nPlease [open an issue](https://github.com/jstrieb/quickserv/issues/new) with any\nbugs, suggestions, or questions. This especially includes discussions about how\nto make error messages as clear as possible, and how to make the default\nsettings applicable to as many users as possible.\n\nPull requests without prior discussion will be ignored – don't waste time\nwriting code before confirming that it will be merged in. As a busy, lone\ndeveloper, it is easier to be responsive when all code contributions have\ncontext.\n\nIf you make a blog post, video, tutorial, hackathon project, or anything else\nusing QuickServ, please [open an\nissue](https://github.com/jstrieb/quickserv/issues/new) or message me on my\n[contact form](https://jstrieb.github.io/about#contact) so that I can link back\nto it!\n\n\n# Support the Project\n\nThere are a few ways to support the project:\n\n- Star the repository and follow me on GitHub\n- Share and upvote on sites like Twitter, Reddit, and Hacker News\n- Report any bugs, glitches, or errors that you find\n- Translate into other languages so everyone can use the project\n- Build and share your own projects made with QuickServ\n\nThese things motivate me to to keep sharing what I build, and they provide\nvalidation that my work is appreciated! They also help me improve the project.\nThanks in advance!\n\nIf you are insistent on spending money to show your support, I encourage you to\ninstead make a generous donation to one of the following organizations. By\nadvocating for Internet freedoms, organizations like these help me to feel\ncomfortable releasing work publicly on the Web.\n\n- [Electronic Frontier Foundation](https://supporters.eff.org/donate/)\n- [Signal Foundation](https://signal.org/donate/)\n- [Mozilla](https://donate.mozilla.org/en-US/)\n- [The Internet Archive](https://archive.org/donate/index.php)\n\n\n# Acknowledgments\n\nThis project would not be possible without the help of:\n\n- [Logan Snow](https://github.com/lsnow99)\n- [Amy Liu](https://www.linkedin.com/in/amyjl/)\n- Hacker News user [rchaves](https://news.ycombinator.com/user?id=rchaves), who\n  helpfully [suggested passing parsed form values as command line\n  arguments](https://news.ycombinator.com/item?id=29005407)\n- Everyone who [supports the project](#support-the-project)\n","funding_links":[],"categories":["Go"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjstrieb%2Fquickserv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjstrieb%2Fquickserv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjstrieb%2Fquickserv/lists"}