All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.twitter.finagle.memcached.protocol.text.server.DecodingToCommand.scala Maven / Gradle / Ivy

There is a newer version: 21.2.0
Show newest version
package com.twitter.finagle.memcached.protocol.text.server

import scala.Function.tupled

import org.jboss.netty.handler.codec.oneone.OneToOneDecoder
import org.jboss.netty.channel.{Channel, ChannelHandlerContext}

import com.twitter.conversions.time._
import com.twitter.finagle.memcached.protocol._
import com.twitter.finagle.memcached.util.Bufs.RichBuf
import com.twitter.finagle.memcached.util.ParserUtils
import com.twitter.io.Buf
import com.twitter.util.Time

import text.{TokensWithData, Tokens}

object DecodingToCommand {
  private val NOREPLY = Buf.Utf8("noreply")
  private val SET     = Buf.Utf8("set")
  private val ADD     = Buf.Utf8("add")
  private val REPLACE = Buf.Utf8("replace")
  private val APPEND  = Buf.Utf8("append")
  private val PREPEND = Buf.Utf8("prepend")
  private val GET     = Buf.Utf8("get")
  private val GETS    = Buf.Utf8("gets")
  private val DELETE  = Buf.Utf8("delete")
  private val INCR    = Buf.Utf8("incr")
  private val DECR    = Buf.Utf8("decr")
  private val QUIT    = Buf.Utf8("quit")
  private val STATS   = Buf.Utf8("stats")
}

abstract class AbstractDecodingToCommand[C <: AnyRef] extends OneToOneDecoder {
  import ParserUtils._

  // Taken from memcached.c
  private val RealtimeMaxdelta = 60*60*24*30

  def decode(ctx: ChannelHandlerContext, ch: Channel, m: AnyRef) = m match {
    case Tokens(tokens) => parseNonStorageCommand(tokens)
    case TokensWithData(tokens, data, _/*ignore CAS*/) => parseStorageCommand(tokens, data)
  }

  protected def parseNonStorageCommand(tokens: Seq[Buf]): C
  protected def parseStorageCommand(tokens: Seq[Buf], data: Buf): C

  protected def validateStorageCommand(tokens: Seq[Buf], data: Buf) = {
    val expiry = tokens(2).toInt match {
      case 0 => 0.seconds.afterEpoch
      case unixtime if unixtime > RealtimeMaxdelta => Time.fromSeconds(unixtime)
      case delta => delta.seconds.fromNow
    }
    (tokens(0), tokens(1).toInt, expiry, data)
  }

  protected def validateDeleteCommand(tokens: Seq[Buf]): Buf = {
    if (tokens.size < 1) throw new ClientError("No key")
    if (tokens.size == 2 && !isDigits(tokens.last)) throw new ClientError("Timestamp is poorly formed")
    if (tokens.size > 2) throw new ClientError("Too many arguments")

    tokens.head
  }
}

class DecodingToCommand extends AbstractDecodingToCommand[Command] {
  import DecodingToCommand._
  import ParserUtils._

  private[this] def validateArithmeticCommand(tokens: Seq[Buf]) = {
    if (tokens.size < 2) throw new ClientError("Too few arguments")
    if (tokens.size == 3 && tokens.last != NOREPLY) throw new ClientError("Too many arguments")
    if (!isDigits(tokens(1))) throw new ClientError("Delta is not a number")

    (tokens.head, tokens(1).toLong)
  }

  private[this] def validateAnyStorageCommand(tokens: Seq[Buf]) {
    if (tokens.isEmpty) throw new ClientError("No arguments specified")
  }

  protected def parseStorageCommand(tokens: Seq[Buf], data: Buf) = {
    validateAnyStorageCommand(tokens)
    val commandName = tokens.head
    val args = tokens.tail
    commandName match {
      case SET       => tupled(Set)(validateStorageCommand(args, data))
      case ADD       => tupled(Add)(validateStorageCommand(args, data))
      case REPLACE   => tupled(Replace)(validateStorageCommand(args, data))
      case APPEND    => tupled(Append)(validateStorageCommand(args, data))
      case PREPEND   => tupled(Prepend)(validateStorageCommand(args, data))
      case _         => throw new NonexistentCommand(Buf.slowHexString(commandName))
    }
  }

  protected def parseNonStorageCommand(tokens: Seq[Buf]) = {
    validateAnyStorageCommand(tokens)
    val commandName = tokens.head
    val args = tokens.tail
    commandName match {
      case GET     => Get(args)
      case GETS    => Gets(args)
      case DELETE  => Delete(validateDeleteCommand(args))
      case INCR    => tupled(Incr)(validateArithmeticCommand(args))
      case DECR    => tupled(Decr)(validateArithmeticCommand(args))
      case QUIT    => Quit()
      case STATS   => Stats(args)
      case _       => throw new NonexistentCommand(Buf.slowHexString(commandName))
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy