ftw 0.0.7 → 0.0.8

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.
data/README.md CHANGED
@@ -62,13 +62,29 @@ I do not plan on exposing any direct means for invoking SPDY.
62
62
 
63
63
  ## Server API
64
64
 
65
- Not sure yet...
65
+ I have implemented a rack server, Rack::Handler::FTW. It does not comply fully
66
+ with the Rack spec. See 'Rack Compliance Issues' below.
66
67
 
67
- Since Rack is not supported, I'll have to do a lot of legwork myself.
68
+ Under the FTW rack handler, there is an environment variable added,
69
+ "ftw.connection". This will be a FTW::Connection you can use for CONNECT,
70
+ Upgrades, etc.
68
71
 
69
- * Implement a proper Socket Server api
70
- * Implement a HTTP server on top of that (add SPDY support later)
71
- * Implement a Sinatra-like DSL on top of HTTP
72
+ There's also a websockets wrapper, FTW::WebSockets::Rack, that will help you
73
+ specifically with websocket requests and such.
74
+
75
+ ## Rack Compliance issues
76
+
77
+ Due to some awkward and bad requirements - specifically those around the
78
+ specified behavior of 'rack.input' - I can't support the rack specification fully.
79
+
80
+ The 'rack.input' must be an IO-like object supporting #rewind which rewinds to
81
+ the beginning of the request.
82
+
83
+ For high-data connections (like uploads, HTTP CONNECT, and HTTP Upgrade), it's
84
+ not practical to hold the entire history of time in a buffer. We'll run out of
85
+ memory, you crazy!
86
+
87
+ Details here: https://github.com/rack/rack/issues/347
72
88
 
73
89
  ## Other Projects
74
90
 
@@ -79,10 +95,3 @@ Here are some related projects that I have no affiliation with:
79
95
  * https://github.com/lifo/cramp - real-time web framework (async, websockets)
80
96
  * https://github.com/igrigorik/em-http-request - HTTP client for EventMachine
81
97
  * https://github.com/geemus/excon - http client library
82
-
83
- ## Missing Features
84
-
85
- * No Rack support, for now. There are technical requirements the Rack SPEC that
86
- prevent rack applications from really servicing uploads, HTTP Upgrades, etc.
87
- Details here: https://github.com/rack/rack/issues/347
88
-
data/lib/ftw/singleton.rb CHANGED
@@ -1,10 +1,22 @@
1
1
  require "ftw/namespace"
2
2
 
3
+ # A mixin that provides singleton-ness
4
+ #
5
+ # Usage:
6
+ #
7
+ # class Foo
8
+ # extend FTW::Singleton
9
+ #
10
+ # ...
11
+ # end
12
+ #
13
+ # foo = Foo.singleton
3
14
  module FTW::Singleton
4
15
  def self.included(klass)
5
16
  raise ArgumentError.new("In #{klass.name}, you want to use 'extend #{self.name}', not 'include ...'")
6
17
  end # def included
7
18
 
19
+ # Create a singleton instance of this class.
8
20
  def singleton
9
21
  @instance ||= self.new
10
22
  return @instance
data/lib/ftw/version.rb CHANGED
@@ -3,5 +3,5 @@ require "ftw/namespace"
3
3
  # :nodoc:
4
4
  module FTW
5
5
  # The version of this library
6
- VERSION = "0.0.7"
6
+ VERSION = "0.0.8"
7
7
  end
@@ -5,6 +5,7 @@
5
5
  # The opcode definitions come from:
6
6
  # http://tools.ietf.org/html/rfc6455#section-11.8
7
7
  module FTW::WebSocket::Constants
8
+ # websocket uuid, used in hash signing of websocket responses (RFC6455)
8
9
  WEBSOCKET_ACCEPT_UUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
9
10
 
10
11
  # Indication that this frame is a continuation in a fragmented message
@@ -3,11 +3,29 @@ require "ftw/websocket/parser"
3
3
  require "base64" # stdlib
4
4
  require "digest/sha1" # stdlib
5
5
 
6
+ # A websocket helper for Rack
7
+ #
8
+ # An example with Sinatra:
9
+ #
10
+ # get "/websocket/echo" do
11
+ # ws = FTW::WebSocket::Rack.new(env)
12
+ # stream(:keep_open) do |out|
13
+ # ws.each do |payload|
14
+ # # 'payload' is the text payload of a single websocket message
15
+ # # publish it back to the client
16
+ # ws.publish(payload)
17
+ # end
18
+ # end
19
+ # ws.rack_response
20
+ # end
6
21
  class FTW::WebSocket::Rack
7
22
  include FTW::WebSocket::Constants
8
23
 
9
24
  private
10
25
 
26
+ # Create a new websocket rack helper... thing.
27
+ #
28
+ # @param rack_env the 'env' bit given to your Rack application
11
29
  def initialize(rack_env)
12
30
  @env = rack_env
13
31
  @handshake_errors = []
@@ -28,16 +46,28 @@ class FTW::WebSocket::Rack
28
46
  @parser = FTW::WebSocket::Parser.new
29
47
  end # def initialize
30
48
 
49
+ # Test values for equality. This is used in handshake tests.
31
50
  def expect_equal(expected, actual, message)
32
51
  if expected != actual
33
52
  @handshake_errors << message
34
53
  end
35
54
  end # def expected
36
55
 
56
+ # Is this a valid handshake?
37
57
  def valid?
38
58
  return @handshake_errors.empty?
39
59
  end # def valid?
40
60
 
