WebFund 2024F Lecture 5

From Soma-notes

Video

Video from the lecture for September 24, 2024 is now available:

Notes

Lecture 5
---------

Yes we're a lecture behind, will update the wiki!

Reminder - you are responsible for the material on the assignments on the midterm and final. Lecture materials are to help but will cover more than is on the tutorials and assignments.

When connecting to an openstack VM via SSH, make sure you are on the Carleton VPN for the easiest experience.

You can also do it with using access.scs.carleton.ca as a jumphost, but that is a bit tricker. Do the VPN version first.

If it hangs, you probably are blocked by a firewall somewhere, probably the openstack one.

What is localhost?
 - it is always IP address 127.0.0.1
 - it is the name and IP address of the computer you are using
 - (kind of like "self" in an object oriented language)

Note that the OpenStack firewall is very restrictive
 - I specifically added an exception to allow port 8000 through

When I say firewall, it is a mechanism or host that filters network traffic
 - allows ssh and web traffic, but not email for example
 - openstack has a very strict firewall
 - the Carleton network has a more permissive firewall



When you start Deno.serve(), it is listening on port 8000 (or whatever port you specify) for incoming HTTP traffic.
 - when it receives a request, it calls the supplied handler function, giving it that request as an argument
 - that handler function should process the request, returning a Response object representing the page that should be sent back to the browser

Make sure to always use === for equality in JavaScript, never == as == has really weird semantics ('1' == 1 is true, '1' === 1 is false).


When to use "async function" rather than "function"
 - if you use await in your function, label it async
 - when it returns a promise

When do you call await on a function?
 - when it returns a promise and you want to resolve the promise

So what is a promise?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

But really, a promise is a way for a function to "promise" that it will do something at a later time.
 - key example: doing I/O
 - when you request to read a file, it won't finish immediately, will take some time for operating system to get the data
 - in synchronous programs, you "block" while the file is being read
     - the program is suspended until the operation is completed
 - we don't want to do this with a web server, because then you can't handle an incoming request while doing file I/O
 - promises allow us to, in effect, do multiple things at once by doing things asynchronously


We're going to talk a lot more about promises later, just wanted to give you an idea that funny things are happening underneath with async/await.

But trust me, this is better than callbacks.

with callbacks, when something happens async, you give it a function to call later. So when that function needs to be async again, it has to define another function to call later. So you get these nested function definitions that get very confusing to read.

I will show a bit more about this later, just know that despite promises being confusing they are much better.

Big thing to know about await
 - when you use it, your function will go to sleep, allowing
   other code to run
 - function will resume when the await'd function returns with its result

So, async/await facilitate concurrency

When to use let vs var:
 - var's scope is the entire function
 - let's scope is the block

Code

Modified version of simpleserver.js:

// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2024 Anil Somayaji
//
// simpleserver.js
// for COMP 2406 (Fall 2024), Carleton University
// 
// Initial version: Sept 18, 2024
//
// Originally inspired by Rod Waldhoff's tiny node.js webserver
//   https://github.com/rodw/tiny-node.js-webserver
//

import { contentType } from "jsr:@std/media-types";

function MIMEtype(filename) {

    // const MIME_TYPES = {
    //  'css': 'text/css',
    //  'gif': 'image/gif',
    //  'htm': 'text/html',
    //  'html': 'text/html',
    //  'ico': 'image/x-icon',
    //  'jpeg': 'image/jpeg',
    //  'jpg': 'image/jpeg',
    //  'js': 'text/javascript',
    //  'json': 'application/json',
    //  'png': 'image/png',
    //  'txt': 'text/text'
    // };

    var extension = "";
    
    if (filename) {
        extension = filename.slice(filename.lastIndexOf('.')+1).toLowerCase();
    }

    return contentType(extension) || "application/octet-stream";
};

async function handler(req) {

    var metadata = {
        status: 200,
        contentType: "text/plain"
    }

    var pathname = "." + new URL(req.url).pathname;
    var contents;

    if (pathname === "./") {
        console.log("Top-level directory requested, instead returning index.html.");
        pathname = "./index.html";
    }
    
    metadata.contentType = MIMEtype(pathname);

    try {
        contents = await Deno.readFile(pathname);
    } catch (e) {
        contents = null;
    }
    
    if (!contents) {
        contents = "File not found: " + pathname;
        metadata.contentType = "text/plain";
        metadata.status = 404;

        console.log("error on request for " + pathname);
    } else {
        console.log("returning " + pathname +
                    " of type " + metadata.contentType);
    }

    return new Response(contents, metadata);
}

Deno.serve(handler);