diff options
Diffstat (limited to 'content/cgi.md')
| -rw-r--r-- | content/cgi.md | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/content/cgi.md b/content/cgi.md new file mode 100644 index 0000000..5f293e9 --- /dev/null +++ b/content/cgi.md @@ -0,0 +1,210 @@ +--- +title: "Server-Side Scripting with CGI" +date: 2021-07-25 +tags: ['server'] +--- +The basic website tutorial here describes how to set up a static website +--- one that just serves HTML files saved on your server, and until you +change something manually, the same content will be served each time a +given page is requested. This is perfectly enough for most personal +website needs. This is how blogs should be implemented, instead of +relying on bloatware like WordPress! + +But sometimes you genuinely *do* need something more. You need your +website to serve different contents depending on the time, on who the +requester is, on the contents of a database, or maybe process user input +from a form. + +## CGI + +CGI, or the Common Gateway Interface, is a specification to allow you, +the server owner, to program your web server using pretty much any +programming language you might know. The specification is almost as old +as the Internet itself and for a long time CGI scripting was the primary +method of creating dynamic websites. + +CGI is a very simple specification indeed. You write a script in your +favorite language, the script receives input about the request in +environment variables, and whatever you print to the standard output +will be the response. Most likely, though, you will want to use a +library for your language of choice that makes a lot of this +request/response handling simpler (e.g. parsing query parameters for +you, setting appropriate headers, etc.). + +### Limitations of CGI + +While in theory you could implement any sort of functionality with CGI +scripts, it\'s going to get difficult managing a lot of separate scripts +if they\'re supposed to be working in tandem to implement a dynamic +website. If you want to build a full out web application, you\'d +probably be better off learning a web framework than gluing together +Perl scripts. + +That said, just as most of the web could be replaced with static +websites, much of the remaining non-static web could be replaced with a +few simple scripts, rather than bloated Ruby on Rails or Django +applications. + +## Let\'s write a CGI script! + +We\'ll implement a simple example CGI script. I\'ll use Ruby for this +tutorial, but you\'ll be able to follow along even if you don\'t know +Ruby, just treat it as pseudocode then find a CGI library for your +language. + +### The working example + +Our working example will be the Lazy Calculator. Yeah, you\'re probably +tired of seeing calculator examples in every programming tutorial, but +have you ever implemented one that takes the weekends off? + +Here\'s how it will work. When in a browser you submit a request to your +website like + +```txt +example.com/calculator.html?a=10&b=32 +``` + +you will receive a page with the result of the addition of 10 and 32: +42. + +*Unless* you send your request on a weekend. Then the website will +respond with + +```txt +I don't get paid to work on weekends! Come back Monday. +``` + +This example will show a few things that CGI scripts can do that you +wouldn\'t have been able to get using just file hosting in your web +server: + +- getting inputs from the user; +- getting external information (here just the system time, but you + could imagine instead connecting to a database); +- using the above to create dynamic output. + +### The code + +Here\'s an implementation of the lazy calculator as a Ruby CGI script: + +```ruby +#!/bin/env ruby + +require 'cgi' +require 'date' + +cgi = CGI.new +today = Date::today + +a = cgi["a"].to_i +b = cgi["b"].to_i + +if today.saturday? || today.sunday? + cgi.out do + "I don't get paid to work on weekends! Come back Monday." + end +else + cgi.out do + (a + b).to_s + end +end +``` + +Let\'s go through what\'s happening here. + +### The shebang line + +CGI works by pointing your web server to an executable program. A Ruby +or Python script by itself is not immediately executable by a computer. +But on Unix-like systems you can specify the program that will be able +to execute your file in its first line if it starts with `#!` (known as +the shebang; read more about it on +[Wikipedia](https://en.wikipedia.org/wiki/Shebang_(Unix))). + +So if you\'re going to be using a scripting language, you\'ll probably +need the appropriate shebang line at the top of your script. If you use +a compiled language, you\'ll just point your web server to the compiled +executable binary. + +### Query parameters + +The next interesting lines of code are where we set the variables `a` +and `b`. Here we are getting user inputs from the request. + +In the example request we mentioned above +(`example.com/calculator.html?a=10&b=32`), the part starting from the +question mark, `?a=10&b=32`, is the *query string*. This is how users +can submit parameters with their web requests. Usually these parameters +are set by e.g. a form on your website, but in our simple example we\'ll +be just manually manipulating the URL. + +The query string contains key-value pairs. The Ruby CGI library makes +them available in the `CGI` object it provides. We just need to index it +with the desired key, and we\'ll get the corresponding value. + +### Wrapping it up + +The remaining parts of the code should be pretty self-explanatory. We +get today\'s date, check if it\'s a Saturday or a Sunday, and depending +on that, we instruct the CGI library to output either the answer, or a +\"come back later\" message. + +The Ruby library by default returns an HTML response, so we really +should have wrapped our outputs in some `html`, `body`, etc. tags. +Alternatively, we could have specified that the response is just plain +text with + +```txt +cgi.out 'text/plain' do +``` + +In general, your CGI library will probably have ways of specifying all +sorts of HTTP response headers, like status code, content type, etc. + +## Making it work + +We have a CGI script, now let\'s point our web server to it. + +### Installing FastCGI + +If you\'re using Nginx, install `fcgiwrap`: + +```sh +apt install fcgiwrap +``` + +This installs the necessary packages for Nginx to use FastCGI --- a +layer between your web server and CGI script that allows for faster +handling of scripts than if the web server had to handle it all by +itself. + +Other web servers will probably have a similarly simple way of enabling +FastCGI, or you can look into other methods for launching CGI scripts. + +### Nginx configuration + +In the configuration file for your website, add something like the +following: + +```nginx +location /calculator.html { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME /usr/local/bin/lazy-calculator.rb; + fastcgi_param QUERY_STRING $query_string; + fastcgi_pass unix:/run/fcgiwrap.socket; +} +``` + +`fastcgi_param` directives specify various parameters for FastCGI. +`SCRIPT_FILENAME` should point to your executable. For `QUERY_STRING`, +we just copy Nginx\'s `$query_string` variable. You might want to pass +other information to your CGI script as well, see for example [the +Debian wiki](https://wiki.debian.org/nginx/FastCGI) for a more detailed +example, including pointing to an entire directory of CGI scripts, +rather than adding each one by hand to your web server config. + +## Contribution + +- Martin Chrzanowski \-- [website](https://m-chrzan.xyz), + [donate](https://m-chrzan.xyz/donate.html) |
