<commit_before>package xitrum.handler

import org.jboss.netty.channel.{Channels, ChannelPipeline, ChannelPipelineFactory => CPF}
import org.jboss.netty.handler.codec.http.{HttpRequestDecoder, HttpChunkAggregator, HttpResponseEncoder}
import org.jboss.netty.handler.execution.{ExecutionHandler, OrderedMemoryAwareThreadPoolExecutor}
import org.jboss.netty.handler.stream.ChunkedWriteHandler

import xitrum.Config
import xitrum.handler.up._
import xitrum.handler.down._

object DefaultHttpChannelPipelineFactory {
  def removeUnusedHandlersForWebSocket(pipeline: ChannelPipeline) {
    // WebSocket handshaker in Netty dynamically changes the pipeline like this:
    // pipeline.remove(classOf[HttpChunkAggregator])
    // pipeline.replace(classOf[HttpRequestDecoder],  "wsdecoder", new WebSocket08FrameDecoder(true, this.allowExtensions))
    // pipeline.replace(classOf[HttpResponseEncoder], "wsencoder", new WebSocket08FrameEncoder(false))

    // Upstream
    pipeline.remove(classOf[NoPipelining])
    pipeline.remove(classOf[BaseUrlRemover])
    if (Config.xitrum.basicAuth.isDefined)
    pipeline.remove(classOf[BasicAuth])
    pipeline.remove(classOf[PublicFileServer])
    pipeline.remove(classOf[PublicResourceServer])
    pipeline.remove(classOf[Request2Env])
    pipeline.remove(classOf[UriParser])
    pipeline.remove(classOf[BodyParser])
    pipeline.remove(classOf[MethodOverrider])
    pipeline.remove(classOf[Dispatcher])

    // Downstream
    pipeline.remove(classOf[ChunkedWriteHandler])
    pipeline.remove(classOf[FixiOS6SafariPOST])
    pipeline.remove(classOf[XSendFile])
    pipeline.remove(classOf[XSendResource])
    pipeline.remove(classOf[Env2Response])
    pipeline.remove(classOf[ResponseCacher])
  }
}

class DefaultHttpChannelPipelineFactory extends CPF {
  // Sharable handlers

  // Upstream
  private[this] val noPipelining         = new NoPipelining
  private[this] val baseUrlRemover       = new BaseUrlRemover
  private[this] val basicAuth            = new BasicAuth
  private[this] val publicFileServer     = new PublicFileServer
  private[this] val publicResourceServer = new PublicResourceServer
  private[this] val request2Env          = new Request2Env
  private[this] val uriParser            = new UriParser
  private[this] val bodyParser           = new BodyParser
  private[this] val methodOverrider      = new MethodOverrider
  private[this] val dispatcher           = new Dispatcher

  // Downstream
  private[this] val fixiOS6SafariPOST    = new FixiOS6SafariPOST
  private[this] val xSendFile            = new XSendFile
  private[this] val xSendResource        = new XSendResource
  private[this] val env2Response         = new Env2Response
  private[this] val responseCacher       = new ResponseCacher

