class MemcachedServer::Server

Class that wraps up a Memcached server

Attributes

hostname[R]

The server hostname or IP address

@return [String, ipaddress]

mc[R]

The Memcache object that implements the logic of the Memcache protocol

@return [MemcachedServer::Memcache]

port[R]

The server port

@return [port]

Public Class Methods

new(hostname, port) click to toggle source
# 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

Public Instance Methods

accept() click to toggle source

Accepts a connection @return [TCPSocket] An accepted TCPSocket for the incoming connection.

# File lib/memcached-server/server.rb, line 194
def accept()
    return @connection.accept()
end
read_bytes(connection, bytes) click to toggle source

Reads <bytes> bytes from <connection>

@param connection [TCPSocket] Client's socket @param bytes [Integer] The number of bytes to read @return [String, nil] The message read

# 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
run() click to toggle source

Starts the server

# 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) click to toggle source

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

@param connection [TCPSocket] Client's socket @param valid_command [MatchData] It encapsulates all the results of a valid command pattern match @return [Boolean] false if valid_command != END else true

# 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) click to toggle source

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

@param command [String] A command to validate @return [MatchData, nil] It encapsulates all the results of a valid command pattern match

# 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