@@ -348,63 +348,41 @@ important QuickServ features.
348
348
# !python3
349
349
350
350
# 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`.
355
355
#
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.
358
359
359
360
import sys
360
361
361
362
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
-
378
363
first = second = 0
379
364
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.
408
386
print (first + second)
409
387
```
410
388
@@ -413,7 +391,8 @@ browser. That's it!
413
391
414
392
See the examples linked in the next section for more QuickServ demonstrations.
415
393
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.
417
396
418
397
419
398
# Examples
@@ -590,6 +569,11 @@ program on standard input, and everything printed by the program on standard
590
569
output is used as the response body. Executed programs are neither responsible
591
570
for writing—nor able to write—HTTP response headers.
592
571
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
+
593
577
Whatever the executed program prints on standard error is logged by QuickServ,
594
578
which means it gets printed in the console window by default. This is handy for
595
579
debugging. If the program terminates with a non-zero exit code, QuickServ
@@ -630,6 +614,8 @@ and they all apply to QuickServ in production.
630
614
631
615
# Advanced
632
616
617
+ ## Command Line Options
618
+
633
619
QuickServ has advanced options configured via command line flags. These
634
620
change how and where QuickServ runs, as well as where it saves its output.
635
621
@@ -648,20 +634,86 @@ Options:
648
634
Use a random port instead of 42069.
649
635
```
650
636
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()
651
691
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(" =" )
654
698
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)
657
705
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)
662
711
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
+ ```
665
717
666
718
667
719
# Project Status & Contributing
@@ -717,4 +769,7 @@ This project would not be possible without the help of:
717
769
718
770
- [ Logan Snow] ( https://github.com/lsnow99 )
719
771
- [ 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 )
720
775
- Everyone who [ supports the project] ( #support-the-project )
0 commit comments