Computer >> कंप्यूटर ट्यूटोरियल >  >> प्रोग्रामिंग >> Ruby

सिर्फ 30 लाइन कोड में रूबी HTTP सर्वर बनाएं

वेब सर्वर, और सामान्य रूप से HTTP, समझने में कठिन लग सकते हैं। ब्राउज़र अनुरोध को कैसे प्रारूपित करता है, और प्रतिक्रिया उपयोगकर्ता को कैसे भेजी जाती है? इस रूबी मैजिक एपिसोड में हम 30 लाइन कोड में रूबी HTTP सर्वर बनाना सीखेंगे। जब हम कर लेंगे, तो हमारा सर्वर HTTP GET अनुरोधों को संभालेगा और हम इसका उपयोग Rack ऐप की सेवा के लिए करेंगे।

HTTP और TCP कैसे एक साथ काम करते हैं

TCP एक ट्रांसपोर्ट प्रोटोकॉल है जो वर्णन करता है कि सर्वर और क्लाइंट डेटा का आदान-प्रदान कैसे करते हैं।

HTTP एक अनुरोध-प्रतिक्रिया प्रोटोकॉल है जो विशेष रूप से वर्णन करता है कि वेब सर्वर HTTP क्लाइंट या वेब ब्राउज़र के साथ डेटा का आदान-प्रदान कैसे करते हैं। HTTP आमतौर पर अपने ट्रांसपोर्ट प्रोटोकॉल के रूप में TCP का उपयोग करता है। संक्षेप में, एक HTTP सर्वर एक TCP सर्वर है जो "HTTP बोलता है"।

# tcp_server.rb
require 'socket'
server = TCPServer.new 5678
while session = server.accept
  session.puts "Hello world! The time is #{Time.now}"
  session.close
end

इस TCP सर्वर उदाहरण में, सर्वर पोर्ट 5678 पर बाइंड करता है और क्लाइंट के कनेक्ट होने का इंतजार करता है। जब ऐसा होता है, तो यह क्लाइंट को एक संदेश भेजता है, और फिर कनेक्शन बंद कर देता है। पहले क्लाइंट से बात करने के बाद, सर्वर अपने संदेश को फिर से भेजने के लिए दूसरे क्लाइंट के कनेक्ट होने का इंतजार करता है।

# tcp_client.rb
require 'socket'
server = TCPSocket.new 'localhost', 5678
while line = server.gets
  puts line
end
server.close

हमारे सर्वर से कनेक्ट करने के लिए, हमें एक TCP क्लाइंट की आवश्यकता होगी। यह उदाहरण क्लाइंट उसी पोर्ट (5678) से कनेक्ट होता है और सर्वर से डेटा प्राप्त करने के लिए server.gets का उपयोग करता है, जिसे फिर प्रिंट किया जाता है। जब यह सर्वर से डेटा प्राप्त करना बंद कर देता है, तो यह सर्वर से कनेक्शन बंद कर देता है और प्रोग्राम बाहर निकल जाएगा।

जब सर्वर चल रहा होता है ($ ruby tcp_server.rb), आप क्लाइंट को सर्वर का संदेश प्राप्त करने के लिए एक अलग टैब में शुरू कर सकते हैं।

$ ruby tcp_client.rb
Hello world! The time is 2016-11-23 15:17:11 +0100
$

थोड़ी कल्पना के साथ, हमारा TCP सर्वर और क्लाइंट कुछ हद तक वेब सर्वर और ब्राउज़र की तरह काम करते हैं। क्लाइंट एक अनुरोध भेजता है, सर्वर प्रतिक्रिया देता है, और कनेक्शन बंद हो जाता है। इस तरह अनुरोध-प्रतिक्रिया पैटर्न काम करता है, जो वास्तव में वही है जो हमें HTTP सर्वर बनाने के लिए चाहिए।

