Class: MemcachedServer::Server

Inherits:
Object
  • Object
show all
Defined in:
lib/memcached-server/server.rb

Overview

Class that wraps up a Memcached server

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hostname, port) ⇒ Server

Returns a new instance of Server.



25
26
27
28
29
30
31
32
# File 'lib/memcached-server/server.rb', line 25

def initialize(hostname, port)

    @hostname = hostname
    @port = port
    @connection = TCPServer.new(hostname, port)
    @mc = Memcache.new()
    
end

Instance Attribute Details

#hostnameString, ipaddress (readonly)

The server hostname or IP address

Returns:

  • (String, ipaddress)


13
14
15
# File 'lib/memcached-server/server.rb', line 13

def hostname
  @hostname
end

#mcMemcachedServer::Memcache (readonly)

The Memcache object that implements the logic of the Memcache protocol



23
24
25
# File 'lib/memcached-server/server.rb', line 23

def mc
  @mc
end

#portport (readonly)

The server port

Returns:



18
19
20
# File 'lib/memcached-server/server.rb', line 18

def port
  @port
end

Instance Method Details

#acceptTCPSocket

Accepts a connection

Returns:

  • (TCPSocket)

    An accepted TCPSocket for the incoming connection.



194
195
196
# File 'lib/memcached-server/server.rb', line 194

def accept()
    return @connection.accept()
end

#read_bytes(connection, bytes) ⇒ String?

Reads <bytes> bytes from <connection>

Parameters:

  • connection (TCPSocket)

    Client’s socket

  • bytes (Integer)

    The number of bytes to read

Returns:

  • (String, nil)

    The message read



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/memcached-server/server.rb', line 161

def read_bytes(connection, bytes)

    data_chunk = connection.read(bytes + 1).chomp()

    if data_chunk.bytesize() != bytes
        connection.puts(Error::CLIENT_ERROR % [" bad data chunk"])
        return nil
    end

    return data_chunk
end

#runObject

Starts the server



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
# File 'lib/memcached-server/server.rb', line 35

def run()
    
    begin
        loop do
            Thread.start(@connection.accept()) do | connection |

                puts("New connection: #{connection.to_s}.")

                close = false
                while command = connection.gets()

                    puts("Command: #{command} | Connection: #{connection.to_s}")

                    valid_command = validate_command(command)
                    if valid_command
                        close = run_command(connection, valid_command)
                    else
                        connection.puts(Error::ERROR)
                    end

                    break if close

                end

                connection.puts(Reply::END_)
                connection.close()
                puts ("Connection closed to: #{connection}.")

            end
        end
        
    rescue => exception
        error = Error::SERVER_ERROR % exception.message
        connection.puts(error)
    end
end

#run_command(connection, valid_command) ⇒ Boolean

Runs a valid memcache command. Depends on MemcachedServer::Memcache method names. In some cases, when the #send method is used, the corresponding MemcachedServer::Memcache method must be equal to valid_command

Parameters:

  • connection (TCPSocket)

    Client’s socket

  • valid_command (MatchData)

    It encapsulates all the results of a valid command pattern match

Returns:



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
# File 'lib/memcached-server/server.rb', line 80

def run_command(connection, valid_command)

    name = valid_command[:name]

    case name
    when 'set', 'add', 'replace'

        key = valid_command[:key]
        flags = valid_command[:flags].to_i
        exptime = valid_command[:exptime].to_i
        bytes = valid_command[:bytes].to_i
        noreply = !valid_command[:noreply].nil?
        data = self.read_bytes(connection, bytes)

        reply = @mc.send(name.to_sym, key, flags, exptime, bytes, data) unless data.nil?()
        connection.puts(reply) unless noreply || reply.nil?()

        return false

    when 'append', 'prepend'

        key = valid_command[:key]
        bytes = valid_command[:bytes].to_i
        data = self.read_bytes(connection, bytes)

        reply = @mc.send(name.to_sym, key, bytes, data) unless data.nil?()
        connection.puts(reply) unless noreply || reply.nil?()

        return false

    when 'cas'

        key = valid_command[:key]
        flags = valid_command[:flags].to_i
        exptime = valid_command[:exptime].to_i
        bytes = valid_command[:bytes].to_i
        noreply = !valid_command[:noreply].nil?
        data = self.read_bytes(connection, bytes)
        cas_id = valid_command[:cas_id].to_i()

        reply = @mc.cas(key, flags, exptime, bytes, cas_id, data) unless data.nil?()
        connection.puts(reply) unless noreply || reply.nil?()

        return false
        
    when 'get'

        keys = valid_command[:keys].split(' ')
        items = @mc.get(keys)

        for item in items
            connection.puts(Reply::GET % [item.key, item.flags, item.bytes, item.data_block]) if item
            connection.puts(Reply::END_)
        end

        return false

    when 'gets'

        keys = valid_command[:keys].split(' ')
        items = @mc.get(keys)

        for item in items
            connection.puts(Reply::GETS % [item.key, item.flags, item.bytes, item.cas_id, item.data_block]) if item
            connection.puts(Reply::END_)
        end

        return false

    else
        # END command stops run
        return true
        
    end
end

#validate_command(command) ⇒ MatchData?

Validates a command. If the command isn’t valid it returns nil.

Parameters:

  • command (String)

    A command to validate

Returns:

  • (MatchData, nil)

    It encapsulates all the results of a valid command pattern match



178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/memcached-server/server.rb', line 178

def validate_command(command)

    valid_formats = CommandFormat.constants.map{| key | CommandFormat.const_get(key)}

    valid_formats.each do | form |

        valid_command = command.match(form)
        return valid_command unless valid_command.nil?
        
    end

    return nil
end