ftw 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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: