Skip to content

Commit 4bdcaed

Browse files
committed
Update README tutorial to use argv
1 parent 11f2350 commit 4bdcaed

File tree

1 file changed

+116
-61
lines changed

1 file changed

+116
-61
lines changed

README.md

Lines changed: 116 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -348,63 +348,41 @@ important QuickServ features.
348348
#!python3
349349

350350
# Each QuickServ script must begin with a line like the one above so that
351-
# QuickServ knows how to run the file. I would do `python3 this_file.py` to
352-
# run this file. The first line tells QuickServ to do it that way. But if
353-
# you wanted to do `julia my_file.jl` for example, then you would make the
354-
# first line of `my_file.jl` be `#!julia`.
351+
# QuickServ knows how to run the file. This line tells QuickServ that I would
352+
# type `python3 this_file.py` to run this file at the command prompt. For
353+
# example, if you wanted to do `julia this_file.py` instead, then you would make
354+
# the first line of `this_file.py` be `#!julia`.
355355
#
356-
# Since we just want QuickServ to show the HTML code to the user and not run
357-
# it, index.html does not begin with this.
356+
# Since we just want QuickServ to show the HTML code to the user and not run it,
357+
# index.html does not begin with this. The first line is only required when
358+
# QuickServ has to run the code.
358359

359360
import sys
360361

361362

362-
# In the form input, "=" and "&" determine where variables start and end. So if
363-
# they are literally included in the variable name or value, they must be
364-
# specially decoded. This code replaces every instance of the text on the left
365-
# with the text on the right to do the decoding:
366-
# %3D -> =
367-
# %26 -> &
368-
# %25 -> %
369-
#
370-
# NOTE: Order matters! "%" must be decoded last. If not, it can mess with
371-
# decoding the others, since their encoded version uses "%"
372-
def decode_characters(text):
373-
text = text.replace("%3D", "=")
374-
text = text.replace("%26", "&")
375-
text = text.replace("%25", "%")
376-
return text
377-
378363
first = second = 0
379364

380-
# Read all of the input into a variable. We are expecting the raw data to look
381-
# like:
382-
# first=123&second=456
383-
data = sys.stdin.read()
384-
385-
# The raw data looks like the above, so split it into pairs at each "&"
386-
pairs = data.split("&")
387-
for pair in pairs:
388-
# Each pair looks like the following, so split at each "=":
389-
# name=value
390-
name, value = pair.split("=")
391-
392-
# Decode any special characters (=, &, %) now that we have split the
393-
# variables up. This isn't necessary here since we're expecting numbers and
394-
# not expecting any of those characters. But it matters a lot when a user
395-
# could submit text with those characters
396-
name = decode_characters(name)
397-
value = decode_characters(value)
398-
399-
# If the name is what we're looking for, store the value for adding
400-
if name == "first":
401-
first = int(value)
402-
elif name == "second":
403-
second = int(value)
404-
405-
# Print the result -- anything printed out goes right to the user. In this
406-
# case, the output is text. But you can print anything and QuickServ will try and
407-
# guess the file type.
365+
# All HTML form values get turned into command line arguments. The names are
366+
# formatted like "--name" and the value comes right after the name. In Python,
367+
# command line arguments are stored in sys.arv. In our case, if we submit "123"
368+
# and "456" to the form, sys.argv looks like:
369+
#
370+
# ["index.py", "--first", "123", "--second", "456"]
371+
#
372+
# Loop over the command line arguments to find the HTML form values we want
373+
for i in range(len(sys.argv) - 1):
374+
name = sys.argv[i]
375+
value = sys.argv[i+1]
376+
377+
# If the name is what we're looking for, store the value for adding
378+
if name == "--first":
379+
first = int(value)
380+
elif name == "--second":
381+
second = int(value)
382+
383+
# Print the result -- anything printed out goes right to the user. In this case,
384+
# the output is text. But you can print anything and QuickServ will guess the
385+
# file type. Even printing the contents of image and video files works.
408386
print(first + second)
409387
```
410388

@@ -413,7 +391,8 @@ browser. That's it!
413391

414392
See the examples linked in the next section for more QuickServ demonstrations.
415393
Read more details in the [How it Works](#how-it-works) section, and in the code
416-
itself.
394+
itself. The [Advanced](#advanced) section has additional information about the
395+
environment QuickServ sets up for executables it runs.
417396

418397

419398
# Examples
@@ -590,6 +569,11 @@ program on standard input, and everything printed by the program on standard
590569
output is used as the response body. Executed programs are neither responsible
591570
for writing—nor able to write—HTTP response headers.
592571

572+
All parsed HTTP form variables (if the `Content-Type` is
573+
`x-www-form-urlencoded`) are also passed as command line arguments when the
574+
program is executed. This way, the user does not need to parse the variables
575+
themselves.
576+
593577
Whatever the executed program prints on standard error is logged by QuickServ,
594578
which means it gets printed in the console window by default. This is handy for
595579
debugging. If the program terminates with a non-zero exit code, QuickServ
@@ -630,6 +614,8 @@ and they all apply to QuickServ in production.
630614

631615
# Advanced
632616

617+
## Command Line Options
618+
633619
QuickServ has advanced options configured via command line flags. These
634620
change how and where QuickServ runs, as well as where it saves its output.
635621

@@ -648,20 +634,86 @@ Options:
648634
Use a random port instead of 42069.
649635
```
650636

637+
## HTTP Headers & Environment Variables
638+
639+
In imitation of CGI, HTTP headers are passed to the executed program as
640+
environment variables. A header called `Header-Name` will be set as the
641+
environment variable `HTTP_HEADER_NAME`.
642+
643+
There is also a `REQUEST_TYPE` variable that specifies whether the request was
644+
`GET`, `POST`, *etc*.
645+
646+
## Read From Standard Input
647+
648+
HTTP requests with a body pass the body to the executed program on standard
649+
input. In most cases, the request body is passed verbatim. This is not the case
650+
for HTML forms.
651+
652+
HTML form data can either be read from command line arguments, as in the
653+
tutorial, or parsed from standard input. Variables take the form
654+
655+
```
656+
name=value&othername=othervalue
657+
```
658+
659+
The simple addition example from the [tutorial](#tutorial) can be rewritten to
660+
parse HTTP form values from the standard input instead of from the command line
661+
arguments.
662+
663+
``` python
664+
#!python3
665+
666+
import sys
667+
668+
669+
# In the form input, "=" and "&" determine where variables start and end. So if
670+
# they are literally included in the variable name or value, they must be
671+
# specially decoded. This code replaces every instance of the text on the left
672+
# with the text on the right to do the decoding:
673+
# %3D -> =
674+
# %26 -> &
675+
# %25 -> %
676+
#
677+
# NOTE: Order matters! "%" must be decoded last. If not, it can mess with
678+
# decoding the others, since their encoded version uses "%"
679+
def decode_characters(text):
680+
text = text.replace("%3D", "=")
681+
text = text.replace("%26", "&")
682+
text = text.replace("%25", "%")
683+
return text
684+
685+
first = second = 0
686+
687+
# Read all of the input into a variable. We are expecting the raw data to look
688+
# like:
689+
# first=123&second=456
690+
data = sys.stdin.read()
651691

652-
<!--
653-
# Motivation & Philosophy
692+
# The raw data looks like the above, so split it into pairs at each "&"
693+
pairs = data.split("&")
694+
for pair in pairs:
695+
# Each pair looks like the following, so split at each "=":
696+
# name=value
697+
name, value = pair.split("=")
654698

655-
The idea came from spending way too much time getting set up during a hackathon
656-
with friends in college.
699+
# Decode any special characters (=, &, %) now that we have split the
700+
# variables up. This isn't necessary here since we're expecting numbers and
701+
# not expecting any of those characters. But it matters a lot when a user
702+
# could submit text with those characters
703+
name = decode_characters(name)
704+
value = decode_characters(value)
657705

658-
I started this project in C, but I finished it in Golang. It leans heavily on
659-
the Go standard library. Go's easy web server integration meant that I could
660-
spend most of my time optimizing the user experience. Thankfully, Go shoulders
661-
much of the complexity for the end-user.
706+
# If the name is what we're looking for, store the value for adding
707+
if name == "first":
708+
first = int(value)
709+
elif name == "second":
710+
second = int(value)
662711

663-
At home, I constantly use it to give my shell scripts simple web front-ends.
664-
-->
712+
# Print the result -- anything printed out goes right to the user. In this
713+
# case, the output is text. But you can print anything and QuickServ will try and
714+
# guess the file type.
715+
print(first + second)
716+
```
665717

666718

667719
# Project Status & Contributing
@@ -717,4 +769,7 @@ This project would not be possible without the help of:
717769

718770
- [Logan Snow](https://github.com/lsnow99)
719771
- [Amy Liu](https://www.linkedin.com/in/amyjl/)
772+
- Hacker News user [rchaves](https://news.ycombinator.com/user?id=rchaves), who
773+
helpfully [suggested passing parsed form values as command line
774+
arguments](https://news.ycombinator.com/item?id=29005407)
720775
- Everyone who [supports the project](#support-the-project)

0 commit comments

Comments
 (0)