File: rubame.rb

package info (click to toggle)
ruby-rubame 0.0.3~git20131224.f3c78ba-2
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 112 kB
  • sloc: ruby: 280; makefile: 3
file content (223 lines) | stat: -rw-r--r-- 4,824 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
require 'websocket'
require 'socket'
require 'fiber'

module Rubame
  class Server
    def initialize(host, port)
      Socket.do_not_reverse_lookup
      @hostname = host
      @port = port

      @reading = []
      @writing = []

      @clients = {} # Socket as key, and Client as value

      @socket = TCPServer.new(@hostname, @port)
      @reading.push @socket
    end

    def accept
      socket = @socket.accept_nonblock
      @reading.push socket
      handshake = WebSocket::Handshake::Server.new
      client = Rubame::Client.new(socket, handshake, self)
      
      while line = socket.gets
        client.handshake << line
        break if client.handshake.finished?
      end
      if client.handshake.valid?
        @clients[socket] = client
        client.write handshake.to_s
        client.opened = true
        return client
      else
        close(client)
      end
      return nil
    end

    def read(client)

      pairs = client.socket.recvfrom(2000)
      messages = []

      if pairs[0].length == 0
        close(client)
      else
        client.frame << pairs[0]

        while f = client.frame.next
          if (f.type == :close)
            close(client)
            return messages
          else
            messages.push f
          end
        end
        
      end

      return messages

    end

    def close(client)
      @reading.delete client.socket
      @clients.delete client.socket
      begin
        client.socket.close
      rescue
      end
      client.closed = true
    end

    def run(time = 0, &blk)
      readable, writable = IO.select(@reading, @writing, nil, 0)

      if readable
        readable.each do |socket|
          client = @clients[socket]
          if socket == @socket
            client = accept
          else
            msg = read(client)
            client.messaged = msg
          end

          blk.call(client) if client and blk
        end
      end

      # Check for lazy send items
      timer_start = Time.now
      time_passed = 0
      begin
        @clients.each do |s, c|
          c.send_some_lazy(5)
        end
        time_passed = Time.now - timer_start
      end while time_passed < time
    end

    def stop
      @socket.close
    end
  end

  class Client
    attr_accessor :socket, :handshake, :frame, :opened, :messaged, :closed

    def initialize(socket, handshake, server)
      @socket = socket
      @handshake = handshake
      @frame = WebSocket::Frame::Incoming::Server.new(:version => @handshake.version)
      @opened = false
      @messaged = []
      @lazy_queue = []
      @lazy_current_queue = nil
      @closed = false
      @server = server
    end

    def write(data)
      @socket.write data
    end

    def send(data)
      frame = WebSocket::Frame::Outgoing::Server.new(:version => @handshake.version, :data => data, :type => :text)
      begin
        @socket.write frame
        @socket.flush
      rescue
        @server.close(self) unless @closed
      end
    end

    def lazy_send(data)
      @lazy_queue.push data
    end

    def get_lazy_fiber
      # Create the fiber if needed
      if @lazy_fiber == nil or !@lazy_fiber.alive?
        @lazy_fiber = Fiber.new do
          @lazy_current_queue.each do |data|
            send(data)
            Fiber.yield unless @lazy_current_queue[-1] == data
          end
        end
      end

      return @lazy_fiber
    end

    def send_some_lazy(count)
      # To save on cpu cycles, we don't want to be chopping and changing arrays, which could get quite large.  Instead,
      # we iterate over an array which we are sure won't change out from underneath us.
      unless @lazy_current_queue
        @lazy_current_queue = @lazy_queue
        @lazy_queue = []
      end

      completed = 0
      begin
        get_lazy_fiber.resume
        completed += 1
      end while (@lazy_queue.count > 0 or @lazy_current_queue.count > 0) and completed < count

    end

    def onopen(&blk)
      if @opened
        begin
          blk.call
        ensure
          @opened = false
        end
      end
    end

    def onmessage(&blk)
      if @messaged.size > 0
        begin
          @messaged.each do |x|
            blk.call(x.to_s)
          end
        ensure
          @messaged = []
        end
      end
    end

    def onclose(&blk)
      if @closed
        begin
          blk.call
        ensure
        end
      end
    end
  end

  line = 0
end

if __FILE__==$0
  server = Rubame::Server.new("0.0.0.0", 25222)
  while (!$quit)
    server.run do |client|
      client.onopen do
        puts "Server reports:  client open"
      end
      client.onmessage do |mess|
        puts "Server reports:  message received: #{mess}"
      end
      client.onclose do
        puts "Server reports:  client closed"
      end
    end
  end
end