The Lifecycle of a Request on Rails APP

The Story of an HTTP Request on RAILS

Jorge Najera
The Startup
Published in
7 min readMay 27, 2020

--

Getting to a website its simple, just enter a website URL on your browser for example “https://www.mydomain.com” and after a couple of seconds a shiny website, content, video, image or whatever that request bring will be display on your browser. For non developers is magic! But for a web developer I think it's crucial to know the real story of a request and learn what happen behind the curtains on his epic journey.

For keep this story short I will explain it from the superficial point of view doing a basic HTTP request to a Rails APP endpoint, but it's important to know that the rabbit hole can look scary and difficult to understand but it shouldn't be.

The Server responding an HTTP Request

For start our story we need to understand the first basic concept the HTTP. Have seen an HTTP 404 or 500 error in any website? I bet that more than once you should encounter this kind of nasty and ugly errors, but why this means? HTTP is defined as HyperText Transfer Protocol (Fancy name!), that permits a browser and a web server communicate, the HTTP principle is based on a request and a response.

When you type a URL on browser or when you click on a hyperlink the browser will always generate a request. Each request will have several headers that compose the instructions of the request so the server can read them and response matching the information request instructions. So lets dive in to analyze a CURL request to my GitHub profile, so we can see the important parts from a request.

Note: For sake of the example some data has been removed.

The Request