इससे पहले कि हम अच्छे भाग पर पहुंचें, आइए देखें कि HTTP अनुरोध और प्रतिक्रियाएं कैसी दिखती हैं।

एक बुनियादी HTTP GET अनुरोध

सबसे बुनियादी HTTP GET अनुरोध बिना किसी अतिरिक्त हेडर या अनुरोध बॉडी के अनुरोध-लाइन है।

GET / HTTP/1.1\r\n

Request-Line में चार भाग होते हैं:

  • एक मेथड टोकन (GET, इस उदाहरण में)
  • Request-URI (/)
  • प्रोटोकॉल संस्करण (HTTP/1.1)
  • लाइन के अंत को इंगित करने के लिए एक CRLF (कैरिज रिटर्न: \r, उसके बाद लाइन फीड: \n)

सर्वर HTTP प्रतिक्रिया के साथ प्रतिक्रिया देगा, जो इस तरह दिख सकती है:

HTTP/1.1 200\r\nContent-Type: text/html\r\n\r\n\Hello world!

इस प्रतिक्रिया में शामिल है:

  • एक स्थिति लाइन: प्रोटोकॉल संस्करण ("HTTP/1.1"), उसके बाद एक स्पेस, प्रतिक्रिया का स्थिति कोड ("200"), और CRLF (\r\n) के साथ समाप्त
  • वैकल्पिक हेडर लाइनें। इस मामले में, केवल एक हेडर लाइन है ("Content-Type: text/html"), लेकिन कई हो सकते हैं (CRLF के साथ अलग: \r\n)
  • स्थिति लाइन और हेडर को बॉडी से अलग करने के लिए एक नई लाइन (या डबल CRLF): (\r\n\r\n)
  • बॉडी: "Hello world!"

न्यूनतम रूबी HTTP सर्वर

पर्याप्त बात। अब जब हम जानते हैं कि रूबी में TCP सर्वर कैसे बनाया जाता है और कुछ HTTP अनुरोध और प्रतिक्रियाएं कैसी दिखती हैं, हम एक न्यूनतम HTTP सर्वर बना सकते हैं। आप देखेंगे कि वेब सर्वर TCP सर्वर की तरह ही दिखता है जिसकी हमने पहले चर्चा की थी। सामान्य विचार वही है, हम बस अपने संदेश को प्रारूपित करने के लिए HTTP प्रोटोकॉल का उपयोग कर रहे हैं। साथ ही, क्योंकि हम अनुरोध भेजने और प्रतिक्रियाओं को पार्स करने के लिए ब्राउज़र का उपयोग करेंगे, हमें इस बार क्लाइंट को लागू करने की आवश्यकता नहीं होगी।

# http_server.rb
require 'socket'
server = TCPServer.new 5678
while session = server.accept
  request = session.gets
  puts request
  session.print "HTTP/1.1 200\r\n" # 1
  session.print "Content-Type: text/html\r\n" # 2
  session.print "\r\n" # 3
  session.print "Hello world! The time is #{Time.now}" #4
  session.close
end

सर्वर द्वारा अनुरोध प्राप्त करने के बाद, पहले की तरह, यह वापस क्लाइंट को संदेश भेजने के लिए session.print का उपयोग करता है: केवल हमारे संदेश के बजाय, यह प्रतिक्रिया को एक स्थिति लाइन, एक हेडर और एक नई लाइन के साथ पूर्वसर्ग करता है:

  1. स्थिति लाइन (HTTP 1.1 200\r\n) ब्राउज़र को बताने के लिए कि HTTP संस्करण 1.1 है और प्रतिक्रिया कोड "200" है
  2. यह इंगित करने के लिए एक हेडर कि प्रतिक्रिया में text/html सामग्री प्रकार है (Content-Type: text/html\r\n)
  3. नई लाइन (\r\n)
  4. बॉडी: "Hello world! …"

पहले की तरह, यह संदेश भेजने के बाद कनेक्शन बंद कर देता है। हम अभी तक अनुरोध नहीं पढ़ रहे हैं, इसलिए यह अभी के लिए इसे कंसोल पर प्रिंट करता है।

यदि आप सर्वर शुरू करते हैं और अपने ब्राउज़र में https://localhost:5678 खोलते हैं, तो आपको पहले TCP क्लाइंट से प्राप्त की तरह वर्तमान समय के साथ "Hello world! …"-लाइन देखनी चाहिए। 🎉

Rack ऐप की सेवा करना

अब तक, हमारा सर्वर प्रत्येक अनुरोध के लिए एक ही प्रतिक्रिया लौटा रहा है। इसे थोड़ा अधिक उपयोगी बनाने के लिए, हम अपने सर्वर में और प्रतिक्रियाएं जोड़ सकते हैं। इन्हें सीधे सर्वर में जोड़ने के बजाय, हम Rack ऐप का उपयोग करेंगे। हमारा सर्वर HTTP अनुरोधों को पार्स करेगा और उन्हें Rack ऐप को पास करेगा, जो तब सर्वर के लिए प्रतिक्रिया लौटाएगा जिसे क्लाइंट को वापस भेजा जाए।

Rack रूबी का समर्थन करने वाले वेब सर्वर और रेल्स और सिनात्रा जैसे अधिकांश रूबी वेब फ्रेमवर्क के बीच एक इंटरफेस है। अपने सरलतम रूप में, Rack ऐप एक ऑब्जेक्ट है जो call का जवाब देता है और तीन आइटम के साथ एक "ट्रिपलेट" लौटाता है: एक HTTP प्रतिक्रिया कोड, HTTP हेडर का हैश, और एक बॉडी।

app = Proc.new do |env|
  ['200', {'Content-Type' => 'text/html'}, ["Hello world! The time is #{Time.now}"]]
end

इस उदाहरण में, प्रतिक्रिया कोड "200" है, हम हेडर के माध्यम से सामग्री प्रकार के रूप में "text/html" पास कर रहे हैं, और बॉडी एक स्ट्रिंग के साथ सरणी है।

हमारे सर्वर को इस ऐप से प्रतिक्रियाओं की सेवा करने की अनुमति देने के लिए, हमें Rack ऐप द्वारा लौटाए गए ट्रिपलेट को HTTP प्रतिक्रिया स्ट्रिंग में बदलना होगा। पहले की तरह हमेशा एक स्थिर प्रतिक्रिया लौटाने के बजाय, अब हमें Rack ऐप द्वारा लौटाए गए ट्रिपलेट से प्रतिक्रिया बनानी होगी।

# http_server.rb
require 'socket'
app = Proc.new do
  ['200', {'Content-Type' => 'text/html'}, ["Hello world! The time is #{Time.now}"]]
end
server = TCPServer.new 5678
while session = server.accept
  request = session.gets
  puts request
  # 1
  status, headers, body = app.call({})
  # 2
  session.print "HTTP/1.1 #{status}\r\n"
  # 3
  headers.each do |key, value|
    session.print "#{key}: #{value}\r\n"
  end
  # 4
  session.print "\r\n"
  # 5
  body.each do |part|
    session.print part
  end
  session.close
end

Rack ऐप से प्राप्त प्रतिक्रिया की सेवा करने के लिए, हम अपने सर्वर में कुछ बदलाव करेंगे:

  1. app.call द्वारा लौटाए गए ट्रिपलेट से स्थिति कोड, हेडर, और बॉडी प्राप्त करें।
  2. स्थिति कोड का उपयोग करके स्थिति लाइन बनाएं
  3. हैश में प्रत्येक कुंजी-मान जोड़ी के लिए हेडर पर लूप करें और हेडर लाइन जोड़ें
  4. स्थिति लाइन और हेडर को बॉडी से अलग करने के लिए नई लाइन प्रिंट करें
  5. बॉडी पर लूप करें और प्रत्येक भाग प्रिंट करें। चूंकि हमारी बॉडी सरणी में केवल एक भाग है, यह सेशन को बंद करने से पहले बस हमारे "Hello world"-संदेश को सेशन पर प्रिंट करेगा।

