Class: EventMachine::Protocols::HttpClient

### Home Classes Methods

### In Files

- lib/em/protocols/httpclient.rb

### Parent

EventMachine::Connection

### Methods

- ::new
- ::request
- #connection_completed
- #dispatch_response
- #post_init
- #receive_data
- #send_request
- #unbind

### Included Modules

- EventMachine::Deferrable

### Files

- ChangeLog
- DEFERRABLES
- EPOLL
- INSTALL
- KEYBOARD
- LEGAL
- LIGHTWEIGHT_CONCURRENCY
- PURE_RUBY
- RELEASE_NOTES
- SMTP
- SPAWNED_PROCESSES
- TODO

### Class/Module Index

Quicksearch

- EventMachine
- EventMachine
- EventMachine::Channel
- EventMachine::Completion
- EventMachine::Connection
- EventMachine::DNS
- EventMachine::DNS::Request
- EventMachine::DNS::RequestIdAlreadyUsed
- EventMachine::DNS::Resolver
- EventMachine::DNS::Socket
- EventMachine::DatagramObject
- EventMachine::DefaultDeferrable
- EventMachine::Deferrable
- EventMachine::DeferrableChildProcess
- EventMachine::Error
- EventMachine::EvmaKeyboard
- EventMachine::EvmaTCPClient
- EventMachine::EvmaTCPServer
- EventMachine::EvmaUDPSocket
- EventMachine::EvmaUNIXClient
- EventMachine::EvmaUNIXServer
- EventMachine::FileNotFoundException
- EventMachine::FileStreamer
- EventMachine::FileWatch
- EventMachine::Iterator
- EventMachine::LoopbreakReader
- EventMachine::PeriodicTimer
- EventMachine::Pool
- EventMachine::ProcessWatch
- EventMachine::Protocols
- EventMachine::Protocols
- EventMachine::Protocols::HeaderAndContentProtocol
- EventMachine::Protocols::HttpClient
- EventMachine::Protocols::HttpClient2
- EventMachine::Protocols::HttpClient2::Request
- EventMachine::Protocols::LineAndTextProtocol
- EventMachine::Protocols::LineProtocol
- EventMachine::Protocols::LineText2
- EventMachine::Protocols::Memcache
- EventMachine::Protocols::Memcache::ParserError
- EventMachine::Protocols::ObjectProtocol
- EventMachine::Protocols::Postgres3
- EventMachine::Protocols::SASLauth
- EventMachine::Protocols::SASLauthclient
- EventMachine::Protocols::SmtpClient
- EventMachine::Protocols::SmtpServer
- EventMachine::Protocols::Socks4
- EventMachine::Protocols::Stomp
- EventMachine::Protocols::Stomp::Message
- EventMachine::Protocols::TcpConnectTester
- EventMachine::Queue
- EventMachine::Reactor
- EventMachine::Selectable
- EventMachine::SpawnedProcess
- EventMachine::StreamObject
- EventMachine::SystemCmd
- EventMachine::ThreadedResource
- EventMachine::TickLoop
- EventMachine::Timer
- EventMachine::UuidGenerator
- EventMachine::YieldBlockFromSpawnedProcess
- Object
- Object::EM
- Object::P
- BufferedTokenizer
- IO
- StringIO
- TestConnection

No matching classes.

# EventMachine::Protocols::HttpClient

Note: This class is deprecated and will be removed. Please use EM-HTTP-Request instead.

@example

    EventMachine.run {
      http = EventMachine::Protocols::HttpClient.request(
        :host => server,
        :port => 80,
        :request => "/index.html",
        :query_string => "parm1=value1&parm2=value2"
      )
      http.callback {|response|
        puts response[:status]
        puts response[:headers]
        puts response[:content]
      }
    }

### Constants

MaxPostContentLength

### Public Class Methods

new() click to toggle source

    # File lib/em/protocols/httpclient.rb, line 66
    def initialize
      warn "HttpClient is deprecated and will be removed. EM-Http-Request should be used instead."
    end

request( args = {} ) click to toggle source

@param args [Hash] The request arguments @option args [String] :host The host IP/DNS name @option args [Integer] :port The port to connect too @option args [String] :verb The request type [GET | POST | DELETE | PUT] @option args [String] :request The request path @option args [Hash] :basic_auth The basic auth credentials (:username and :password) @option args [String] :content The request content @option args [String] :contenttype The content type (e.g. text/plain) @option args [String] :query_string The query string @option args [String] :host_header The host header to set @option args [String] :cookie Cookies to set

    # File lib/em/protocols/httpclient.rb, line 81
    def self.request( args = {} )
      args[:port] ||= 80
      EventMachine.connect( args[:host], args[:port], self ) {|c|
        # According to the docs, we will get here AFTER post_init is called.
        c.instance_eval {@args = args}
      }
    end

### Public Instance Methods

connection_completed() click to toggle source

We send the request when we get a connection. AND, we set an instance variable to indicate we passed through here. That allows unbind to know whether there was a successful connection. NB: This naive technique won't work when we have to support multiple requests on a single connection.

    # File lib/em/protocols/httpclient.rb, line 100
    def connection_completed
      @connected = true
      send_request @args
    end

