Last active
December 18, 2015 20:39
-
-
Save danielwellman/5841538 to your computer and use it in GitHub Desktop.
Spike to create a Bane behavior with a full listen queue.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'socket' | |
# This Behavior implements the same interface as BehaviorServer since we must set up our connection | |
# differently than GServer (the base class of BehaviorServer) does. This means we won't be able to define | |
# this behavior in the same was as other behaviors. Something will have to change to unify these. | |
class FullListenQueue | |
def initialize(port) | |
@port = port | |
end | |
# Start the server and the bad behavior. | |
# | |
# This starts a server and then creates a client which immediately connects to the server, effectively | |
# filling the server's listen queue. | |
# | |
# Note that we make no attempt to check that our client _actually_ connected to the server. | |
# It's a race, and possible that someone from the outside could connect to our server. | |
# To fix this race, we could make the server expect a known secret key from the client, such as a timestamp. | |
# We would need to then make the server accept and handle connections in a non-blocking loop. | |
# | |
# Once this has been run, any client that tries to connect to this server should get a ECONNREFUSED error. | |
# | |
# Question: | |
# - We listen on all interfaces. If you listen only to localhost and try to connect from a remote machine, | |
# what error do you get? I'm wondering if 'not listening to outside connections' is distinguishable from | |
# 'listen queue is full'? | |
def start | |
@server = TCPServer.new('0.0.0.0', @port) # The server is now accepting connections | |
@server.listen(1) | |
@client = TCPSocket.new('localhost', @port) # This connects right away | |
end | |
def join | |
# TODO What will we join on? | |
end | |
def stop | |
@client.close | |
@server.close | |
end | |
end | |
if __FILE__ == $0 | |
server = FullListenQueue.new(3000) | |
server.start | |
puts 'Started, now sleeping...' | |
sleep 30 | |
#server.join | |
puts 'Shutting it down!' | |
server.stop | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'socket' | |
puts 'Creating a client' | |
client = TCPSocket.new('localhost', 3000) | |
puts 'Writing' | |
client.write('Anybody home?') | |
#puts 'Reading' | |
#client.read | |
puts 'Closing' | |
client.close | |
puts 'Done.' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you are a Bane user and reading this, I'd welcome your feedback:
This behavior spike will implement a full listen queue - any attempts to access this port will be denied since the TCP listen queue is already full.
To solve this, this behavior will create a server with a listen queue of size one, then immediately create a client that connects to the server and stays connected forever. Thus, the listen queue is filled up and any other clients who attempt to connect to the server will be denied.
There is a race condition here between line 30 and 32. The server is created but if another client is attempting to connect, it may steal the connection before the client on line 32 can connect.
To make this behavior more predictable to clients, we could prevent this - for example, by requiring some secret key that the client must pass to the server on connect. This would mean the server would need to drop connections until it got the right connection, and the client would need to try multiple times until it connected. This predictable behavior has a cost of a more complex client and server, so I ask -- is it worth it?