WebFund 2015W: Assignment 9

From Soma-notes

This assignment has 15 points. For each of the following five HTTP GET and POST requests:

  • give a transcript of the HTTP request going out and the response returned by the ajax-notes server, as CAPTURED BY YOU using socat as discussed in Tutorial 9 (1 point),
  • briefly explain how the browser made this request (did the user type an address in the URL bar or did they interact with a page, and if they interacted with a page how did that page generate that HTTP request) (1 point), and
  • briefly explain what code the server ran to generate the response. (1 point)

In other words, you should be giving an HTTP request example and then explaining the browser context that generated the request and the server context that generated the response.

Submit your answers on cuLearn in a text or PDF file by 10 AM on Monday, March 30, 2015. No other formats will be accepted.

  1. GET /
  2. GET /javascripts/notes.js
  3. GET /getNotes
  4. POST /updateNote
  5. POST /newNote

Solutions

Note I inserted blank lines between the request and response in the transcripts below. I also deleted the socat request/response metadata that sometimes gets inserted just before the end of the previous request/response (e.g., the timestamp inserted before the closing > of the trailing <html> tag).

GET /

> 2015/04/05 13:06:41.964459  length=406 from=0 to=405
GET / HTTP/1.1\r
Host: localhost:3005\r
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0\r
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r
Accept-Language: en-US,en;q=0.5\r
Accept-Encoding: gzip, deflate\r
DNT: 1\r
Cookie: connect.sid=s%3A7VfppM1sNSPZH2ItZKhGrNFwEYtZ9hqe.j39zaf1VaOxBtKb48jUl%2BrxDauYO3IQSu%2BMaNlQgBz8\r
Connection: keep-alive\r
\r

< 2015/04/05 13:06:41.984748  length=188 from=0 to=187
HTTP/1.1 200 OK\r
X-Powered-By: Express\r
Content-Type: text/html; charset=utf-8\r
Content-Length: 588\r
ETag: W/"24c-32554b76"\r
Date: Sun, 05 Apr 2015 17:06:41 GMT\r
Connection: keep-alive\r
\r
< 2015/04/05 13:06:41.985227  length=587 from=188 to=774
<!DOCTYPE html>
<html>
  <head>
    <title>COMP 2406 AJAX Notes Demo</title>
    <link rel="stylesheet" href="/stylesheets/style.css">
    <script src="/javascripts/jquery-1.11.2.js"></script>
  </head>
  <body>
    <h1>COMP 2406 AJAX Notes Demo</h1>
    <p>Welcome to COMP 2406 AJAX Notes Demo</p>
    <p>Please log in</p>
    <div>
      <form action="/login" method="post">
        <div>
          <input type="text" name="username">
          <label for="username">Username</label>
        </div>
        <button type="submit">Login</button>
      </form>
    </div>
  </body>
</html>

The browser generated the GET / request because the user typed in (or had bookmarked) http://localhost:3000/ (or whatever port it is running on). Note that the user did not click on a link because of the lack of a Referer: header.

The server ran the callback previously registered by the call to "router.get('/', ...)", to generate this response. This function saw that the user was logged in and then called res.render() in order to convert index.jade to HTML and send it back.

GET /javascripts/notes.js