dispatch_response() click to toggle source

    # File lib/em/protocols/httpclient.rb, line 258
    def dispatch_response
      @read_state = :base
      set_deferred_status :succeeded, {
        :content => @content,
        :headers => @headers,
        :status => @status
      }
      # TODO, we close the connection for now, but this is wrong for persistent clients.
      close_connection
    end

post_init() click to toggle source

    # File lib/em/protocols/httpclient.rb, line 89
    def post_init
      @start_time = Time.now
      @data = ""
      @read_state = :base
    end

receive_data(data) click to toggle source

    # File lib/em/protocols/httpclient.rb, line 177
    def receive_data data
      while data and data.length > 0
        case @read_state
        when :base
          # Perform any per-request initialization here and don't consume any data.
          @data = ""
          @headers = []
          @content_length = nil # not zero
          @content = ""
          @status = nil
          @read_state = :header
          @connection_close = nil
        when :header
          ary = data.split( /\r?\n/, 2 )
          if ary.length == 2
            data = ary.last
            if ary.first == ""
              if (@content_length and @content_length > 0) || @connection_close
                @read_state = :content
              else
                dispatch_response
                @read_state = :base
              end
            else
              @headers << ary.first
              if @headers.length == 1
                parse_response_line
              elsif ary.first =~ /\Acontent-length:\s*/
                # Only take the FIRST content-length header that appears,
                # which we can distinguish because @content_length is nil.
                # TODO, it's actually a fatal error if there is more than one
                # content-length header, because the caller is presumptively
                # a bad guy. (There is an exploit that depends on multiple
                # content-length headers.)
                @content_length ||= $'.to_i
              elsif ary.first =~ /\Aconnection:\s*close/
                @connection_close = true
              end
            end
          else
            @data << data
            data = ""
          end
        when :content
          # If there was no content-length header, we have to wait until the connection
          # closes. Everything we get until that point is content.
          # TODO: Must impose a content-size limit, and also must implement chunking.
          # Also, must support either temporary files for large content, or calling
          # a content-consumer block supplied by the user.
          if @content_length
            bytes_needed = @content_length - @content.length
            @content += data[0, bytes_needed]
            data = data[bytes_needed..-1] || ""
            if @content_length == @content.length
              dispatch_response
              @read_state = :base
            end
          else
            @content << data
            data = ""
          end
        end
      end
    end

send_request(args) click to toggle source

    # File lib/em/protocols/httpclient.rb, line 105
    def send_request args
      args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
      args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?

      verb = args[:verb].to_s.upcase
      unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
        set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
        return # NOTE THE EARLY RETURN, we're not sending any data.
      end

      request = args[:request] || "/"
      unless request[0,1] == "/"
        request = "/" + request
      end

      qs = args[:query_string] || ""
      if qs.length > 0 and qs[0,1] != '?'
        qs = "?" + qs
      end

      version = args[:version] || "1.1"

      # Allow an override for the host header if it's not the connect-string.
      host = args[:host_header] || args[:host] || "_"
      # For now, ALWAYS tuck in the port string, although we may want to omit it if it's the default.
      port = args[:port].to_i != 80 ? ":#{args[:port]}" : ""

      # POST items.
      postcontenttype = args[:contenttype] || "application/octet-stream"
      postcontent = args[:content] || ""
      raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength

      # ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
      # TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
      req = [
        "#{verb} #{request}#{qs} HTTP/#{version}",
        "Host: #{host}#{port}",
        "User-agent: Ruby EventMachine",
      ]

        if verb == "POST" || verb == "PUT"
          req << "Content-type: #{postcontenttype}"
          req << "Content-length: #{postcontent.length}"
        end

        # TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
        # Eventually we will want to deal intelligently with arrays and hashes.
        if args[:cookie]
          req << "Cookie: #{args[:cookie]}"
        end

        # Allow custom HTTP headers, e.g. SOAPAction
        args[:custom_headers].each do |k,v|
          req << "#{k}: #{v}"
        end if args[:custom_headers]

        # Basic-auth stanza contributed by Matt Murphy.
        if args[:basic_auth]
          basic_auth_string = ["#{args[:basic_auth][:username]}:#{args[:basic_auth][:password]}"].pack('m').strip.gsub(/\n/,'')
          req << "Authorization: Basic #{basic_auth_string}"
        end

        req << ""
        reqstring = req.map {|l| "#{l}\r\n"}.join
        send_data reqstring

        if verb == "POST" || verb == "PUT"
          send_data postcontent
        end
    end

unbind() click to toggle source

    # File lib/em/protocols/httpclient.rb, line 269
    def unbind
      if !@connected
        set_deferred_status :failed, {:status => 0} # YECCCCH. Find a better way to signal no-connect/network error.
      elsif (@read_state == :content and @content_length == nil)
        dispatch_response
      end
    end

[Validate]

Generated with the Darkfish Rdoc Generator 2.