require "option_parser" require "ipc" require "socket" require "./colors" class WrappedTCPFileDescriptor < TCPSocket # do not close the connection when garbage collected!! def finalize # puts "WrappedTCPFileDescriptor garbage collection!!" # super end end service_name = "tcp" port_to_listen = 1234 OptionParser.parse do |parser| parser.on "-p port", "--port port", "Port to listen on." do |port| port_to_listen = port.to_u16 end parser.on "-s service-name", "--service-name service-name", "Service name." do |name| service_name = name end parser.on "-h", "--help", "Show this help" do puts parser exit 0 end end fdlist_client = [] of TCPSocket fdlist = [] of TCPSocket server = TCPServer.new("localhost", port_to_listen) service = IPC::SwitchingService.new service_name service << server.fd service.loop do |event| # TODO: remove closed tcp connections fdlist_client.select do |x| ! x.closed? end case event when IPC::Event::Connection puts "#{CBLUE}IPC::Event::Connection#{CRESET}" when IPC::Event::Disconnection puts "#{CBLUE}IPC::Event::Disconnection#{CRESET}" when IPC::Event::ExtraSocket puts "#{CBLUE}IPC::Event::ExtraSocket#{CRESET}" if server.fd == event.connection.fd client = server.accept fdlist << client service << client.fd puts "#{CBLUE}new client: #{client.fd}#{CRESET}" next end # since it's an external communication # we have to read the message here, it's not handled by default in libipc client = WrappedTCPFileDescriptor.new(fd: event.connection.fd, family: Socket::Family::INET) message = client.gets if message.nil? # disconnection puts "#{CBLUE}disconnection of client #{event.connection.fd}#{CRESET}" service.remove_fd event.connection.fd fdlist.select do |x| x.fd != event.connection.fd end client.close else message.chomp puts "#{CORANGE}message from client #{client.fd} (#{message.size} bytes): #{message}#{CRESET}" begin requested_service = message newservice = IPC::Connection.new requested_service service << newservice.fd service.switch.add event.connection.fd, newservice.fd client << "OK\n" rescue e puts "#{CRED}Exception during connection to the service: #{e}#{CRESET}" end # client << message end when IPC::Event::Switch puts "\033[36mIPC::Event::Switch#{CRESET}: from fd #{event.connection.fd}" # IPC::Event::Message has to be the last entry # because ExtraSocket and Switch inherit from Message class when IPC::Event::Message puts "#{CBLUE}IPC::Event::Message#{CRESET}: #{event.connection.fd}" puts "\033[33mconnection to the service: #{String.new event.message.payload}\033[00m" begin # should be in the format: service-name IP port payload = String.new event.message.payload uri = URI.parse payload.chomp host = uri.host port = uri.port host ||= "localhost" port ||= 9000 requested_service = uri.path.lchop newservice = TCPSocket.new(host, port) puts "sending the requested service to the remote tcpd: #{requested_service}" newservice << "#{requested_service}\n" puts "waiting for a response from the remote tcpd" response = newservice.gets if response.nil? raise "#{CRED}No response from the tcpd server#{CRESET}" end response.chomp puts "#{CGREEN}response from the remote tcpd: #{CRESET}#{response}" # TODO: when to remove this? This has to happen, memory leak otherwise # XXX: hint, check after a select for all dead connections fdlist_client << newservice # newservice = IPC::Connection.new requested_service service << newservice.fd service.switch.add event.connection.fd, newservice.fd # service.switch.print event.connection.send 1.to_u8, "OK" rescue e puts "\033[31mException: #{e}" event.connection.close end end end