> 2015/04/05 13:25:30.458213  length=445 from=0 to=444
GET /javascripts/notes.js HTTP/1.1\r
Host: localhost:3005\r
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0\r
Accept: */*\r
Accept-Language: en-US,en;q=0.5\r
Accept-Encoding: gzip, deflate\r
DNT: 1\r
Referer: http://localhost:3005/notes\r
Cookie: connect.sid=s%3AInaH5BQtzrhgc1eCqzAq_Xvx5ZDZFm9k.glgLGfIdJ1ZTQv8jQ9GLVwVa3Tn%2BUp3Ws0JiTE6JeuY\r
Connection: keep-alive\r
Pragma: no-cache\r
Cache-Control: no-cache\r
\r

< 2015/04/05 13:25:30.468303  length=3761 from=0 to=3760
HTTP/1.1 200 OK\r
X-Powered-By: Express\r
Accept-Ranges: bytes\r
Date: Sun, 05 Apr 2015 17:25:30 GMT\r
Cache-Control: public, max-age=0\r
Last-Modified: Mon, 09 Mar 2015 01:05:38 GMT\r
ETag: W/"d90-56881568"\r
Content-Type: application/javascript\r
Content-Length: 3472\r
Connection: keep-alive\r
\r
$(function() {
    var insertNotesIntoDOM = function(userNotes) {
        var i;

        $(".notes").remove();
        for (i=0; i < userNotes.length; i++) {
            console.log("Adding note " + i);
            $("#notesList").append('<li class="notes"> <div>' + 
                                   userNotes[i].title + ": " +
                                   '<button type="button" class="editButton"' +
				   'id="edit' + i + '">' + '[edit]</button>' +
                                   '<p>' + userNotes[i].content + '</p>' +
                                   "</li>");
            $("#edit" + i).click(userNotes[i], editNote);
        }
    }

    var getAndListNotes = function() {
        $.getJSON("/getNotes", insertNotesIntoDOM);
    }

    var updateNoteOnServer = function(event) {
        var theNote = event.data;
        theNote.title = $("#noteTitle").val();
        theNote.content = $("#noteContent").val();
        noteListing();
        $.post("/updateNote",
               {"id": theNote._id,
                "title": theNote.title,
                "content": theNote.content},
               getAndListNotes);
    }

    var editNote = function(event) {
        var theNote = event.data;

        $(".notesArea").replaceWith('<div class="notesArea" ' +
                                    'id="editNoteDiv"></div>');
        $("#editNoteDiv").append('<h2>Edit Note</h2>');

        $("#editNoteDiv").append('<div><input type="text" ' +
                                 'id="noteTitle"></input></div>');
        $("#noteTitle").val(theNote.title);

        $("#editNoteDiv").append('<div><textarea cols="40" rows="5" ' +
                                 'id="noteContent" wrap="virtual">' +
                                 '</textarea></div>');
        $("#noteContent").val(theNote.content);


        $("#editNoteDiv").append('<button type="button" id="updateNote">' +
                                 'Update</button>')
        $("#updateNote").click(theNote, updateNoteOnServer);

        $("#editNoteDiv").append('<button type="button" id="cancelUpdate">' +
                                 'Cancel</button>')
        $("#cancelUpdate").click(noteListing);
    }
    
    var editNewNote = function(result) {
        var theNote = {"title": "",
                       "content": ""};

        if (result.indexOf("ERROR") == -1) {
            theNote._id = result;
            editNote({data: theNote});  //faking event object
        } else {
            // report that we couldn't create a note        
            noteListing();
        }
    }

    var newNote = function() {
        $.post("/newNote", editNewNote);
    }
    
    var noteListing = function() {
        $(".notesArea").replaceWith('<div class="notesArea" ' +
                                    'id="listNotesDiv">');

        $("#listNotesDiv").append('<h2>Notes</h2>');
        $("#listNotesDiv").append('<ul id="notesList">' +
                                  '<li class="notes">Loading Notes...</li>' +
                                  '</ul>');

        $("#listNotesDiv").append('<button id="newNote" type="button">' +
                                  'New Note</button>');
        $("#newNote").click(newNote);

        $("#listNotesDiv").append('<button id="refreshNotes" type="button">' +
                                  'Refresh</button>');
        $("#refreshNotes").click(getAndListNotes);

        getAndListNotes();
    }

    noteListing();
});

(Note that in order to prevent the notes.js response from getting mixed in with the jQuery response (which happened concurrently), I had to change layout.jade to instead load the script from code.jquery.com (so it wouldn't appear in the socat output). Specifically, I replaced the existing jquery loading line with "script(src='//code.jquery.com/jquery-1.11.2.min.js')".)

The browser was displaying /notes from the application (as evidenced by the Referer: header); the Jade code notes.jade encodes an script tag which refers to "javascripts/notes.js". When the browser renders the HTML produced by this HTML template, this script tag causes the browser to make an HTTP GET request for /javascripts/notes.js.

This GET request is handled by the static file webserver added to the application by the call to "app.use(express.static(...))" in app.js.

GET /getNotes

> 2015/04/05 13:20:21.155190  length=467 from=410 to=876
GET /getNotes HTTP/1.1\r
Host: localhost:3005\r
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0\r
Accept: application/json, text/javascript, */*; q=0.01\r
Accept-Language: en-US,en;q=0.5\r
Accept-Encoding: gzip, deflate\r
DNT: 1\r
X-Requested-With: XMLHttpRequest\r
Referer: http://localhost:3005/notes\r
Cookie: connect.sid=s%3A5VyCgEI8XPZoQdySt-HLQPgdSyIkvDck.i1vb2GNcw3GGVOx%2FKTWe6YVZZC3MGp4i2kM1OwWad5U\r
Connection: keep-alive\r
\r

