Daily Code Reading #25 – RestClient::AbstractResponse

Since yesterday I reviewed the RestClient::Response class, it’s time to read the parent class RestClient::AbstractResponse. There are a few interesting methods in AbstractResponse but the #return! method is a good one to review.

The Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
module RestClient
  class AbstractResponse
 
    # Return the default behavior corresponding to the response code:
    # the response itself for code in 200..206, redirection for 301 and 302 in get and head cases, redirection for 303 and an exception in other cases
    def return! &block
      if (200..206).include? code
        self
      elsif [301, 302].include? code
        unless [:get, :head].include? args[:method]
          raise Exceptions::EXCEPTIONS_MAP[code], self
        else
          follow_redirection &block
        end
      elsif code == 303
        args[:method] = :get
        args.delete :payload
        follow_redirection &block
      elsif Exceptions::EXCEPTIONS_MAP[code]
        raise Exceptions::EXCEPTIONS_MAP[code], self
      else
        raise RequestFailed, self
      end
    end
 
  end
end

Review

There is a lot of branches here based on the HTTP status code, so I’m going to take it one branch at a time.

200 – 206 Success

1
2
      if (200..206).include? code
        self

The success status codes will just return self, the Response object.

301 or 302 – Redirect

1
2
3
4
5
6
      elsif [301, 302].include? code
        unless [:get, :head].include? args[:method]
          raise Exceptions::EXCEPTIONS_MAP[code], self
        else
          follow_redirection &block
        end

301 and 302 responses both have two different responses.

  1. If the request was using HTTP GET or HTTP HEAD, RestClient will automatically follow the redirect.
  2. Otherwise it will raise an exception with the message ‘301 – Moved Permanently’ or ‘302 – Found’.

303 – See Other

1
2
3
4
      elsif code == 303
        args[:method] = :get
        args.delete :payload
        follow_redirection &block

The HTTP 303 code is sent so the client will retry their request at a new URI using HTTP GET. RestClient accomplishes this by changing the :method, deleting the POST/PUT :payload, and following the redirection.

Other status codes handled by RestClient

1
2
      elsif Exceptions::EXCEPTIONS_MAP[code]
        raise Exceptions::EXCEPTIONS_MAP[code], self

RestClient handles other status codes by throwing an exception. This happens when the request was successful at the transmission level, but it was rejected by the server. The status codes RestClient supports is defined in exceptions.rb.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  STATUSES = {100 => 'Continue',
              101 => 'Switching Protocols',
              200 => 'OK',
              201 => 'Created',
              202 => 'Accepted',
              203 => 'Non-Authoritative Information',
              204 => 'No Content',
              205 => 'Reset Content',
              206 => 'Partial Content',
              300 => 'Multiple Choices',
              301 => 'Moved Permanently',
              302 => 'Found',
              303 => 'See Other',
              304 => 'Not Modified',
              305 => 'Use Proxy',
              400 => 'Bad Request',
              401 => 'Unauthorized',
              403 => 'Forbidden',
              404 => 'Resource Not Found',
              405 => 'Method Not Allowed',
              406 => 'Not Acceptable',
              407 => 'Proxy Authentication Required',
              408 => 'Request Timeout',
              409 => 'Conflict',
              410 => 'Gone',
              411 => 'Length Required',
              412 => 'Precondition Failed',
              413 => 'Request Entity Too Large',
              414 => 'Request-URI Too Long',
              415 => 'Unsupported Media Type',
              416 => 'Requested Range Not Satisfiable',
              417 => 'Expectation Failed',
              500 => 'Internal Server Error',
              501 => 'Not Implemented',
              502 => 'Bad Gateway',
              503 => 'Service Unavailable',
              504 => 'Gateway Timeout',
              505 => 'HTTP Version Not Supported'}

Other status codes not handled by RestClient

1
2
3
      else
        raise RequestFailed, self
      end

Finally, if there is a status code that isn’t handled by RestClient, a RequestFailed is thrown. This would happen for status 207 (Multi-Status, used in WebDAV) or 307 (Temporary Redirect, HTTP/1.1).

RestClient::AbstractResponse#return! highlights an important rule when writing multiple if/else or case statements: put the most common cases first. I learned this from Code Complete several years ago and use it every day when writing code. It makes it easier to separate the main and edge case branches of the code.