61
+ # Get the response Rack is expecting.
62
+ #
63
+ # If this was a valid websocket request, it will return a response
64
+ # that completes the HTTP portion of the websocket handshake.
65
+ #
66
+ # If this was an invalid websocket request, it will return a
67
+ # 400 status code and descriptions of what failed in the body
68
+ # of the response.
69
+ #
70
+ # @return [number, hash, body]
41
71
  def rack_response
42
72
  if valid?
43
73
  # Return the status, headers, body that is expected.
@@ -58,6 +88,15 @@ class FTW::WebSocket::Rack
58
88
  end
59
89
  end # def rack_response
60
90
 
91
+ # Enumerate each websocket payload (message).
92
+ #
93
+ # The payload of each message will be yielded to the block.
94
+ #
95
+ # Example:
96
+ #
97
+ # ws.each do |payload|
98
+ # puts "Received: #{payload}"
99
+ # end
61
100
  def each
62
101
  connection = @env["ftw.connection"]
63
102
  while true
@@ -68,6 +107,9 @@ class FTW::WebSocket::Rack
68
107
  end
69
108
  end # def each
70
109
 
110
+ # Publish a message over this websocket.
111
+ #
112
+ # @param message Publish a string message to the websocket.
71
113
  def publish(message)
72
114
  writer = FTW::WebSocket::Writer.singleton
73
115
  writer.write_text(@env["ftw.connection"], message)
@@ -29,7 +29,10 @@ class FTW::WebSocket::Writer
29
29
  include FTW::WebSocket::Constants
30
30
  extend FTW::Singleton
31
31
 
32
- #
32
+ # A list of valid modes. Used to validate input in #write_text.
33
+ #
34
+ # In :server mode, payloads are not masked. In :client mode, payloads
35
+ # are masked. Masking is described in RFC6455.
33
36
  VALID_MODES = [:server, :client]
34
37
 
35
38
  private
@@ -23,35 +23,56 @@ class Rack::Handler::FTW
23
23
  include FTW::Protocol
24
24
  include FTW::CRLF
25
25
 
26
+ # The version of the rack specification supported by this handler.
26
27
  RACK_VERSION = [1,1]
28
+
29
+ # A string constant value (used to avoid typos).
27
30
  REQUEST_METHOD = "REQUEST_METHOD".freeze
31
+ # A string constant value (used to avoid typos).
28
32
  SCRIPT_NAME = "SCRIPT_NAME".freeze
33
+ # A string constant value (used to avoid typos).
29
34
  PATH_INFO = "PATH_INFO".freeze
35
+ # A string constant value (used to avoid typos).
30
36
  QUERY_STRING = "QUERY_STRING".freeze
37
+ # A string constant value (used to avoid typos).
31
38
  SERVER_NAME = "SERVER_NAME".freeze
39
+ # A string constant value (used to avoid typos).
32
40
  SERVER_PORT = "SERVER_PORT".freeze
33
41
 
42
+ # A string constant value (used to avoid typos).
34
43
  RACK_DOT_VERSION = "rack.version".freeze
44
+ # A string constant value (used to avoid typos).
35
45
  RACK_DOT_URL_SCHEME = "rack.url_scheme".freeze
46
+ # A string constant value (used to avoid typos).
36
47
  RACK_DOT_INPUT = "rack.input".freeze
48
+ # A string constant value (used to avoid typos).
37
49
  RACK_DOT_ERRORS = "rack.errors".freeze
50
+ # A string constant value (used to avoid typos).
38
51
  RACK_DOT_MULTITHREAD = "rack.multithread".freeze
52
+ # A string constant value (used to avoid typos).
39
53
  RACK_DOT_MULTIPROCESS = "rack.multiprocess".freeze
54
+ # A string constant value (used to avoid typos).
40
55
  RACK_DOT_RUN_ONCE = "rack.run_once".freeze
56
+ # A string constant value (used to avoid typos).
41
57
  FTW_DOT_CONNECTION = "ftw.connection".freeze
42
58
 
59
+ # This method is invoked when rack starts this as the server.
43
60
  def self.run(app, config)
44
61
  server = self.new(app, config)
45
62
  server.run
46
- end
63
+ end # def self.run
47
64
 
48
65
  private
49
66
 
67
+ # setup a new rack server
50
68
  def initialize(app, config)
51
69
  @app = app
52
70
  @config = config
53
- end
71
+ end # def initialize
54
72
 
73
+ # Run the server.
74
+ #
75
+ # Connections are farmed out to threads.
55
76
  def run
56
77
  # {:environment=>"development", :pid=>nil, :Port=>9292, :Host=>"0.0.0.0",
57
78
  # :AccessLog=>[], :config=>"/home/jls/projects/ruby-ftw/examples/test.ru",
@@ -72,6 +93,11 @@ class Rack::Handler::FTW
72
93
  end
73
94
  end # def run
74
95
 
96
+ # Handle a new connection.
97
+ #
98
+ # This method parses http requests and passes them on to #handle_request
99
+ #
100
+ # @param connection The FTW::Connection being handled.
75
101
  def handle_connection(connection)
76
102
  while true
77
103
  begin
@@ -86,6 +112,8 @@ class Rack::Handler::FTW
86
112
  connection.disconnect("Fun")
87
113
  end # def handle_connection
88
114
 
115
+ # Handle a request. This will set up the rack 'env' and invoke the
116
+ # application associated with this handler.
89
117
  def handle_request(request, connection)
90
118
  path, query = request.path.split("?", 2)
91
119
  env = {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ftw
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors: