Ruby Programming – ODIN Project – 3

odinRuby on the Web

This brief section will bridge the gap between what you’ve been doing on the command line and the wide world of the Internet… how do you send automated Tweets? How about building a basic web server and browser of your own!


Step 1: Bringing Ruby Online

You know how to write scripts and games using Ruby but so far that’s all confined within the boundaries of your local system. Now you will be working with the Internet.

You’ll be working with HTTP requests and responses quite a bit in this section.

Ruby (or a gem that you’ve downloaded) will give you the power to send HTTP requests, whether GET or POST or PUT or DELETE, and then wait for a response. You’ll find yourself needing to take apart that response to get to the good stuff, whether that’s the HTML of a web page you’re trying to load or a JSON object returned by some website’s API that you’ve queried. Every gem has a slightly different way of doing it, but the principles remain the same. On the flip side, Ruby gives you the tools to listen for requests and deliver responses.

It’s important to understand the underlying fundamentals of how web requests and responses work if you want to actually build web pages. Ruby on Rails will take care of lots of this for you if that’s your goal.

Points to Ponder

Look through these now and then use them to test yourself after doing the tasks

  • How do you issue an HTTP request in Ruby?
  • How do you parse the response to get to the body content?
  • What is REST?
  • What’s the difference between a GET and a POST request?

Your Tasks

1. In your terminal type $ curl http://www.google.com and observe the massive chunk of text that gets returned. That’s Google’s homepage and should give you an idea of some of the responses you can get from these types of requests.

2. Read Beginning Ruby (http://peterc.org/beginningruby/) Chapter 14: Ruby and the Internet

3. Read the simple tutorial Learn REST: A Tutorial (http://rest.elkstein.org/2008/02/using-rest-in-ruby.html) which shows you how easy it is to issue HTTP requests using Ruby and achieve a response.

4. Read What is HTTP? (http://www.jmarshall.com/easy/http/#whatis).  This is an explanation of how HTTP works.  Read to where it talks about the POST method.  You can stop before the HTTP Proxies section.


 Project: Ruby on the Web

Ever wanted to SPAM your Twitter followers? How about building your own basic web server?


Project 1: Twitter Spambot

You’ve learned how to use Ruby to send requests to the web and parse responses. When you’re building real web applications, you’ll often want to interface with other applications over the web via their API.

If you’re not familiar with APIs, now’s your chance. In this case, Twitter gives you access to a bunch of their commonly used commands (like sending a tweet, accessing your followers, etc) but via your program instead of having to click buttons on their site. Hence why it’s called the Application Programming Interface.  Your application can programmatically access their system!

When companies make their APIs public, they usually want to limit the amount of spam and abuse so they make you authenticate yourself with each request. You usually have to register your application with them and they give you a key to use to authenticate your requests. Sometimes it’s as simple as just including that key in the URL string when you make a request, but often these days they want a less hard-coded and more secure way of authenticating you.

The protocol that’s commonly used for this is called OAuth. In this case Twitter uses OAuth, but you’d have to use the same protocol (though perhaps a different version). There are two major versions of OAuth if you wanted to interface with Facebook, Instagram, Tumblr, etc. so it’s worth getting some experience working with it. It’s not always immediately intuitive but just remember that the basic idea is to make sure the request is coming from someone they have authorized. If you set up your SSH keys to interface with Github back during the installation phase, it’s not terribly different from that idea.

Note that, for this project, you may want to create a throwaway Twitter account (mine is @SpamBot26103678) because you’ll probably accidentally go over their data limits or rate limits or other anti-spamming mechanisms and will send out a lot of testing tweets along the way. No sense getting your main account banned for playing around.


 Your Task

  1. Complete Jumpstart Labs MicroBlogger tutorial (http://tutorials.jumpstartlab.com/projects/microblogger.html). It will take you through how to authenticate with Twitter and start tweeting. Don’t worry about the last part on Klout.

Project 2: A Real Web Server and Browser (from the command line)

Part of the reason for getting into how Ruby interacts with the web is because it’s directly relevant to what you’ll be doing later on with Rails. Rails is just Ruby code neatly packaged. Anything Rails does, you can reproduce on your own with some Ruby knowledge.

In this case, you’ll be building a simple web server that receives requests and sends a response based on those requests. You’ll also create a simple browser client to issue those requests.

There are a lot of steps here, and you’ll need to draw on some of your previous knowledge of working with files and possibly using some basic regular expressions to parse a string.


Background Reading

Web Server Primer

At its core any web server is simply a never ending loop that attempts to accept connections on a listening socket. Here is a very simple TCP server:

    require 'socket'

    # IP address is 0.0.0.0 and it's on port 8080:
    server = TCPServer.new("0.0.0.0", 8080) 
    loop do
        connection = server.accept
        inputline = connection.gets
        ...
        connection.puts outputline
        connection.close
    end

The servers differ in how they construct this loop and how they process incoming connections. The above sample is for a blocking server. Which means that it can only process one request at a time and that other requests will be waiting for the current one to finish. A long running request might make the server unreachable for a while. A group of those will quickly render the server unusable. There are several strategies to overcome this shortcoming. We will discuss those strategies and look at how they are utilized by the different servers.

For a server to be called a web (HTTP) server it must speak the HTTP protocol. Hence it needs a way to parse the incoming HTTP requests. Each of the servers presented here attempts to solve this problem in its own way. But we will soon find that most of them rely on some clone of Mongrel’s parser. If we modify our first server to include HTTP support it could look like this:

    require 'socket'
    server = TCPServer.new("0.0.0.0", 8080)
    loop do
        connection = server.accept
        request = HTTP.parse(connection.gets) # an imaginary HTTP parser
        ...
        connection.puts status
        connection.puts headers
        connection.puts body
        connection.close
    end 

So socket is a library available to Ruby without needing any special downloads (it’s part of the standard library, you just need to remind Ruby to require it). It lets you open and close connections to other machines or servers, just like you did when you were learning how to work with files!

It’s actually pretty much the same principle. A web server is just like opening and closing files on your own computer. The response you’re waiting for is just a long string of characters or binary data like it is with a file. The steps for working with files and servers are almost identical:

1. You need to tell Ruby where to find this “file” (what’s the IP address and port you are looking for?)

2. Open the connection to that file (or the socket to the remote server)

3. Send your request to start reading the file (or whatever you want from that server)

4. Read the contents of the file (or read the response from the server)

5. Close the file (or the socket connection to the server)


Your Tasks

1. Read What is HTTP? (http://www.jmarshall.com/easy/http/#whatis).  This is an explanation of how HTTP works.  Read to where it talks about the POST method.

2. Read Tutorialspoint’s Ruby Socket Programming tutorial (http://www.tutorialspoint.com/ruby/ruby_socket_programming.htm). Don’t worry about the Multi-Client server content too much, but keep reading past it.

3. In one file, implement their “A Simple Server“. It’s easy to copy/paste code, but make sure you conceptually understand what each line is doing.

  • When you call TCPServer.open, the ::open class method is the EXACT same method that you use to call File.open because TCPServer inherits (several levels up) from the same IO class that File does!  This is another way that working with servers is like working with files.
  • Now when you #puts to that socket, it gets picked up on the other side by your client. Not magic at all, just a stream of bytes like typing into the STDIN from the command line using #gets or to STDOUT using #puts.

4. In another file, implement their “A Simple Client“. This should really look a lot like working with files. localhost just represents the address of your current computer (as opposed to, say http://www.google.com). Whenever you’re building web applications and need to test them locally before deploying, you’ll run a local server whose address will be localhost and some port number (often 3000, but that’s arbitrary).

5. In one tab of your terminal, run your server. Press CTRL + c to break from the infinite loop when you want to stop it.

6. In another tab, run your client. You should see whatever you told the server to print get puts‘d to the command line. Congratulations, you just built a server!

7. Now build the “A Tiny Web Browser” from the same TutorialsPoint article (http://www.tutorialspoint.com/ruby/ruby_socket_programming.htm) and test it out against some existing webpages. That’s basically just the same thing you built before but pointing at the web instead of your localhost.

8. Create an HTML file and save it as index.html. It should look like:

    <html>
      <body>
        <h1>Welcome to the Viking Home Page</h1>
      </body>
    </html>

9. Now comes the fun part. Modify your simple server to take the HTTP request from the browser and, if it is a GET request that points to /index.html, send back the contents of index.html.

  • You’ll need to parse the incoming request yourself to determine which verb is being used, which file it wants, and other similar information that’s contained in a standard HTTP request. Again, check the examples here (http://www.jmarshall.com/easy/http/#whatis) for what HTTP requests look like as a reference. The easiest way may be to use a Regular Expression.
  • Send your own properly formatted HTTP response, including a status code of 200 if the file is found, and then the actual contents of the requested file. Don’t forget to include the size (in characters) of the outgoing file (this is a normal part of every HTTP response) to help you display it using your mini-browser.
  • If it asks for another file that doesn’t exist, send back an error code (like 404) and an appropriate (or inappropriate) error message.

10. Modify your simple web browser to send the appropriate GET request to your web server, just like you did earlier with the really simple client/server combo. Test it out.  You should be able to ask for and retrieve the index.html file (and puts it into the terminal).  This will require you to remember some of the commands you used to open files. You should also set it up to identify when you’ve got back an error code and display the error message.

11. Build another HTML file called thanks.html. It should look like:

    <html>
      <body>
        <h1>Thanks for Posting!</h1>
        <h2>Here's what we got from you:</h2>
        <ul>
          <%= yield %>
        </ul>
      </body>
    </html>

12. Now set up your mini web browser client to also send POST requests. Where before we were pretending to be browsing the web, now we’re going to pretend that we just pushed the “submit” button on a form and need to send the form data to your server.

  • Modify your client to ask the user which type of request the user wants to send.
  • If the user wants to send a POST request, pretend you’re registering a viking for a raid. Ask the user for a couple of pieces of data about the viking, including a name and an email.
  • Store the results of this in a hash-inside-a-hash, which might end up looking like {:viking => {:name=>"Erik the Red", :email=>"erikthered@theodinproject.com"} }. Why the hash-in-a-hash? Because this is exactly what it will look like when your browser sends data back from a Rails-generated form. You could just use a normal hash, but that would be less fun.
  • When you send the POST request, this time include your data hash as part of the request. Again, see the examples here (http://www.jmarshall.com/easy/http/#whatis). You’ll want to use the JSON format to transmit your hash, which means you need to add the require 'json' line at the top of your server and client to use the JSON library.
  • The method that converts your hash object into a flat JSON string for easy HTTP transmission is #to_json.
  • You’ll also need to include the size of the data you’re sending in the Content-Length field of the HTTP packet.

13. Finally, set up your server to recognize and respond to a POST request.

  • Turn that JSON string back into an object by using JSON.parse and save that in another hash that we’ll call params (again because this is what Rails does). Your code might look like params = {}; params << JSON.parse(the_post_JSON_string_here).
  • Now open the thanks.html file and (without modifying the original file since you will want to use it multiple times) use your script to replace the line <%= yield %> with a new <li> for each one of the data items that you originally entered in your “form” back in your browser. Display these in whatever format you’d like, for instance <li>Name: Erik the Red</li><li>Email:erikthered@theodinproject.com</li>.
  • Now send that modified file back to your mini-browser and show it. If the server detects a POST request, you should identify and parse out the JSON data from it (it will probably be helpful to use the Content-Length line you included to break out the data)

14. Play with your new browser! Try submitting different things into your name and email fields and watch them display in the html that gets sent back. It’s not magic, it’s HTTP and Ruby.

You have just built a command line web browser that sends actual HTTP requests and a web server that can actually interpret those requests, load up files, modify those files based on the inputs, and send them back to your browser.

Now think about what you did – including some of the steps that are new, like using a hash named params and replacing the line in the thanks.html file called <%= yield %> with some dynamically generated HTML. Those are the types of things that Rails does.


Look at Ruby: Webserver in 70 lines of code (http://blogs.msdn.com/b/abhinaba/archive/2005/10/14/474841.aspx) which is an example solution to a similar but not identical problem.


 Additional Resources

Tuxradar’s Code Project: Create a web server in Ruby (http://www.tuxradar.com/content/code-project-create-web-server-ruby)

Luke Francl’s Implementing a minimal HTTP server in Ruby (https://practicingruby.com/articles/implementing-an-http-file-server)