zhenkyle/shadowsocks_ruby

View on GitHub
lib/shadowsocks_ruby/protocols/protocol_stack.rb

Summary

Maintainability
A
3 hrs
Test Coverage
require "to_camel_case"

module ShadowsocksRuby
  module Protocols

    # Factory class for build protocol stacks
    #
    # @see Protocols
    class ProtocolStack

      # @example This is what a cfg_ary Array look like
      #   [ 
      #     ["some_packet_protocol_name"], # a packet protocol is required
      #     ["some_cipher_protocol_name", {:cipher => "some_cipher_object"}], # a cipher protocol is optional
      #     ["some_obfs_protocol_name", {:obfs_params => "..."}] # a obfs protocol is optional
      #   ]
      #
      # @param [Array] cfg_ary
      # @param [String] cipher_name
      # @param [password] password
      def initialize cfg_ary, cipher_name, password
        @cfg_ary = cfg_ary.map do |protocol_name, param|
          protocol_class = protocol_name.to_camel_case + "Protocol"
          protocol_class = Protocols.const_get(protocol_class)
          [protocol_class, param ||= {}]
        end
        @cipher_name = cipher_name
        @password = password
      end

      # Factory method for build a protocol stack
      #
      # @param [EventMachine::Connection] conn
      # @return top protocol (packet protocol) in the stack
      def build! conn
        cipher = nil

        protocols = @cfg_ary.map do | klass, params|
          case klass.to_s
          when Protocols::PlainProtocol.to_s, Protocols::Socks5Protocol.to_s, \
            Protocols::ShadowsocksProtocol.to_s, Protocols::HttpSimpleProtocol.to_s
            # do nothing, these are known protocols don't need cipher
          when Protocols::TlsTicketProtocol.to_s
            # this protocol need a key
            params = {:key => (cipher ||= Cipher.build(@cipher_name, @password)).key}.merge(params)
          else
            params = {:cipher => (cipher ||= Cipher.build(@cipher_name, @password))}.merge(params)
          end
            klass.new(params)
        end

        protocols.each_cons(2) do | p, p1 |
          p.next_protocol = p1
        end

        protocols.last.tap do |p|
          p.next_protocol = conn
        end

        protocols.each do |p|
          case
          when conn.is_a?(ShadowsocksRuby::Connections::TCP::ClientConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :tcp_receive_from_client, :async_recv
              def_delegator :@next_protocol, :tcp_send_to_client, :send_data
            end
          when conn.is_a?(ShadowsocksRuby::Connections::TCP::RemoteServerConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :tcp_receive_from_remoteserver, :async_recv
              def_delegator :@next_protocol, :tcp_send_to_remoteserver, :send_data
            end
          when conn.is_a?(ShadowsocksRuby::Connections::TCP::LocalBackendConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :tcp_receive_from_localbackend, :async_recv
              def_delegator :@next_protocol, :tcp_send_to_localbackend, :send_data
            end
          when conn.is_a?(ShadowsocksRuby::Connections::TCP::DestinationConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :tcp_receive_from_destination, :async_recv
              def_delegator :@next_protocol, :tcp_send_to_destination, :send_data
            end
          when conn.is_a?(ShadowsocksRuby::Connections::UDP::ClientConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :udp_receive_from_client, :async_recv
              def_delegator :@next_protocol, :udp_send_to_client, :send_data
            end
          when conn.is_a?(ShadowsocksRuby::Connections::UDP::RemoteServerConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :udp_receive_from_remoteserver, :async_recv
              def_delegator :@next_protocol, :udp_send_to_remoteserver, :send_data
            end
          when conn.is_a?(ShadowsocksRuby::Connections::UDP::LocalBackendConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :udp_receive_from_localbackend, :async_recv
              def_delegator :@next_protocol, :udp_send_to_localbackend, :send_data
            end
          when conn.is_a?(ShadowsocksRuby::Connections::UDP::DestinationConnection)
            class << p
              extend Forwardable
              def_delegator :@next_protocol, :udp_receive_from_destination, :async_recv
              def_delegator :@next_protocol, :udp_send_to_destination, :send_data
            end
          end
        end          
        
        protocols.first
      end
    end
  end
end