THE REQUEST
najera % curl
https://github.com/JNajera --verbose
* Trying 140.82.113.3...
* TCP_NODELAY set
* Connected to github.com (140.82.113.3) port 443 (#0)
* SSL HANDSHAKE A TOPIC FOR ANOTHER POST
* SSL certificate verify ok.
> GET /JNajera HTTP/1.1
> Host: github.com

> User-Agent: curl/7.64.1
> Accept: */*
... RESPONSE CONTINUED ...

To start the communication with the server the browser first make a simple DNS Lookup to find where the github.com domains is hosted, so it basically will find the IP Address of the web server that could answer his request. The browser will try to find it in their local cache first but if it's missing it will go the Internet and lookup from the Root DNS’s to find the IP of the server. This topic could be cover in a full POST, so we will keep it short but more technical stuff happens in these steps.

When the browser finds the IP of the server it will prepare the Request and deliver it to the server. Let's talk about the example request, the first line of the request we find the HTTP Verb in these example we find the Verb GET that usually means to retrieve a document, image, video, etc, we will talk about other Verbs next on. The next piece of information is the requested URL page “/JNajera”, and finally the HTTP version which in the example is 1.1.

The other lines on the request example define extra information like the host, the type of that data that it's excepted to retrieve if it's a JSON or plain text for example, and there exist a bunch of extra headers that could make the request more specific.

Generating a Response Inside a Rails APP

When the request is delivered to the server several things could happen depending on the server setup. Let’s assume github.com is a Rails APP deployed on a VPS on a single server for this example.

So after the request is delivered to the server, it is usually passed to the “web server”, then its delivery to the Rack Middleware and finally to our Rails App. It's a roller coaster, we need to pass thought everything so the response can be delivered back to browser.

First it's important to understand that a “web server” only “speaks” HTTP, there exists different web servers like Pushion Passenger, Puma, Unicorn or Webrick (Please don’t use it on production). Some are writing on C or even in Ruby. Their basic job is to understand the request and make decision of how to deal with it, like sending and image from the assets of anything that is configured to do for specific circumstances.

Web Server Purpose… Handle the Requests

The “web server” then pass the information to Rack. Rack its consider like a simple API to talk between Rails and the “Web server”. Rack has the job to tell Rails I have a request for you, this is the data from the request, HTTP Verb, URL, header, etc and the listen to Rails and translate the result to the “web server” with a status, code, headers and body. The principal reason to have Rack protocol is that you can plug and play any “web server” that support Rack and your APP will continue running seamlessly.

Rails have use Rack Middleware to handle cookies, logs, profiling, cache and other important parts. Rack looks like a black box… but it shouldn't be, you could get a list of all the Middleware magic that take action on Rack before the request is passed to our APP.

najera % bin/rails middleware

use Rack::Sendfile
use ActionDispatch::Executor
use ActiveSupport::Cache::Strategy::LocalCache::Middleware
use Rack::Runtime
use Rack::MethodOverride
use ActionDispatch::RequestId
use ActionDispatch::RemoteIp
use Rails::Rack::Logger
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
use ActionDispatch::Callbacks
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash
use ActionDispatch::ContentSecurityPolicy::Middleware
use Rack::Head
use Rack::ConditionalGet
use Rack::ETag
use Rack::TempfileReaper
run Github::Application.routes

After dealing with Rack, passing all the Middleware we see some light, our Rails App take control of the information and its responsible for the final response. The last piece of Rack is to pass the data to our Router, so we need to step back a little, to talk about the HTTP Verb kinds. What kind of HTTP Verb exist and why are they important for a request on our Rails App?

My first thought when I read the HTTP Verb documentation

Well the basic HTTPS Verbs or Methods that a developer should understand are GET, POST, PUT, DELETE or PATCH. In a Rails World or REST APIs the HTTP Verb and the URL help us find the right controller and action that will be executed. By convention each action should map to and specific CRUD (Create, Retrieve, Update, Delete) operation, technically its possible to violate this guideline but its highly discouraged.

After our request is passed to our Rails APP, the request will be matched with our routes.rb file that have the map of a specific HTTP Verb and the URL to a specific controller and action that need to follow up with the request, so basically the Router job is to find where to take the request next tocontroller#action.

Example from the Rails guide of CRUD mapping the routes with HTTP Verb

Finally, our request will execute some of our code in our controller and action. We will stick with the CURL example to my GitHub profile so what should happen inside?

Well the APP will lookup for my “User” in the database with the slugged URL that we provided “/JNajera” and if a slug matches a “User” it will return the record and been render some HTML. Here is where the MVC take place of how the APP handle the data, but it will be topic for another POST so we will leave like a BLACK BOX for the example.

The Response

THE REQUEST
najera % curl
https://github.com/JNajera --verbose
... REQUEST HEADERS ...
THE RESPONSE
<
HTTP/1.1 200 OK
< server: GitHub.com
< date: Wed, 27 May 2020 03:52:23 GMT
< content-type: text/html; charset=utf-8
< status: 200 OK
< vary: X-Requested-With, Accept-Encoding, Accept, X-Requested-With
< etag: W/"b1ea90f48557b7d5c47e218d04661775"
< cache-control: max-age=0, private, must-revalidate
< strict-transport-security: max-age=31536000; includeSubdomains; preload
< HERE MORE DATA HAS BEEN OMITTED

Don’t give up we are almost there! After the request completes it lifecycle in our APP and the response is server back to Rack, Rack to the “web server” and the “web server” to the browser we will have some data that our Browser need to read in order to display the information that our “web server” has prepared for him.

Following the example when GitHub server get the request, the first line of the response to the browser contains the HTTP protocol that in this example is 1.1 and the status code in this example 200 OK (Yei! We assume that it find my user profile). There exist several status codes that our “web server” could have delivered depending on what happened during the process of the request on its epic journey. Some possible responses we can see are grouped in five classes:

1. Informational responses (100–199)
2. Successful responses (200–299)
3. Redirects (300–399)
4. Client errors (400–499)
5. and Server errors (500–599)

So we see an 200 OK that means that the request get back home safe and complete. After the status code, there are more headers' information that include all the raw details that include the server to let us know what happen with the request and how was handled. At the bottom of the request we will find the body of the response, that contains the data we request, where will usually find some HTML, CSS, JavaScript, or any other binary data that could represent a file like an image, PDF or simple text file.

So the story of a request is a journey that took less than a blink of an eye and that’s exiting! So the next time that you start a simple “Hello World” in a Rails App think in all the hard work that Rails handled for us, so we can concentrate on create our application.

The Request returning to our Browser

--

--

Jorge Najera
The Startup

Cofounder at Bolteam & Actiun. Fullstack Engineer, Coffee junkie and Crazycatman.