< 2015/04/05 13:20:21.165400  length=195 from=284479 to=284673
HTTP/1.1 200 OK\r
X-Powered-By: Express\r
Content-Type: application/json; charset=utf-8\r
Content-Length: 312\r
ETag: W/"138-c76af03e"\r
Date: Sun, 05 Apr 2015 17:20:21 GMT\r
Connection: keep-alive\r
\r
< 2015/04/05 13:20:21.166425  length=311 from=284674 to=284984
[{"_id":"54f349cd92a8c3f039a69eaa","title":"Yes","owner":"Anil","content":"Well?\\noeau"},{"_id":"54f34cad35c2f35b3aa44fe8","title":"Titled!","owner":"Anil","content":"No content"},{"_id":"55216bbde49d1a6667de0008","title":"Note for Assignment 9 solutions","owner":"Anil","content":"This is a body of a note.\\n"}]

(Note that the response is done through two separate transmissions, one for the header and one for the returned object.)

This GET request was initiated by the getJSON() call in getAndListNotes() in notes.js (running in the context of the /notes page on the browser).

The server runs the anonymous callback registered by router.get('/getNotes',) in router/index.js. This function does a res.send() of the notes object that is returned from querying the notesCollection (notesCollection.find()).

POST /updateNote

> 2015/04/05 13:23:11.991854  length=610 from=4934 to=5543
POST /updateNote HTTP/1.1\r
Host: localhost:3005\r
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0\r
Accept: */*\r
Accept-Language: en-US,en;q=0.5\r
Accept-Encoding: gzip, deflate\r
DNT: 1\r
Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r
X-Requested-With: XMLHttpRequest\r
Referer: http://localhost:3005/notes\r
Content-Length: 56\r
Cookie: connect.sid=s%3AInaH5BQtzrhgc1eCqzAq_Xvx5ZDZFm9k.glgLGfIdJ1ZTQv8jQ9GLVwVa3Tn%2BUp3Ws0JiTE6JeuY\r
Connection: keep-alive\r
Pragma: no-cache\r
Cache-Control: no-cache\r
\r
id=55216f7ae49d1a6667de0009&title=Test&content=Hello!%0A

< 2015/04/05 13:23:12.001057  length=163 from=289573 to=289735
HTTP/1.1 200 OK\r
X-Powered-By: Express\r
Content-Type: text/html; charset=utf-8\r
Content-Length: 16\r
Date: Sun, 05 Apr 2015 17:23:12 GMT\r
Connection: keep-alive\r
\r
< 2015/04/05 13:23:12.001440  length=15 from=289736 to=289750
update succeede

(In my transcript I couldn't find the last "d" in succeeded. )

This POST is initiated by the $.post("/updateNote", ...) call in updateNoteOnServer() in notes.js (running in the context of /notes in the browser).

On the server this POST request is serviced by the anonymous callback registered by the call to router.post('/updateNote',...) in router/index.js. This function does a res.send("update succeeded") when the database has been updated with the POST'ed note.

POST /newNote

> 2015/04/05 13:07:09.057471  length=488 from=1233 to=1720
POST /newNote HTTP/1.1\r
Host: localhost:3005\r
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0\r
Accept: */*\r
Accept-Language: en-US,en;q=0.5\r
Accept-Encoding: gzip, deflate\r
DNT: 1\r
X-Requested-With: XMLHttpRequest\r
Referer: http://localhost:3005/notes\r
Cookie: connect.sid=s%3A7VfppM1sNSPZH2ItZKhGrNFwEYtZ9hqe.j39zaf1VaOxBtKb48jUl%2BrxDauYO3IQSu%2BMaNlQgBz8\r
Connection: keep-alive\r
Pragma: no-cache\r
Cache-Control: no-cache\r
Content-Length: 0\r
\r

< 2015/04/05 13:07:09.135980  length=170 from=285831 to=286000
HTTP/1.1 200 OK\r
X-Powered-By: Express\r
Content-Type: application/json; charset=utf-8\r
Content-Length: 26\r
Date: Sun, 05 Apr 2015 17:07:09 GMT\r
Connection: keep-alive\r
\r
< 2015/04/05 13:07:09.137200  length=26 from=286001 to=286026
"55216bbde49d1a6667de0008"

The POST /newNote is initiated by the call $.post("/newNote",...) in the newNote() function in notes.js (running in the context of /notes in the browser). This call happens after a user presses the "New Note" button.

To handle this POST request, the server runs the anonymous callback registered by the call to router.post('newNote', ...) in router/index.js. This function does a res.send() of the ID of the newly created note upon successful insertion of a new, untitled note into the notes collection.