Monday, October 27, 2014

Front End Web Dev Concepts - HTTP

Everyone knows what HTTP is, but it's rare that you meet developers with a good understanding of how to use it.  This is because we developers only have to understand that HTTP is the way in which browsers and servers communicate.  This is generally the most important thing to know, but we should also know how to use all of HTTP's features in order to create a faster and more secure website.

Clients make requests; Servers give responses

HTTP's main concept is Request-Response.  A client makes a request to a server and a server provides a response to the client.  The most common example of a client is a browser, which makes requests to the website, the most common example of a server.
   Client                      Server   
                                        
+----------+                +----------+
|          |   Connection   |          |
|          |                |          |
|          +--------------> |          |
|          |                |          |
|          |   Request      |          |
|          |                |          |
|          +--------------> |          |
|          |                |          |
|          |   Response     |          |
|          |                |          |
|          | <--------------|          |
|          |                |          |
|          | End Connection |          |
|          |                |          |
|          | <--------------|          |
+----------+                +----------+

Structure and Protocol

Requests and Responses use the same structure.  They both have a (1) Start Line, (2) Headers and (3) Body.  In a request, the start line indicates what the client is trying to access from the server, like a profile, blog post, or product, and it does this in the form of a path, like /user/profile.html.  Aside from requesting data back from a server, a request can also tell the server to create or manipulate data, like create a user or pay for something.  To standardize the types of things a client can ask the server to do, HTTP has methods that represent these actions.  When a client wants to receive data, it uses the GET method.  In general when a client wants to create data, it uses the POST method.  If you want to learn more about methods, read the w3.org documentation on HTTP methods.

Next we have headers.  Headers indicate information about the request, like where it came from, how to cache, or what format the client is expecting in the response.  In the example below, you can see that the User-Agent header's value is curl/7.30.0.  The server can now see what program was making this request.  This is how sites get browser statistics since each browser tells the server who it is in the User-Agent header.  See the HTTP List of headers wiki for more info.

Finally we have the body.  In our example below, there is no body in the request.  The body is optional, and usually in GET requests, there is no body.  For responses to a GET request, the response body contains the file requested (like the HTML for google.com).  See the response example below to see what the body returns.

Example request:
Start Line: GET /user/profile.html HTTP/1.1 
Headers:    User-Agent: curl/7.30.0
            Host: google.com
            Accept: */*

Example response:
Start Line: HTTP/1.1 200 OK
Headers:    Server: nginx/1.6.0
            Date: Mon, 27 Oct 2014 12:54:47 GMT
            Content-Type: text/html
            Content-Length: 1349
            Last-Modified: Thu, 03 Jul 2014 23:31:18 GMT
            Connection: keep-alive
            ETag: "53b5e7c6-545"
            Accept-Ranges: bytes

Body:       <!DOCTYPE html>
            <html>
               <head>
                  <title>Statefate</title>
                  ...
            </html>


Some tricks

HTTP follows a request/response pattern.  This means that every action that happens in HTTP starts with a request, and ends with a response.  However, before we can send the request, the client has to connect to the server.  Once the client and server are connected, the request is made by a client (browser), and a response is made by a server (website).  Once the response is sent, the connection is closed.

HTTP does not allow the server to send responses whenever it wants to.  For example, when you are IMing your friend through a website, your browser needs to be told when a new message is coming in, but how can it know if the server cannot tell you?  One way is to make a request every second to check for new messages.  This is generally bad since the ideal solution is to only make a request when there is a new message.  Another solution is to think of the chat messages as a streaming response.   See the drawing below:
   Client                      Server   
                                        
+----------+                +----------+
|          |                |          |
|          |   Request      |          |
|          +--------------> |          |
|          |                |          |
|          |    Response    |          |
|          | <--------------|          |
|          |                |          |
|          | <--------------|          |
|          |                |          |
|          | <--------------|          |
|          |                |          |
|          | <--------------|          |
|          |                |          |
|          |    Close       |          |
|          | <--------------|          |
+----------+                +----------+

Here the server is sending multiple mini responses (chat messages) which make up the entire HTTP response and waits until they have all been sent (after everyone has signed off) to close the connection.  How does this fit in the Structure of the response? Once we send the response headers, we can send chunks of the response body as new messages from your friend come in.  The cool part is that a client can also send data in the request body even after it started to get a response from the server. Here is some example code to see this in action. To run this, you will need to install Nodejs. The response will echo the data you pass to it in the request body:


request.js
var http = require('http');
var options = {
 "hostname": "localhost",
 "port": 8009,
 "method": "POST",
 "path": "/"
};

var req = http.request(options, function(res) {
 // console.log('headers', res.headers);
 res.on('data', function(chunk) {
  console.log('echo', '' + chunk);
 });
});

module.exports = req;


response.js
var http = require('http');
http.createServer(function(req, res) {
 console.log('starting request');
 req.on('data', function(chunk) {
  console.log('writing', '' + chunk);
  res.write(chunk);
 });
 req.on('end', function() {
  console.log('ending request');
  res.end();
 });
}).listen(8009);

Then run
$ node server.js &
$ node
> var req = require('./request');
> req.write('echo test');
...




Wednesday, September 26, 2012

Extend NodeList

I started to move away from jQuery. Plain old JS in new browsers now support css selectors to return dom elements, which was like 50% of the awesomeness that jQuery gave you in my opinion. I'm not sure if anyone out there is not using jQuery, so this probably pertains to no one.
So I started cranking out my own framework... I started changing the prototype of NodeList (which is the return type of the document.querySelectorAll method) and things were going great, until I had to create a NodeList object. new NodeList throws an error...
I figured I could get around this problem by extending NodeList. I did this with the following code:
var ElementList = function() {}
ElementList.prototype = document.querySelectorAll('doesntexist')

This works but it has two problems:

  1. as you add to the ElementList array, the length property doesn't get updated
  2. FF has a bug (new ElementList) instanceof NodeList == false
The way I get around 1 is by using a for in loop, and check to make sure the the property matches /^\d+$/. Then to get around the second one, I might file a bug report to FF, but in the mean time, I just check for ElementList inheritance along with NodeList inheritance.

UPDATE

I have since filed a bug with FF, and the issue has been fixed.