require 'helper'
require 'integration/shared_examples'

describe "draft03" do
  include EM::SpecHelper
  default_timeout 1

  before :each do
    @request = {
      :port => 80,
      :method => "GET",
      :path => "/demo",
      :headers => {
        'Host' => 'example.com',
        'Connection' => 'Upgrade',
        'Sec-WebSocket-Key2' => '12998 5 Y3 1  .P00',
        'Sec-WebSocket-Protocol' => 'sample',
        'Upgrade' => 'WebSocket',
        'Sec-WebSocket-Key1' => '4 @1  46546xW%0l 1 5',
        'Origin' => 'http://example.com',
        'Sec-WebSocket-Draft' => '3'
      },
      :body => '^n:ds[4U'
    }

    @response = {
      :headers => {
        "Upgrade" => "WebSocket",
        "Connection" => "Upgrade",
        "Sec-WebSocket-Location" => "ws://example.com/demo",
        "Sec-WebSocket-Origin" => "http://example.com",
        "Sec-WebSocket-Protocol" => "sample"
      },
      :body => "8jKS\'y:G*Co,Wxa-"
    }
  end

  it_behaves_like "a websocket server" do
    def start_server
      EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
        yield ws
      }
    end

    def start_client
      client = EM.connect('0.0.0.0', 12345, Draft03FakeWebSocketClient)
      client.send_data(format_request(@request))
      yield client if block_given?
    end
  end

  # These examples are straight from the spec
  # http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-03#section-4.6
  describe "examples from the spec" do
    it "should accept a single-frame text message" do
      em {
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
          ws.onmessage { |msg|
            msg.should == 'Hello'
            done
          }
        }

        # Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        # Send frame
        connection.onopen {
          connection.send_data("\x04\x05Hello")
        }
      }
    end
    
    it "should accept a fragmented text message" do
      em {
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
          ws.onmessage { |msg|
            msg.should == 'Hello'
            done
          }
        }

        # Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        # Send frame
        connection.onopen {
          connection.send_data("\x84\x03Hel")
          connection.send_data("\x00\x02lo")
        }
      }
    end
    
    it "should accept a ping request and respond with the same body" do
      em {
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }

        # Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        # Send frame
        connection.onopen {
          connection.send_data("\x02\x05Hello")
        }
        
        connection.onmessage { |frame|
          next if frame.nil?
          frame.should == "\x03\x05Hello"
          done
        }
      }
    end
    
    it "should accept a 256 bytes binary message in a single frame" do
      em {
        data = "a" * 256
        
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
          ws.onmessage { |msg|
            msg.should == data
            done
          }
        }

        # Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        # Send frame
        connection.onopen {
          connection.send_data("\x05\x7E\x01\x00" + data)
        }
      }
    end
    
    it "should accept a 64KiB binary message in a single frame" do
      em {
        data = "a" * 65536
        
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
          ws.onmessage { |msg|
            msg.should == data
            done
          }
        }

        # Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        # Send frame
        connection.onopen {
          connection.send_data("\x05\x7F\x00\x00\x00\x00\x00\x01\x00\x00" + data)
        }
      }
    end
  end

  describe "close handling" do
    it "should respond to a new close frame with a close frame" do
      em {
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws| }

        # Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        # Send close frame
        connection.onopen {
          connection.send_data("\x01\x00")
        }

        # Check that close ack received
        connection.onmessage { |frame|
          frame.should == "\x01\x00"
          done
        }
      }
    end

    it "should close the connection on receiving a close acknowlegement" do
      em {
        ack_received = false

        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
          ws.onopen {
            # 2. Send a close frame
            EM.next_tick {
              ws.close_websocket
            }
          }
        }

        # 1. Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        # 3. Check that close frame recieved and acknowlege it
        connection.onmessage { |frame|
          frame.should == "\x01\x00"
          ack_received = true
          connection.send_data("\x01\x00")
        }

        # 4. Check that connection is closed _after_ the ack
        connection.onclose {
          ack_received.should == true
          done
        }
      }
    end

    it "should not allow data frame to be sent after close frame sent" do
      em {
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
          ws.onopen {
            # 2. Send a close frame
            EM.next_tick {
              ws.close_websocket
            }

            # 3. Check that exception raised if I attempt to send more data
            EM.add_timer(0.1) {
              lambda {
                ws.send('hello world')
              }.should raise_error(EM::WebSocket::WebSocketError, 'Cannot send data frame since connection is closing')
              done
            }
          }
        }

        # 1. Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))
      }
    end

    it "should still respond to control frames after close frame sent" do
      em {
        EM::WebSocket.start(:host => "0.0.0.0", :port => 12345) { |ws|
          ws.onopen {
            # 2. Send a close frame
            EM.next_tick {
              ws.close_websocket
            }
          }
        }

        # 1. Create a fake client which sends draft 76 handshake
        connection = EM.connect('0.0.0.0', 12345, FakeWebSocketClient)
        connection.send_data(format_request(@request))

        connection.onmessage { |frame|
          if frame == "\x01\x00"
            # 3. After the close frame is received send a ping frame, but
            # don't respond with a close ack
            connection.send_data("\x02\x05Hello")
          else
            # 4. Check that the pong is received
            frame.should == "\x03\x05Hello"
            done
          end
        }
      }
    end
  end
end
