While learning and working with rails
, we must all have heard of the concept of Rack
.
So what is Rack
? How are request
being sent to the Rails app handled?
Hi everyone, today we are going to learn about Rack
issues.
Rack
Rack is defined:
Rack provides a minimal, modular, and adaptable interface for developing web applications in Ruby
It can be understood that Rack
is an interface
, a component
placed between the webserver
and the ruby
framework, with the purpose of bridging the communication between the webserver
and the ruby
framework.
Request response with Rack
When a request is sent from the browser, the webserver
(Puma, Unicorn, Webrick) will receive this HTTP request
, then pass it to Rack
, and the Rack
continue to send it to the web app (Rails, Sinatra).
The web application handles the request
, creating a response
returns the user in the opposite direction:
So why do we need Rack
, why webservers do not send request
and receive response
from webapps but through Rack
?
By using Rack
to communicate between webserver and webapp, we can use many different webservers with many different web applications. For example, if you want to use Puma instead of Webrick, or use Sinatra instead of Rails, because webservers and webapps can communicate with each other via Rack
:
Deeper
Back to the request
, when sent from the browser to the webserver, the webserver will convert the HTTP request into a ruby hash: env
, env
which looks like:
1 2 3 4 5 6 7 8 9 10 | <span class="token punctuation">{</span> <span class="token string">"GATEWAY_INTERFACE"</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">"CGI/1.1"</span> <span class="token punctuation">,</span> <span class="token string">"PATH_INFO"</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">"/"</span> <span class="token punctuation">,</span> <span class="token string">"QUERY_STRING"</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token string">"REMOTE_ADDR"</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">"127.0.0.1"</span> <span class="token punctuation">,</span> " <span class="token constant">REM</span> <span class="token constant">OTE_HOST</span> <span class="token string">"=>"</span> localhost <span class="token string">", "</span> <span class="token constant">REQUEST_METHOD</span> <span class="token string">"=>"</span> <span class="token constant">GET</span> <span class="token string">", "</span> <span class="token constant">REQUEST_URI</span> <span class="token string">"=>"</span> http <span class="token punctuation">:</span> <span class="token operator">/</span> <span class="token operator">/</span> localhost <span class="token punctuation">:</span> <span class="token number">9292</span> <span class="token operator">/</span> <span class="token string">", "</span> <span class="token constant">SCRIPT_NAME</span> " <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token string">"SERVER_NAME"</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">"localhost"</span> <span class="token punctuation">,</span> <span class="token string">"SERVER_PORT"</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">"9292"</span> <span class="token punctuation">,</span> <span class="token string">"SERVER_PROTOCOL"</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">"HTTP/1.1"</span> <span class="token punctuation">,</span> " <span class="token constant">SERVER_SOFTWAR</span> <span class="token constant">E</span> <span class="token string">"=>"</span> <span class="token constant">WEBrick</span> <span class="token operator">/</span> <span class="token number">1.3</span> <span class="token number">.1</span> <span class="token punctuation">(</span> <span class="token constant">Ruby</span> <span class="token operator">/</span> <span class="token number">2.2</span> <span class="token number">.1</span> <span class="token operator">/</span> <span class="token number">2015</span> <span class="token operator">-</span> <span class="token number">02</span> <span class="token operator">-</span> <span class="token number">26</span> <span class="token punctuation">)</span> <span class="token string">", "</span> <span class="token constant">HTTP_HOST</span> <span class="token string">"=>"</span> localhost <span class="token punctuation">:</span> <span class="token number">9292</span> <span class="token string">", "</span> <span class="token constant">HTTP_ACCEPT_LANGUAGE</span> <span class="token string">"=>"</span> en <span class="token operator">-</span> <span class="token constant">US</span> <span class="token punctuation">,</span> en <span class="token punctuation">;</span> q <span class="token operator">=</span> <span class="token number">0.8</span> <span class="token punctuation">,</span> de <span class="token punctuation">;</span> q <span class="token operator">=</span> <span class="token number">0.6</span> <span class="token string">", "</span> <span class="token constant">HTTP_CACHE_CONTROL</span> <span class="token string">"=>"</span> max <span class="token operator">-</span> age <span class="token operator">=</span> <span class="token number">0</span> <span class="token string">", "</span> <span class="token constant">HTTP_ACCEPT_ENCODING</span> <span class="token string">"=>"</span> gzip <span class="token string">", "</span> <span class="token constant">HTTP_ACCEPT</span> <span class="token string">"=>"</span> text <span class="token operator">/</span> html <span class="token punctuation">,</span> application <span class="token operator">/</span> xhtml <span class="token operator">+</span> xml <span class="token punctuation">,</span> application <span class="token operator">/</span> xml <span class="token punctuation">;</span> q <span class="token operator">=</span> <span class="token number">0.9</span> <span class="token punctuation">,</span> image <span class="token operator">/</span> webp <span class="token punctuation">,</span> <span class="token operator">*</span> <span class="token operator">/</span> <span class="token operator">*</span> <span class="token punctuation">;</span> q <span class="token operator">=</span> <span class="token number">0.8</span> <span class="token string">", "</span> <span class="token constant">HTTP_USER_AGENT</span> <span class="token string">"=>"</span> <span class="token constant">Mo</span> zilla <span class="token operator">/</span> <span class="token number">5.0</span> <span class="token punctuation">(</span> <span class="token constant">Macintosh</span> <span class="token punctuation">;</span> <span class="token constant">Intel</span> <span class="token constant">Mac</span> <span class="token constant">OS</span> <span class="token constant">X</span> <span class="token number">10</span> _10_3 <span class="token punctuation">)</span> <span class="token constant">AppleWebKit</span> <span class="token operator">/</span> <span class="token number">537.36</span> <span class="token punctuation">(</span> <span class="token constant">KHTML</span> <span class="token punctuation">,</span> like <span class="token constant">Gecko</span> <span class="token punctuation">)</span> <span class="token constant">Chrome</span> <span class="token operator">/</span> <span class="token number">42.0</span> <span class="token number">.2311</span> <span class="token number">.1</span> <span class="token number">35</span> <span class="token constant">Safari</span> <span class="token operator">/</span> <span class="token number">537.36</span> <span class="token string">", "</span> rack <span class="token punctuation">.</span> version <span class="token string">"=>[1, 3], "</span> rack <span class="token punctuation">.</span> url_scheme <span class="token string">"=>"</span> http <span class="token string">", "</span> <span class="token constant">HTTP_VERSION</span> <span class="token string">"=>"</span> <span class="token constant">HTTP</span> <span class="token operator">/</span> <span class="token number">1.1</span> <span class="token string">", "</span> <span class="token constant">REQU</span> <span class="token constant">EST_PATH</span> <span class="token string">"=>"</span> <span class="token operator">/</span> " <span class="token punctuation">}</span> |
env
can be divided into 3 parts:
- Request headers:
1 2 3 4 5 6 7 8 | <span class="token constant">HTTP_HOST</span> <span class="token punctuation">:</span> <span class="token string">"localhost:9292"</span> <span class="token constant">HTTP_REFERER</span> <span class="token punctuation">:</span> <span class="token string">"http://localhost:9292/"</span> <span class="token constant">HTTP_ACCEPT_LANGUAGE</span> <span class="token punctuation">:</span> <span class="token string">"en-US,en;q=0.8,de;q=0.6"</span> <span class="token constant">HTTP_ACCEPT_ENCODING</span> <span class="token punctuation">:</span> <span class="token string">"gzip"</span> <span class="token constant">HTTP_USER_AGENT</span> <span class="token punctuation">:</span> <span class="token string">"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36"</span> <span class="token constant">HTTP_ACCEPT</span> <span class="token punctuation">:</span> <span class="token string">"*/*"</span> <span class="token constant">HTTP_VERSION</span> <span class="token punctuation">:</span> <span class="token string">"HTTP/1.1"</span> |
- Server info:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token constant">GATEWAY_INTERFACE</span> <span class="token punctuation">:</span> <span class="token string">"CGI/1.1"</span> <span class="token constant">PATH_INFO</span> <span class="token punctuation">:</span> <span class="token string">"/"</span> <span class="token constant">QUERY_STRING</span> <span class="token punctuation">:</span> <span class="token string">""</span> <span class="token constant">REMOTE_ADDR</span> <span class="token punctuation">:</span> <span class="token string">"127.0.0.1"</span> <span class="token constant">REMOTE_HOST</span> <span class="token punctuation">:</span> <span class="token string">"localhost"</span> <span class="token constant">REQUEST_METHOD</span> <span class="token punctuation">:</span> <span class="token string">"GET"</span> <span class="token constant">REQUEST_URI</span> <span class="token punctuation">:</span> <span class="token string">"http://localhost:9292/"</span> <span class="token constant">SCRIPT_NAME</span> <span class="token punctuation">:</span> <span class="token string">""</span> <span class="token constant">SERVER_NAME</span> <span class="token punctuation">:</span> <span class="token string">"localhost"</span> <span class="token constant">SERVER_PORT</span> <span class="token punctuation">:</span> <span class="token string">"9292"</span> |
- Rack info:
1 2 3 | rack <span class="token punctuation">.</span> version <span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token number">1</span> <span class="token punctuation">,</span> <span class="token number">3</span> <span class="token punctuation">]</span> rack <span class="token punctuation">.</span> url_scheme <span class="token punctuation">:</span> <span class="token string">"http"</span> |
So how is this env
variable handled in Rack?
Rack application will receive this env
variable. A Rack app needs a method call
, and returns an array of 3 elements:
- The HTTP response code
- A Hash of headers
- The response body
1 2 3 4 5 6 7 8 | <span class="token keyword">class</span> <span class="token class-name">RackApp</span> <span class="token keyword">def</span> <span class="token function">call</span> <span class="token punctuation">(</span> env <span class="token punctuation">)</span> <span class="token comment">#do some action according to the contents in the env hash</span> result <span class="token operator">=</span> <span class="token function">action</span> <span class="token punctuation">(</span> env <span class="token punctuation">[</span> <span class="token string">"PATH_INFO"</span> <span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token punctuation">[</span> <span class="token number">200</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token string">'Content-Type'</span> <span class="token operator">=</span> <span class="token operator">></span> <span class="token string">'text/html'</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> result <span class="token punctuation">]</span> <span class="token keyword">end</span> <span class="token keyword">end</span> |
Rack middleware
Each Rack middleware
is a rack app
, with different purposes, performing different functions with the request
sent or the response
returned.
Print rails?
Rails
is rack
application, a final application built by a series of middleware
with different purposes. $ rake middleware
will list the middleware in the app:
Some middleware:
- Rack :: Sendfile : handles static files under the public directory
- Rack :: Runtime : Adds an X-Runtime field in the returned response header, indicating the processing time for the corresponding request
- Rack :: MethodOverride : sets the value for the HTTP request method based on the input “_method” in each form generated by the rails form helper.
- ActionDispatch :: RequestId : assign a unique ID to each incoming request
- ActionDispatch :: ShowExceptions, ActionDispatch :: DebugExceptions : catch exceptions and display custom pages:
- ActionDispatch :: Callbacks : provides before / after callbacks
- ActionDispatch :: Flash : save flash, and remove old flash in the session
- ActionDispatch :: Session :: CookieStore : stores session in cookie
- ActionDispatch :: Cookies : store cookies in the browser via Set-Cookie header
- Rack :: Cache : supports rails with HTTP Caching
request
to, middleware
in turn called in order, finally to RailsApp :: Application.routes, find the corresponding controller, and return the response
Above are some of my findings on Rack
, thanks for listening