websocket-server 1.0.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +185 -0
  4. data/Rakefile +56 -0
  5. data/exe/websocket +41 -0
  6. data/lib/log.rb +244 -0
  7. data/lib/server/mime_types.rb +38 -0
  8. data/lib/websocket/arguments_parser.rb +142 -0
  9. data/lib/websocket/channel_initializer.rb +73 -0
  10. data/lib/websocket/config.rb +59 -0
  11. data/lib/websocket/encoding.rb +21 -0
  12. data/lib/websocket/file_server_channel_progressive_future_listener.rb +32 -0
  13. data/lib/websocket/frame_handler.rb +71 -0
  14. data/lib/websocket/header_helpers.rb +70 -0
  15. data/lib/websocket/http_static_file_server_handler.rb +50 -0
  16. data/lib/websocket/http_static_file_server_handler_instance_methods.rb +160 -0
  17. data/lib/websocket/idle_handler.rb +41 -0
  18. data/lib/websocket/idle_state_user_event_handler.rb +47 -0
  19. data/lib/websocket/instance_methods.rb +127 -0
  20. data/lib/websocket/listenable.rb +41 -0
  21. data/lib/websocket/message_handler.rb +47 -0
  22. data/lib/websocket/response_helpers.rb +83 -0
  23. data/lib/websocket/server.rb +26 -0
  24. data/lib/websocket/shutdown_hook.rb +36 -0
  25. data/lib/websocket/ssl_cipher_inspector.rb +44 -0
  26. data/lib/websocket/ssl_context_initialization.rb +106 -0
  27. data/lib/websocket/telnet_proxy.rb +22 -0
  28. data/lib/websocket/validation_helpers.rb +51 -0
  29. data/lib/websocket/version.rb +16 -0
  30. data/lib/websocket-server.rb +13 -0
  31. data/lib/websocket_client.rb +478 -0
  32. data/lib/websocket_server.rb +50 -0
  33. data/web/client.html +43 -0
  34. data/web/css/client/console.css +167 -0
  35. data/web/css/client/parchment.css +112 -0
  36. data/web/favicon.ico +0 -0
  37. data/web/fonts/droidsansmono.v4.woff +0 -0
  38. data/web/js/client/ansispan.js +103 -0
  39. data/web/js/client/client.js +144 -0
  40. data/web/js/client/console.js +393 -0
  41. data/web/js/client/websocket.js +76 -0
  42. data/web/js/jquery.min.js +2 -0
  43. metadata +145 -0
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ require_relative 'idle_state_user_event_handler'
17
+
18
+ # The WebSocket module
19
+ module WebSocket
20
+ java_import Java::io.netty.handler.codec.http.websocketx.TextWebSocketFrame
21
+
22
+ # The IdleHandler class handles idle channels detected by the
23
+ # server pipeline.
24
+ class IdleHandler < WebSocket::IdleStateUserEventHandler
25
+ def initialize
26
+ super()
27
+ end
28
+
29
+ def handle_idle_channel(ctx, _evt)
30
+ klass = self.class.name
31
+ log.debug "#{klass} < IdleStateUserEventHandler ##{__method__}"
32
+ message = TextWebSocketFrame.new("\nDisconnecting idle session\n")
33
+ # TODO: Test
34
+ ctx.writeAndFlush(message).sync
35
+ ctx.close
36
+ # ctx.channel.writeAndFlush(message).sync
37
+ # ctx.channel.disconnect().awaitUninterruptibly()
38
+ # ctx.channel.close().awaitUninterruptibly()
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ # The WebSocket module
17
+ module WebSocket
18
+ java_import Java::io.netty.channel.ChannelDuplexHandler
19
+ java_import Java::io.netty.handler.codec.http.websocketx.PingWebSocketFrame
20
+ java_import Java::io.netty.handler.timeout.IdleState
21
+
22
+ # The PingMessage class is just a convenient alias
23
+ # for the PingWebSocketFrame class.
24
+ PingMessage = Class.new(PingWebSocketFrame)
25
+
26
+ # The IdleStateUserEventHandler class specifies methods implementing
27
+ # what to do when the pipeline detects an idle channel.
28
+ class IdleStateUserEventHandler < ChannelDuplexHandler
29
+ def initialize
30
+ super()
31
+ end
32
+
33
+ # java_signature 'public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception'
34
+ def userEventTriggered(ctx, evt)
35
+ return unless evt.respond_to?(:state)
36
+ case evt.state
37
+ when IdleState::READER_IDLE
38
+ return handle_idle_channel(ctx, evt) if respond_to?(:handle_idle_channel)
39
+ message = TextWebSocketFrame.new("\nDisconnecting idle session\n")
40
+ ctx.writeAndFlush(message).sync
41
+ ctx.close
42
+ when IdleState::WRITER_IDLE
43
+ ctx.writeAndFlush(PingMessage.new)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,127 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ require_relative 'channel_initializer'
17
+ require_relative 'shutdown_hook'
18
+
19
+ # The WebSocket module
20
+ module WebSocket
21
+ java_import Java::io.netty.bootstrap.ServerBootstrap
22
+ java_import Java::io.netty.channel.group.DefaultChannelGroup
23
+ java_import Java::io.netty.channel.nio.NioEventLoopGroup
24
+ java_import Java::io.netty.handler.logging.LoggingHandler
25
+ java_import Java::io.netty.handler.logging.LogLevel
26
+ java_import Java::io.netty.util.concurrent.GlobalEventExecutor
27
+
28
+ # The InstanceMethods module
29
+ module InstanceMethods
30
+ def configure_handlers(&block)
31
+ add_listener(self)
32
+ channel_initializer << block if block_given?
33
+ end
34
+
35
+ def bootstrap
36
+ @bootstrap = ServerBootstrap.new
37
+ @bootstrap.group(boss_group, worker_group)
38
+ @bootstrap.channel(Server::CHANNEL_TYPE)
39
+ @bootstrap.handler(logging_handler) if @options[:log_requests]
40
+ @bootstrap.childHandler(channel_initializer)
41
+ end
42
+
43
+ def channel_initializer
44
+ @channel_initializer ||= ::WebSocket::ChannelInitializer.new(channel_group, @options)
45
+ end
46
+
47
+ def boss_group
48
+ @boss_group ||= NioEventLoopGroup.new(2)
49
+ end
50
+
51
+ def worker_group
52
+ @worker_group ||= NioEventLoopGroup.new
53
+ end
54
+
55
+ def channel_group
56
+ @channel_group ||= DefaultChannelGroup.new('server_channels', GlobalEventExecutor::INSTANCE)
57
+ end
58
+
59
+ def logging_handler
60
+ LoggingHandler.new(LogLevel::INFO)
61
+ end
62
+
63
+ # rubocop: disable Metrics/AbcSize
64
+ # rubocop: disable Metrics/MethodLength
65
+ def run(params = {})
66
+ options.merge!(params)
67
+ channel = bootstrap.bind(port).sync().channel()
68
+ channel_group.add(channel)
69
+ ::WebSocket::ShutdownHook.new(self)
70
+ log.info "Listening on #{channel.local_address}"
71
+ channel.closeFuture().sync()
72
+ rescue java.net.BindException => e
73
+ raise "Bind error: #{e.message}: #{options[:host]}:#{port}"
74
+ rescue java.net.SocketException => e
75
+ raise "Socket error: #{e.message}: #{options[:host]}:#{port}"
76
+ ensure
77
+ stop
78
+ end
79
+ # rubocop: enable Metrics/AbcSize
80
+ # rubocop: enable Metrics/MethodLength
81
+
82
+ IntegerPattern = /^\d+$/.freeze
83
+
84
+ def valid_port?(val)
85
+ IntegerPattern.match?(val.to_s) && val.positive? && val < 65_536
86
+ end
87
+
88
+ def given_port
89
+ value = (options[:ssl] ? options[:ssl_port] : options[:port]).to_i
90
+ raise 'Given port parameter is invalid' unless valid_port?(value)
91
+ value
92
+ end
93
+
94
+ def port
95
+ @port ||= given_port
96
+ end
97
+
98
+ def shutdown
99
+ channel_group.disconnect().awaitUninterruptibly()
100
+ channel_group.close().awaitUninterruptibly()
101
+ end
102
+
103
+ def stop
104
+ boss_group&.shutdownGracefully()
105
+ worker_group&.shutdownGracefully()
106
+ end
107
+
108
+ def <<(handler)
109
+ channel_initializer.handlers << handler
110
+ end
111
+
112
+ def add_listener(listener)
113
+ channel_initializer.add_listener(listener)
114
+ end
115
+
116
+ # def all_exist?(*files)
117
+ # files.all? { |f| File.exist?(f) }
118
+ # end
119
+
120
+ # def init_ssl_context(certificate, private_key)
121
+ # return certificate_key_pair(certificate, private_key) if all_exist?(certificate, private_key)
122
+ # options[:use_jdk_ssl_provider] ? jdk_ssl_provider : self_signed_certificate
123
+ # end
124
+ end
125
+ # module ServerInstanceMethods
126
+ end
127
+ # module WebSocket
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+
15
+ # The WebSocket module
16
+ module WebSocket
17
+ # The Listenable module
18
+ module Listenable
19
+ def listeners
20
+ @listeners ||= java.util.concurrent.CopyOnWriteArrayList.new
21
+ end
22
+
23
+ def add_listener(listener)
24
+ listeners << listener
25
+ end
26
+
27
+ def remove_listener(listener)
28
+ listeners.delete(listener)
29
+ end
30
+
31
+ def notify(message, *args)
32
+ return if listeners.empty?
33
+ log.trace "Notifying listeners (#{listeners}) of message: #{message}"
34
+ listeners.each do |listener|
35
+ listener.send(message.to_sym, *args) if listener.respond_to?(message.to_sym)
36
+ end
37
+ end
38
+ end
39
+ # module Listenable
40
+ end
41
+ # module WebSocket
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ require_relative 'frame_handler'
17
+
18
+ # The WebSocket module
19
+ module WebSocket
20
+ java_import Java::io.netty.channel.ChannelHandler
21
+ java_import Java::io.netty.channel.ChannelFutureListener
22
+ java_import Java::io.netty.handler.codec.http.websocketx.TextWebSocketFrame
23
+
24
+ # The MessageHandler class provides a method for specifying code
25
+ # to handle incoming messages and respond with the results from
26
+ # the given code.
27
+ class MessageHandler < WebSocket::FrameHandler
28
+ include ChannelHandler
29
+ include ChannelFutureListener
30
+
31
+ def initialize(&handler)
32
+ super()
33
+ @handler = handler
34
+ end
35
+
36
+ def handle_message(ctx, message)
37
+ request = message&.to_s&.strip
38
+ return if request.nil?
39
+ response = @handler.call(ctx, request)
40
+ return if response.nil?
41
+ log.debug "#{self.class}#handle_message response: #{response.chomp}"
42
+ ctx.channel.writeAndFlush(TextWebSocketFrame.new(response))
43
+ response
44
+ end
45
+ end
46
+ end
47
+ # module WebSocket
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ require_relative 'encoding'
17
+
18
+ # The WebSocket module
19
+ module WebSocket
20
+ java_import Java::io.netty.channel.ChannelFutureListener
21
+ java_import Java::io.netty.buffer.Unpooled
22
+ java_import Java::io.netty.handler.codec.http.DefaultFullHttpResponse
23
+ java_import Java::io.netty.handler.codec.http.HttpHeaderNames
24
+ java_import Java::io.netty.handler.codec.http.HttpVersion
25
+ java_import Java::io.netty.util.CharsetUtil
26
+
27
+ # The ResponseHelpers helpers
28
+ module ResponseHelpers
29
+ NonSpacesBeforeEOLPattern = %r{([^\s]+)$}.freeze
30
+
31
+ def index_listing(path)
32
+ results = `ls -la #{path}`.strip.split("\n")
33
+ results.shift
34
+ index = results.collect do |s|
35
+ s.gsub(NonSpacesBeforeEOLPattern) do |resource_name|
36
+ %(<a href="#{resource_name}">#{resource_name}</a>)
37
+ end
38
+ end.join '<br />'
39
+ "<html><pre>#{index}</pre></html>"
40
+ end
41
+
42
+ def send_listing(ctx, path)
43
+ response = DefaultFullHttpResponse.new(HttpVersion::HTTP_1_1, HttpResponseStatus::OK)
44
+ response.headers().set(HttpHeaderNames::CONTENT_TYPE, WebSocket::HtmlContentType)
45
+
46
+ html = index_listing(path)
47
+
48
+ buffer = Unpooled.copiedBuffer(html, WebSocket::Encoding)
49
+ response.content().writeBytes(buffer)
50
+ buffer.release()
51
+
52
+ # Close the connection as soon as the error message is sent.
53
+ ctx.writeAndFlush(response).addListener(ChannelFutureListener::CLOSE)
54
+ end
55
+
56
+ def send_redirect(ctx, redirect_to_uri)
57
+ response = DefaultFullHttpResponse.new(HttpVersion::HTTP_1_1, HttpResponseStatus::FOUND)
58
+ response.headers().set(HttpHeaderNames::LOCATION, redirect_to_uri)
59
+
60
+ # Close the connection as soon as the error message is sent.
61
+ ctx.writeAndFlush(response).addListener(ChannelFutureListener::CLOSE)
62
+ end
63
+
64
+ def send_error(ctx, status)
65
+ message = Unpooled.copiedBuffer("#{status}\r\n", CharsetUtil::UTF_8)
66
+ response = DefaultFullHttpResponse.new(HttpVersion::HTTP_1_1, status, message)
67
+ response.headers().set(HttpHeaderNames::CONTENT_TYPE, WebSocket::HtmlContentType)
68
+
69
+ # Close the connection as soon as the error message is sent.
70
+ ctx.writeAndFlush(response).addListener(ChannelFutureListener::CLOSE)
71
+ end
72
+
73
+ def send_not_modified(ctx, date)
74
+ response = DefaultFullHttpResponse.new(HttpVersion::HTTP_1_1, HttpResponseStatus::NOT_MODIFIED)
75
+ date_header(response, date)
76
+
77
+ # Close the connection as soon as the error message is sent.
78
+ ctx.writeAndFlush(response).addListener(ChannelFutureListener::CLOSE)
79
+ end
80
+ end
81
+ # module ResponseHelpers
82
+ end
83
+ # module WebSocket
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+ require 'tcp-server'
16
+
17
+ require_relative 'instance_methods'
18
+
19
+ # The WebSocket module
20
+ module WebSocket
21
+ # The Server class sets up the netty server
22
+ class Server < ::Server::Server
23
+ include ::WebSocket::InstanceMethods
24
+ end
25
+ end
26
+ # module WebSocket
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ # The WebSocket module
17
+ module WebSocket
18
+ # The ShutdownHook class specifies a routine to be invoked when the
19
+ # java runtime is shutdown.
20
+ class ShutdownHook < java.lang.Thread
21
+ attr_reader :server
22
+
23
+ def initialize(server)
24
+ super()
25
+ @server = server
26
+ java.lang.Runtime.runtime.add_shutdown_hook(self)
27
+ end
28
+
29
+ def run
30
+ $stdout.write "\r\e[0K"
31
+ $stdout.flush
32
+ ::WebSocket::Server.log.info 'Shutting down'
33
+ server.shutdown if server.respond_to?(:shutdown)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ # The WebSocket module
17
+ module WebSocket
18
+ java_import Java::io.netty.channel.SimpleChannelInboundHandler
19
+ java_import Java::io.netty.handler.ssl.SslHandler
20
+
21
+ # The SslCipherInspector class enables debugging the details around
22
+ # cipher configuration and pipeline handling of SSL handshakes.
23
+ class SslCipherInspector < SimpleChannelInboundHandler
24
+ def initialize
25
+ super()
26
+ end
27
+
28
+ def channelRead(ctx, msg)
29
+ cipher_suite = cipher_suite ctx
30
+ log.info "Server communications are secured by #{cipher_suite}"
31
+ ctx.fireChannelRead(msg)
32
+ end
33
+
34
+ private
35
+
36
+ def cipher_suite(ctx)
37
+ # Confirm the SSL context has been loaded into the channel pipeline
38
+ ssl_handler = ctx.pipeline.get(SslHandler.java_class)
39
+ ssl_engine = ssl_handler.engine()
40
+ ssl_session = ssl_engine.getSession()
41
+ ssl_session.getCipherSuite()
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ # The WebSocket module
17
+ module WebSocket
18
+ # This module implements methods responsible for the
19
+ # initialization of an SSL context
20
+ module SslContextInitializationMethods
21
+ java_import Java::io.netty.handler.ssl.SslContextBuilder
22
+ java_import Java::io.netty.handler.ssl.SslProvider
23
+ java_import Java::io.netty.handler.ssl.util.SelfSignedCertificate
24
+ java_import java.io.FileInputStream
25
+ java_import java.io.BufferedInputStream
26
+ java_import java.io.ByteArrayOutputStream
27
+ java_import java.security.KeyFactory
28
+ java_import java.security.cert.CertificateFactory
29
+ java_import java.security.spec.PKCS8EncodedKeySpec
30
+
31
+ DefaultCertificateType = 'X.509'.freeze
32
+
33
+ def init_ssl(channel_initializer)
34
+ return unless options[:ssl] && channel_initializer.respond_to?(:ssl_context)
35
+ context = ssl_context
36
+ channel_initializer.ssl_context = context unless context.nil?
37
+ end
38
+
39
+ def ssl_context
40
+ @ssl_context ||= init_ssl_context(options[:ssl_certificate_file_path], options[:ssl_private_key_file_path])
41
+ end
42
+
43
+ def certificate_key_pair(certificate, private_key)
44
+ log.info "Securing socket layer using #{certificate} and #{private_key}"
45
+ certificate = read_certificate(certificate)
46
+ private_key = read_private_key(private_key)
47
+ SslContextBuilder.forServer(private_key, certificate).build()
48
+ end
49
+
50
+ def jdk_ssl_provider
51
+ log.info 'Securing socket layer using JDK self-signed certificate'
52
+ ssc = SelfSignedCertificate.new
53
+ context_builder = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
54
+ context_builder.sslProvider(SslProvider::JDK).build()
55
+ end
56
+
57
+ def self_signed_certificate
58
+ log.info 'Securing socket layer using self-signed certificate'
59
+ ssc = SelfSignedCertificate.new
60
+ SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build()
61
+ end
62
+
63
+ def read_certificate(file_path)
64
+ file_input_stream = FileInputStream.new(file_path)
65
+ buffered_input_stream = BufferedInputStream.new(file_input_stream)
66
+ CertificateFactory.getInstance(DefaultCertificateType).generateCertificate(buffered_input_stream)
67
+ end
68
+
69
+ def read_private_key(file_path)
70
+ file_input_stream = FileInputStream.new(file_path)
71
+ buffered_input_stream = BufferedInputStream.new(file_input_stream)
72
+ decode_private_key(stream_data(buffered_input_stream))
73
+ ensure
74
+ close_streams(buffered_input_stream, file_input_stream)
75
+ end
76
+
77
+ def stream_data(input_stream, output_stream = ByteArrayOutputStream.new)
78
+ buffer = Java::byte[1024 * 4].new
79
+ loop do
80
+ n = input_stream.read(buffer)
81
+ break if n == -1
82
+ output_stream.write(buffer, 0, n)
83
+ end
84
+ output_stream.toByteArray()
85
+ ensure
86
+ close_streams(output_stream)
87
+ end
88
+
89
+ def close_streams(*streams)
90
+ streams.each { |stream| safely_close_stream(stream) }
91
+ end
92
+
93
+ def safely_close_stream(stream)
94
+ stream.close()
95
+ rescue StandardError => e
96
+ log.warn "Failed to close stream: #{e.message}"
97
+ end
98
+
99
+ def decode_private_key(encoded_private_key)
100
+ private_key_spec = PKCS8EncodedKeySpec.new(encoded_private_key)
101
+ KeyFactory.getInstance('RSA').generatePrivate(private_key_spec)
102
+ end
103
+ end
104
+ # module SslContextInitializationMethods
105
+ end
106
+ # module WebSocket
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ # The WebSocket module
14
+ module WebSocket
15
+ # The TelnetProxy class adds a handler to the server channel
16
+ # pipeline which will proxy incoming requests to a telnet server.
17
+ class TelnetProxy
18
+ def initialize(host, port)
19
+ pipeline << TelnetProxyFrontendHandler.new(host, port)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ require 'java'
14
+ require 'netty'
15
+
16
+ require_relative 'encoding'
17
+
18
+ # The WebSocket module
19
+ module WebSocket
20
+ # The ValidationHelpers module
21
+ module ValidationHelpers
22
+ FileSeparatorDotPattern = %r{#{File::SEPARATOR}\.}.freeze
23
+ DotFileSeparatorPattern = %r{\.#{File::SEPARATOR}}.freeze
24
+
25
+ # Simplistic dumb security check.
26
+ # Something more serious is required in a production environment.
27
+ def insecure_uri?(uri)
28
+ FileSeparatorDotPattern.match?(uri) ||
29
+ DotFileSeparatorPattern.match?(uri) ||
30
+ uri.start_with?('.') ||
31
+ uri.end_with?('.') ||
32
+ options[:insecure_uri_pattern].match?(uri)
33
+ end
34
+
35
+ QueryStringPattern = %r{\?.*}.freeze
36
+ ForwardSlashPattern = %r{/}.freeze
37
+
38
+ def sanitize_uri(uri)
39
+ # Decode the path.
40
+ uri = CGI.unescape(uri.gsub(QueryStringPattern, ''), WebSocket::Encoding.name)
41
+ return nil if uri.empty? || !uri.start_with?('/')
42
+ # Convert file separators.
43
+ uri = uri.gsub(ForwardSlashPattern, File::SEPARATOR)
44
+ return nil if insecure_uri?(uri)
45
+ # Convert to absolute path.
46
+ File.join(options[:web_root], uri)
47
+ end
48
+ end
49
+ # module ValidationHelpers
50
+ end
51
+ # module WebSocket
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # -*- mode: ruby -*-
5
+ # vi: set ft=ruby :
6
+
7
+ # =begin
8
+ #
9
+ # Copyright Nels Nelson 2016-2022 but freely usable (see license)
10
+ #
11
+ # =end
12
+
13
+ # The WebSocket module
14
+ module WebSocket
15
+ VERSION = '1.0.1'.freeze
16
+ end