Class: AlchemyServer::Handler

Inherits:
EventMachine::Connection
  • Object
show all
Defined in:
lib/alchemy/handler.rb

Overview

This is an internal class that’s used by Alchemy::Server to handle the Memcached protocol and act as an interface between the Server and the Recipes.

Constant Summary collapse

ACCEPTED_COMMANDS =
["set", "add", "replace", "append", "prepend", "get", "delete", "flush_all", "version", "verbosity", "quit", "stats"].freeze
ERR_UNKNOWN_COMMAND =

ERRORs

"ERROR\r\n".freeze
ERR_BAD_CLIENT_FORMAT =
"CLIENT_ERROR bad command line format\r\n".freeze
ERR_SERVER_ISSUE =
"SERVER_ERROR %s\r\n"
GET_COMMAND =

GET

/\Aget (.{1,250})\s*\r\n/m
GET_RESPONSE =
"VALUE %s %s %s\r\n%s\r\nEND\r\n".freeze
GET_RESPONSE_EMPTY =
"END\r\n".freeze
SET_COMMAND =

SET

/\A(\w+) (.{1,250}) ([0-9]+) ([0-9]+) ([0-9]+)\r\n/m
SET_RESPONSE_SUCCESS =
"STORED\r\n".freeze
SET_RESPONSE_FAILURE =
"NOT STORED\r\n".freeze
SET_CLIENT_DATA_ERROR =
"CLIENT_ERROR bad data chunk\r\nERROR\r\n".freeze
DELETE_COMMAND =

DELETE

/\Adelete (.{1,250})\s?([0-9]*)\r\n/m
DELETE_RESPONSE_SUCCESS =
"DELETED\r\n".freeze
DELETE_RESPONSE_FAILURE =
"NOT_FOUND\r\n".freeze
FLUSH_COMMAND =

FLUSH

/\Aflush_all\s?([0-9]*)\r\n/m
FLUSH_RESPONSE =
"OK\r\n".freeze
VERSION_COMMAND =

VERSION

/\Aversion\r\n/m
VERSION_RESPONSE =
"VERSION #{VERSION}\r\n".freeze
VERBOSITY_COMMAND =

VERBOSITY

/\Averbosity\r\n/m
VERBOSITY_RESPONSE =
"OK\r\n".freeze
QUIT_COMMAND =

QUIT

/\Aquit\r\n/m
STATS_COMMAND =

STAT Response

/\Astats\r\n/m
STATS_RESPONSE =
"STAT pid %d
STAT uptime %d
STAT time %d
STAT version %s
STAT rusage_user %0.6f
STAT rusage_system %0.6f
STAT curr_items %d
STAT total_items %d
STAT bytes %d
STAT curr_connections %d
STAT total_connections %d
STAT cmd_get %d
STAT cmd_set %d
STAT get_hits %d
STAT get_misses %d
STAT bytes_read %d
STAT bytes_written %d
STAT limit_maxbytes %d
%sEND\r\n".freeze
LIST_STATS_RESPONSE =
"STAT list_%s_items %d
STAT list_%s_total_items %d
STAT list_%s_logsize %d
STAT list_%s_expired_items %d\n".freeze

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Handler

Creates a new handler for the MemCache protocol that communicates with a given client.



78
79
80
# File 'lib/alchemy/handler.rb', line 78

def initialize(options = {})
  @opts = options
end

Instance Method Details

#post_initObject

Process incoming commands from the attached client.



85
86
87
88
89
90
91
92
# File 'lib/alchemy/handler.rb', line 85

def post_init
  @server = @opts[:server]
  @expiry_stats = Hash.new(0)
  @expected_length = nil
  @server.stats[:total_connections] += 1
  set_comm_inactivity_timeout @opts[:timeout]
  @list_collection = @opts[:list]
end

#process(request, data) ⇒ Object



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
# File 'lib/alchemy/handler.rb', line 111

def process(request, data)
  case request
    when SET_COMMAND
      set($1, $2, $3, $4, $5.to_i, data)
    when GET_COMMAND
      get($1)
    when STATS_COMMAND
      stats
    when DELETE_COMMAND
      delete($1)
    when FLUSH_COMMAND
      flush_all
    when VERSION_COMMAND
      respond VERSION_RESPONSE
    when VERBOSITY_COMMAND
      respond VERBOSITY_RESPONSE
    when QUIT_COMMAND
      close_connection
      nil
    else
      logger.warn "Bad Format: #{data}."
      respond ERR_BAD_CLIENT_FORMAT
  end
  rescue => e
    logger.error "Error handling request: #{e}."
    logger.debug e.backtrace.join("\n")
    respond ERR_SERVER_ISSUE, e.to_s
end

#receive_data(incoming) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/alchemy/handler.rb', line 94

def receive_data(incoming)
  data = incoming
  
  ## Reject request if command isn't recognized
  if !ACCEPTED_COMMANDS.include?(data.split(" ").first)
    response = respond ERR_UNKNOWN_COMMAND
  elsif request_line = data.slice!(/.*?\r\n/m)
    response = process(request_line, data)
  else
    response = respond ERR_BAD_CLIENT_FORMAT
  end
  
  if response
    send_data response
  end
end