अनुरोध पढ़ना

अब तक, हमारा सर्वर request वेरिएबल को नजरअंदाज कर रहा है। हमें ऐसा करने की आवश्यकता नहीं थी क्योंकि हमारा Rack ऐप हमेशा वही प्रतिक्रिया लौटाता था।

Rack::Lobster एक उदाहरण ऐप है जो Rack के साथ आता है और कार्य करने के लिए अनुरोध URL पैरामीटर्स का उपयोग करता है। पहले हमारे द्वारा ऐप के रूप में उपयोग किए गए Proc के बजाय, हम अब से इसे अपने परीक्षण ऐप के रूप में उपयोग करेंगे।

# http_server.rb
require 'socket'
require 'rack'
require 'rack/lobster'
app = Rack::Lobster.new
server = TCPServer.new 5678
while session = server.accept
  # ...

ब्राउज़र खोलने पर अब पहले प्रिंट किए गए उबाऊ स्ट्रिंग के बजाय एक लॉबस्टर दिखाई देगा। लॉबस्टररिफिक!

"flip!" और "crash!" लिंक क्रमशः /?flip=left और /?flip=crash से लिंक करते हैं। हालांकि, लिंक का अनुसरण करने पर, लॉबस्टर फ्लिप नहीं करता और अभी कुछ भी क्रैश नहीं होता। ऐसा इसलिए है क्योंकि हमारा सर्वर अभी क्वेरी स्ट्रिंग्स को नहीं संभालता। याद रखें request वेरिएबल जिसे हमने पहले नजरअंदाज किया था? यदि हम अपने सर्वर के लॉग को देखते हैं, तो हम प्रत्येक पेज के लिए अनुरोध स्ट्रिंग्स देखेंगे।

GET / HTTP/1.1
GET /?flip=left HTTP/1.1
GET /?flip=crash HTTP/1.1

HTTP अनुरोध स्ट्रिंग्स में अनुरोध मेथड ("GET"), अनुरोध पथ (/, /?flip=left और /?flip=crash), और HTTP संस्करण शामिल हैं। हम इस जानकारी का उपयोग यह निर्धारित करने के लिए कर सकते हैं कि हमें क्या सेवा करने की आवश्यकता है।

# http_server.rb
require 'socket'
require 'rack'
require 'rack/lobster'
app = Rack::Lobster.new
server = TCPServer.new 5678
while session = server.accept
  request = session.gets
  puts request
  # 1
  method, full_path = request.split(' ')
  # 2
  path, query = full_path.split('?')
  # 3
  status, headers, body = app.call({
    'REQUEST_METHOD' => method,
    'PATH_INFO' => path,
    'QUERY_STRING' => query
  })
  session.print "HTTP/1.1 #{status}\r\n"
  headers.each do |key, value|
    session.print "#{key}: #{value}\r\n"
  end
  session.print "\r\n"
  body.each do |part|
    session.print part
  end
  session.close
end

अनुरोध को पार्स करने और अनुरोध पैरामीटर्स को Rack ऐप को भेजने के लिए, हम अनुरोध स्ट्रिंग को विभाजित करेंगे और इसे Rack ऐप को भेजेंगे:

  1. अनुरोध स्ट्रिंग को मेथड और पूर्ण पथ में विभाजित करें
  2. पूर्ण पथ को पथ और क्वेरी में विभाजित करें
  3. उन्हें Rack एनवायरनमेंट हैश में ऐप को पास करें।

उदाहरण के लिए, GET /?flip=left HTTP/1.1\r\n जैसा अनुरोध ऐप को इस तरह पास किया जाएगा:

{
  'REQUEST_METHOD' => 'GET',
  'PATH_INFO' => '/',
  'QUERY_STRING' => '?flip=left'
}

हमारे सर्वर को पुनः आरंभ करना, https://localhost:5678 पर जाना, और "flip!"-लिंक पर क्लिक करना अब लॉबस्टर को फ्लिप करेगा, और "crash!" लिंक पर क्लिक करने से हमारा वेब सर्वर क्रैश हो जाएगा।

हमने HTTP सर्वर को लागू करने की सतह को खरोंचा है, और हमारा केवल 30 लाइन कोड का है, लेकिन यह मूल विचार समझाता है। यह GET अनुरोध स्वीकार करता है, अनुरोध के गुणों को Rack ऐप को पास करता है, और प्रतिक्रियाओं को ब्राउज़र पर वापस भेजता है। हालांकि यह अनुरोध स्ट्रीमिंग और POST अनुरोध जैसी चीजों को नहीं संभालता, हमारा सर्वर सैद्धांतिक रूप से अन्य Rack ऐप्स की सेवा के लिए भी इस्तेमाल किया जा सकता है।

यह रूबी में HTTP सर्वर बनाने पर हमारी त्वरित नज़र का समापन करता है। यदि आप हमारे सर्वर के साथ खेलना चाहते हैं, तो यहां कोड है। @AppSignal पर हमें बताएं यदि आप अधिक जानना चाहते हैं, या कोई विशिष्ट प्रश्न है।

यदि आपको यह लेख पसंद आया, तो रूबी मैजिक न्यूज़लेटर की सदस्यता लें: रूबी का (लगभग) मासिक पो(र)शन।

  1. रूबी इंटरप्रेटर बनाना: भाग 2 – एक कस्टम प्रोग्रामिंग भाषा का निर्माण रूबी इंटरप्रेटर बनाना: भाग 2 – एक कस्टम प्रोग्रामिंग भाषा का निर्माण

    गिटहब पर पूर्ण स्रोत स्टॉफल प्रोग्रामिंग भाषा का पूरा कार्यान्वयन गिटहब पर उपलब्ध है। यदि आपको बग मिलें या कोई प्रश्न हों तो बेझिझक एक इश्यू खोलें। इस ब्लॉग पोस्ट में, हम स्टॉफल के लिए इंटरप्रेटर को लागू करना जारी रखेंगे, जो पूरी तरह से रूबी में बनी एक खिलौना प्रोग्रामिंग भाषा है। हमने पिछले पोस्ट

  1. Stoffle भाषा के लिए Ruby इंटरप्रेटर बनाना Stoffle भाषा के लिए Ruby इंटरप्रेटर बनाना

    GitHub पर पूर्ण स्रोत Stoffle प्रोग्रामिंग भाषा का एक पूर्ण कार्यान्वयन GitHub पर उपलब्ध है। यदि आपको बग मिलते हैं या आपके कोई प्रश्न हैं तो बेझिझक एक इश्यू खोलें। इस ब्लॉग पोस्ट में, हम Stoffle के लिए इंटरप्रेटर को लागू करना शुरू करने जा रहे हैं, एक खिलौना प्रोग्रामिंग भाषा जिसे पूरी तरह से

  1. Ruby में एप्लिकेशन सर्वर्स को समझना: Puma क्यों मायने रखता है Ruby में एप्लिकेशन सर्वर्स को समझना: Puma क्यों मायने रखता है

    एप्लिकेशन सर्वर्स को समझना आइए मान लें कि आप Ruby में अपना नया चमकदार वेब एप्लिकेशन बनाना शुरू करते हैं। और कोई कोड लिखने से पहले... आप इसे अपने ब्राउज़र में लोड होते देखना चाहते हैं, भले ही केवल डिफ़ॉल्ट वेलकम पेज देखने के लिए। तो आप अपना ब्राउज़र खोलते हैं, आप इसे localhost:3000 पर इंगित करते हैं,