View script | License | Download documentation as: HTML or editable |
Download script | History | Other scripts by: sunanda |
[0.044] 18.402k
Documentation for: cookie-example.rCookie-exampleSunanda December 2003 {To be completed later] 1. IntroductionThis script is slightly more than an example of how to set and retrieve a cookie. In miniature, it shows most of the major things you need to do to create a website that can respond to specific users. These notes fill out some of the details that may not be obvious. Most of the code here is taken from actual code used to run REBOL.org, so this is all practical advice and no irrelevant theory. Contents1. Introduction 2. Notes about functions 2.1 send-output 3. Mainline processing notes2.2 clear-place-holders 2.3 defuse-cgi-field 2.4 read-cgi 2.5 cookie 2.6 read-user-record-from-cookie 2.7 write-user-record 2.8 Web-page 3.1 if error? cgi-error: try 4. Setting a cookie3.2 if any [none? cgi-input "" = cgi-input] 3.3 create object with all fields 2. Notes about functions2.1 send-outputTo write a webpage, you simply print HTML commands. But before the <HTML> there can be HTTP headers. In fact, there must be at least one: the content-type. For a standard HTML page, the content-type will be text/html. If you are displaying anything else (like a PDF), then you need a different content type. The headers end with a blank line -- hence the ^/ in the line: print "Content-type: text/html^/" 2.2 clear-place-holdersWe use a scheme with HTML templates that have place-holders-- places where we’ll insert values or other HTML. This function removes any left-over place-holders before we print the page. Our place-holders start and end with two exclamation marks. In retrospect, that wasn’t such a great idea. It would have been better to use some sort of pseudo-tag take like [user-name], but they would have been harder to clear. 2.3 defuse-cgi-fieldAny data that a user has entered via a form has to be treated with some suspicion. Validation is obvious -- if you are expecting a number -- you need to check that it is a number. How you do that safely is another matter. If you are going to redisplay the data, you need to make sure that it does not contain any HTML or Javascript commands. If it does, you could end up causing something unfortunate to happen in the user’s browser. Hence defusing the input fields. We convert the tag characters (greater-than and less than signs) to their equivalent numeric entities. They’ll display okay on any browser, but they won’t be treated as active formatting elements. How likely is it that someone will attack your website this way? Well, when our feedback form went live, we weren’t defusing the entered fields. When we (the Librarians) looked at our second ever feedback message, it contained an XSS (cross-system attack). It wasn’t very dangerous -- just popped up a Javascript dialog box. But it was an attack that defusing the input fields would have avoided. 2.4 read-cgiThis is a standard bit of code, published by RT somewhere in the Core Guide to CGI programming. It hides the complexity of get or post and returns a block with pairs of values for the CGI fields, eg: [field1: "value of field 1" field2: "value of field2"] That takes care of half of the complexity of getting your CGI fields into words for later validation and processing. But only half, as it can’t return fields that are not present. That is dealt with a bit further down in the sample script with code that would look like this in a more real example: cgi-object: make object! [field1: 20 field2: "No" field3: "update"] cgi-object: construct/with decode-cgi cgi-input cgi-object In the above, there are three CGI fields expected, and the object will contain them all, with default values for any that were not present in the input data. 2.5 cookieThis is a little function that either reads a cookie or sets it. More on cookies further down. 2.6 read-user-record-from-cookie2.7 write-user-record2.8 Web-page3. Mainline processing notes3.1 if error? cgi-error: tryThe whole of the code is wrapped in this error block. Without this, it is possible an error will leave the user with a blank screen or a server error message. Not very helpful, not very useful. The cgi-error handler on REBOL. org works this way too. In addition to displaying an apology message, it also writes all relevant details to an error log file (so the Library team can diagnose and fix the error) and sends an email alert so we know it’s happened. 3.2 if any [none? cgi-input "" = cgi-input]Read-cgi behaves slightly differently under different servers (or possibly on different platforms). This double test for none and for "" works on all platforms to detect that there were no CGI fields. 3.3 create object with all fieldsThis creates an object, mapping the CGI fields to the object. This is an easy way to access the CGI fields later. If we had more than one CGI field, they may not all be there for any one message. The easy way around that is to create the object with default values for all fields, and then map the CGI fields to it: cgi-object: make object! [ input-text: "" update-flag: "no" user-name: none" ] cgi-object: construct/with decode-cgi cgi-input cgi-object We can then access these fields, even if they did not occur in the input message: if cgi-object/update-flag = "yes" [....] if not none? cgi-object/user-name [....] That removes the need for the test check if required fields are present in this example. But you may have a case where it is important to distinguish between no field being present, and that field’s null value. 4. Setting a cookieA cookie is just some value that you create on the server and send to the browser (or other user agent). When a URL is sent by the browser to the same server it will supply the cookie (or cookies if you have set more than one) In this example, we are using the cookie to identify the user. So we need a unique value for each cookie. The example creates the unique value by doing a checksum on the current time. 4.1 Warning about the exampleThis is a very bad way to create a unique value:
4.2 Saving user dataWe use the cookie value as a direct key to a file. That file contains an object that holds all the "persistent" data for that user. |