require 'em_test_helper'

class TestHttpClient < Test::Unit::TestCase

  def setup
    @port = next_port
  end

  #-------------------------------------

  def test_http_client
    ok = false
    EM.run {
      c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
      c.callback {
        ok = true
        c.close_connection
        EM.stop
      }
      c.errback {EM.stop} # necessary, otherwise a failure blocks the test suite forever.
    }
    assert ok
  end

  #-------------------------------------

  def test_http_client_1
    ok = false
    EM.run {
      c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
      c.callback {
        ok = true
        c.close_connection
        EM.stop
      }
      c.errback {EM.stop}
    }
    assert ok
  end

  #-------------------------------------

  def test_http_client_2
    ok = false
    EM.run {
      c = silent { EM::P::HttpClient.send :request, :host => "www.google.com", :port => 80 }
      c.callback {
        ok = true
        c.close_connection
        EM.stop
      }
      c.errback {EM.stop}
    }
    assert ok
  end


  #-----------------------------------------

  # Test a server that returns a page with a zero content-length.
  # This caused an early version of the HTTP client not to generate a response,
  # causing this test to hang. Observe, there was no problem with responses
  # lacking a content-length, just when the content-length was zero.
  #
  class EmptyContent < EM::Connection
      def initialize *args
        super
      end
      def receive_data data
        send_data "HTTP/1.0 404 ...\r\nContent-length: 0\r\n\r\n"
        close_connection_after_writing
      end
  end

  def test_http_empty_content
      ok = false
      EM.run {
        EM.start_server "127.0.0.1", @port, EmptyContent
        c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port }
        c.callback {
          ok = true
          c.close_connection
          EM.stop
        }
      }
      assert ok
  end


  #---------------------------------------

  class PostContent < EM::P::LineAndTextProtocol
      def initialize *args
        super
        @lines = []
      end
      def receive_line line
        if line.length > 0
          @lines << line
        else
          process_headers
        end
      end
      def receive_binary_data data
        @post_content = data
        send_response
      end
      def process_headers
        if @lines.first =~ /\APOST ([^\s]+) HTTP\/1.1\Z/
          @uri = $1.dup
        else
          raise "bad request"
        end

        @lines.each {|line|
          if line =~ /\AContent-length:\s*(\d+)\Z/i
            @content_length = $1.dup.to_i
          elsif line =~ /\AContent-type:\s*(\d+)\Z/i
            @content_type = $1.dup
          end
        }

        raise "invalid content length" unless @content_length
        set_binary_mode @content_length
      end
      def send_response
        send_data "HTTP/1.1 200 ...\r\nConnection: close\r\nContent-length: 10\r\nContent-type: text/html\r\n\r\n0123456789"
        close_connection_after_writing
      end
  end
  
  # TODO, this is WRONG. The handler is asserting an HTTP 1.1 request, but the client
  # is sending a 1.0 request. Gotta fix the client
  def test_post
      response = nil
      EM.run {
        EM.start_server '127.0.0.1', @port, PostContent
        setup_timeout(2)
        c = silent { EM::P::HttpClient.request(
          :host         => '127.0.0.1',
          :port         => @port,
          :method       => :post,
          :request      => "/aaa",
          :content      => "XYZ",
          :content_type => "text/plain"
        )}
        c.callback {|r|
          response = r
          EM.stop
        }
      }

      assert_equal( 200, response[:status] )
      assert_equal( "0123456789", response[:content] )
  end


  # TODO, need a more intelligent cookie tester.
  # In fact, this whole test-harness needs a beefier server implementation.
  def test_cookie
    ok = false
    EM.run {
      c = silent { EM::Protocols::HttpClient.send :request, :host => "www.google.com", :port => 80, :cookie=>"aaa=bbb" }
      c.callback {
        ok = true
        c.close_connection
        EM.stop
      }
      c.errback {EM.stop}
    }
    assert ok
  end

  # We can tell the client to send an HTTP/1.0 request (default is 1.1).
  # This is useful for suppressing chunked responses until those are working.
  def test_version_1_0
    ok = false
    EM.run {
      c = silent { EM::P::HttpClient.request(
        :host => "www.google.com",
        :port => 80,
        :version => "1.0"
      )}
      c.callback {
        ok = true
        c.close_connection
        EM.stop
      }
      c.errback {EM.stop}
    }
    assert ok
  end

  #-----------------------------------------

  # Test a server that returns chunked encoding
  #
  class ChunkedEncodingContent < EventMachine::Connection
    def initialize *args
      super
    end
    def receive_data data
      send_data ["HTTP/1.1 200 OK",
                "Server: nginx/0.7.67", 
                "Date: Sat, 23 Oct 2010 16:41:32 GMT",
                "Content-Type: application/json",
                "Transfer-Encoding: chunked",
                "Connection: keep-alive",
                "", 
                "1800",
                "chunk1" * 1024,
                "5a",
                "chunk2" * 15,
                "0",
                ""].join("\r\n")
      close_connection_after_writing
    end
  end

  def test_http_chunked_encoding_content
    ok = false
    EM.run {
      EM.start_server "127.0.0.1", @port, ChunkedEncodingContent
      c = silent { EM::P::HttpClient.send :request, :host => "127.0.0.1", :port => @port }
      c.callback { |result|
        if result[:content] == "chunk1" * 1024 + "chunk2" * 15
          ok = true
        end
        c.close_connection
        EM.stop
      }
    }
    assert ok
  end

end