  /**
   * You can override this method to customize the default pipeline.
   *
   * Upstream direction: first handler -> last handler
   * Downstream direction: last handler -> first handler
   */
  def getPipeline(): ChannelPipeline = {
    // This method is run for every request, thus should be fast

    val ret = Channels.pipeline()

    // Upstream
    ret.addLast("HttpRequestDecoder",   new HttpRequestDecoder)
    ret.addLast("HttpChunkAggregator",  new HttpChunkAggregator(Config.xitrum.request.maxSizeInMB * 1024 * 1024))
    ret.addLast("noPipelining",         noPipelining)
    ret.addLast("baseUrlRemover",       baseUrlRemover)  // HttpRequest is attached to the channel here
    if (Config.xitrum.basicAuth.isDefined)
    ret.addLast("basicAuth",            basicAuth)
    ret.addLast("publicFileServer",     publicFileServer)
    ret.addLast("publicResourceServer", publicResourceServer)
    ret.addLast("request2Env",          request2Env)
    ret.addLast("uriParser",            uriParser)
    ret.addLast("bodyParser",           bodyParser)
    ret.addLast("methodOverrider",      methodOverrider)
    ret.addLast("dispatcher",           dispatcher)
<commit_msg>Improve DefaultHttpChannelPipelineFactory so that default handlers can be easily picked up by apps that want to use custom pipeline<commit_after>package xitrum.handler

import org.jboss.netty.channel.{Channels, ChannelPipeline, ChannelPipelineFactory => CPF}
import org.jboss.netty.handler.codec.http.{HttpRequestDecoder, HttpChunkAggregator, HttpResponseEncoder}
import org.jboss.netty.handler.execution.{ExecutionHandler, OrderedMemoryAwareThreadPoolExecutor}
import org.jboss.netty.handler.stream.ChunkedWriteHandler

import xitrum.Config
import xitrum.handler.up._
import xitrum.handler.down._

object DefaultHttpChannelPipelineFactory {
  // Put sharable handlers here so that they can be easily picked up by apps
  // that want to use custom pipeline. Those app may only want a subset of
  // default handlers.

  // Upstream
  lazy val noPipelining         = new NoPipelining
  lazy val baseUrlRemover       = new BaseUrlRemover
  lazy val basicAuth            = new BasicAuth
  lazy val publicFileServer     = new PublicFileServer
  lazy val publicResourceServer = new PublicResourceServer
  lazy val request2Env          = new Request2Env
  lazy val uriParser            = new UriParser
  lazy val bodyParser           = new BodyParser
  lazy val methodOverrider      = new MethodOverrider
  lazy val dispatcher           = new Dispatcher

  // Downstream
  lazy val fixiOS6SafariPOST    = new FixiOS6SafariPOST
  lazy val xSendFile            = new XSendFile
  lazy val xSendResource        = new XSendResource
  lazy val env2Response         = new Env2Response
  lazy val responseCacher       = new ResponseCacher

  def removeUnusedHandlersForWebSocket(pipeline: ChannelPipeline) {
    // WebSocket handshaker in Netty dynamically changes the pipeline like this:
    // pipeline.remove(classOf[HttpChunkAggregator])
    // pipeline.replace(classOf[HttpRequestDecoder],  "wsdecoder", new WebSocket08FrameDecoder(true, this.allowExtensions))
    // pipeline.replace(classOf[HttpResponseEncoder], "wsencoder", new WebSocket08FrameEncoder(false))

    // Upstream
    pipeline.remove(classOf[NoPipelining])
    pipeline.remove(classOf[BaseUrlRemover])
    if (Config.xitrum.basicAuth.isDefined)
    pipeline.remove(classOf[BasicAuth])
    pipeline.remove(classOf[PublicFileServer])
    pipeline.remove(classOf[PublicResourceServer])
    pipeline.remove(classOf[Request2Env])
    pipeline.remove(classOf[UriParser])
    pipeline.remove(classOf[BodyParser])
    pipeline.remove(classOf[MethodOverrider])
    pipeline.remove(classOf[Dispatcher])

    // Downstream
    pipeline.remove(classOf[ChunkedWriteHandler])
    pipeline.remove(classOf[FixiOS6SafariPOST])
    pipeline.remove(classOf[XSendFile])
    pipeline.remove(classOf[XSendResource])
    pipeline.remove(classOf[Env2Response])
    pipeline.remove(classOf[ResponseCacher])
  }
}

class DefaultHttpChannelPipelineFactory extends CPF {
  import DefaultHttpChannelPipelineFactory._

  /**
   * You can override this method to customize the default pipeline.
   *
   * Upstream direction: first handler -> last handler
   * Downstream direction: last handler -> first handler
   */
  def getPipeline(): ChannelPipeline = {
    // This method is run for every request, thus should be fast

    val ret = Channels.pipeline()

    // Upstream
    ret.addLast("HttpRequestDecoder",   new HttpRequestDecoder)
    ret.addLast("HttpChunkAggregator",  new HttpChunkAggregator(Config.xitrum.request.maxSizeInMB * 1024 * 1024))
    ret.addLast("noPipelining",         noPipelining)
    ret.addLast("baseUrlRemover",       baseUrlRemover)  // HttpRequest is attached to the channel here
    if (Config.xitrum.basicAuth.isDefined)
    ret.addLast("basicAuth",            basicAuth)
    ret.addLast("publicFileServer",     publicFileServer)
    ret.addLast("publicResourceServer", publicResourceServer)
    ret.addLast("request2Env",          request2Env)
    ret.addLast("uriParser",            uriParser)
    ret.addLast("bodyParser",           bodyParser)
    ret.addLast("methodOverrider",      methodOverrider)
    ret.addLast("dispatcher",           dispatcher)

{'fraction_non_alphanumeric': 0.08305400372439478, 'fraction_numerical': 0.005710738671632526, 'mean_word_length': 3.9211973121563837, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5524826', 'n_tokens_mistral': 2306, 'n_tokens_neox': 2286, 'n_words': 502}