Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Cách Nginx Truyền HTTP Headers Đến Ứng Dụng Ruby: Hướng Dẫn Chi Tiết Và Chuyên Nghiệp

Ngày nay hầu hết phát triển web đều sử dụng framework. Dù bạn dùng Rails, Sinatra hay Lotus, bạn không thực sự cần quan tâm về cách cookies và các headers khác truyền từ Nginx hay Apache, đến application server và vào ứng dụng của bạn. Chúng просто hoạt động.

Chúng ta sẽ đi sâu vào hành trình này. Bởi hóa ra câu chuyện về headers chứa đựng nhiều thông tin thú vị về lịch sử của web.

HTTP Headers Là Gì?

Mỗi khi trình duyệt web thực hiện một request, nó gửi kèm các thứ gọi là HTTP headers. Chúng chứa cookies, thông tin về user agent, caching info — rất nhiều thứ hữu ích.

Bạn có thể xem headers nào đang được gửi bằng cách nhìn vào request trong developer tools của trình duyệt. Dưới đây là ví dụ. Như bạn thấy, headers không có gì kỳ diệu. Chúng chỉ là văn bản được định dạng theo cách nhất định.

Cách Nginx Truyền HTTP Headers Đến Ứng Dụng Ruby: Hướng Dẫn Chi Tiết Và Chuyên Nghiệp

Headers KHÔNG Được Truyền Đến App Như Thế Nào

Nếu bạn từng viết Rack app, bạn chắc đã thấy hash env, chứa các biến môi trường của app. Nếu nhìn bên trong, bạn sẽ thấy ngoài các biến môi trường hệ thống thông thường, nó còn chứa tất cả request headers.

# config.ru
run lambda { |env| [200, {"Content-Type" => "text/plain"}, [env.inspect]] }

# Output:
# { "HTTP_HOST"=>"localhost:9000", "HTTP_CONNECTION"=>"keep-alive", "HTTP_PRAGMA"=>"no-cache", "HTTP_CACHE_CONTROL"=>"no-cache", "HTTP_ACCEPT"=>"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "HTTP_UPGRADE_INSECURE_REQUESTS"=>"1", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36", ... }

Đây KHÔNG phải là cách Nginx truyền headers đến app của bạn. :)

Application Servers

Hiện nay, hầu hết Ruby web apps chạy trong application servers như Unicorn. Vì app servers không được spawn bởi Nginx, Nginx không thể set biến môi trường cho chúng.

Vậy headers di chuyển từ Nginx đến Unicorn như thế nào? Đơn giản. Khi Nginx forward request đến app server, nó gửi toàn bộ request — headers và tất cả.

Để chứng minh điều này, tôi xây dựng một application server đơn giản dump tất cả thứ Nginx gửi nó ra STDOUT.

require "socket"
# Tạo socket và "lưu" vào file system
server = UNIXServer.new('/tmp/socktest.sock')
# Chờ connection (từ Nginx)
socket = server.accept
# Đọc mọi thứ từ socket
while line = socket.readline
  puts line.inspect
end
socket.close

Nếu bạn cấu hình Nginx kết nối đến server này thay vì Unicorn, bạn sẽ thấy chính xác thông tin gì được gửi đến app server: chỉ là một HTTP request bình thường. Headers và tất cả.

Cách Nginx Truyền HTTP Headers Đến Ứng Dụng Ruby: Hướng Dẫn Chi Tiết Và Chuyên Nghiệp

Để biết thêm về cách viết upstream app server đơn giản, xem bài viết của tôi về Unix sockets.

Tại Sao Lại Cần Biến Môi Trường?

Năm 1993, NSCA công bố spec cho "Common Gateway Interface" hay CGI.

Cách Nginx Truyền HTTP Headers Đến Ứng Dụng Ruby: Hướng Dẫn Chi Tiết Và Chuyên Nghiệp

Đó là cách để servers như Apache chạy các chương trình tùy ý trên disk để tạo dynamic webpages. User request một trang, và Apache thực sự shell out và chạy chương trình để generate kết quả. Vì Apache spawn apps trực tiếp, nó có thể set biến môi trường cho chúng.

CGI standard quy định HTTP headers được truyền vào như biến môi trường. Và để tránh va chạm tên với biến môi trường hiện có, nó quy định "HTTP_" nên được prepend vào tên.

Vì thế bạn có một đống biến môi trường trông như thế này:

HTTP_ACCEPT="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
HTTP_ACCEPT_CHARSET="ISO-8859-1,utf-8;q=0.7,*;q=0.7"
HTTP_ACCEPT_ENCODING="gzip, deflate"
HTTP_ACCEPT_LANGUAGE="en-us,en;q=0.5"
HTTP_CONNECTION="keep-alive"
HTTP_HOST="example.com"
HTTP_USER_AGENT="Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0"

Ngày nay hầu không ai dùng CGI cho development mới, nhưng vẫn rất phổ biến thấy HTTP headers lưu như biến môi trường — dù đôi khi chúng là "fake" environment variables.

App Servers "Gia Lập" Như Thế Nào

Application servers parse headers từ raw HTTP request. Vậy chúng vào environment như thế nào? App server đặt chúng vào đó.

Tôi đào một chút trong WEBrick và tìm thấy "dấu vết":

self.each{|key, val|
  next if /^content-type$/i =~ key
  next if /^content-length$/i =~ key
  name = "HTTP_" + key
  name.gsub!(/-/o, "_")
  name.upcase!
  meta[name] = val
}

Cuối cùng, các "fake" environment variables này được merge với các biến môi trường thực hơn và truyền vào Rack app của bạn, tiếp tục đến Rails, lấy chúng ra khỏi environment hash. :)