<commit_before>package org.orbeon.oxf.servlet

import org.orbeon.io.CharsetNames
import org.orbeon.oxf.util.NetUtils
import org.slf4j.{Logger, LoggerFactory}

import java.io.{ByteArrayInputStream, ByteArrayOutputStream, InputStream}
import javax.servlet.http.{HttpServletRequest, HttpServletRequestWrapper}
import javax.servlet.{Filter, FilterChain, FilterConfig, ReadListener, ServletInputStream, ServletRequest, ServletResponse}
import scala.jdk.CollectionConverters._

// To enable, add the following to your `web.xml`, before all the other filters.
//
// <filter>
//     <filter-name>orbeon-http-logging-filter</filter-name>
//     <filter-class>org.orbeon.oxf.servlet.HttpLoggingFilter</filter-class>
// </filter>
// <filter-mapping>
//     <filter-name>orbeon-http-logging-filter</filter-name>
//     <url-pattern>/xforms-server</url-pattern>
//     <dispatcher>REQUEST</dispatcher>
// </filter-mapping>

class HttpLoggingFilter extends Filter {

  val Logger: Logger = LoggerFactory.getLogger("org.orbeon.filter.http-logging")

  override def doFilter(
    servletRequest  : ServletRequest,
    servletResponse : ServletResponse,
    chain           : FilterChain
  ): Unit = {

    val httpRequest         = servletRequest.asInstanceOf[HttpServletRequest]
    val byteArray           = NetUtils.inputStreamToByteArray(httpRequest.getInputStream)
    val requestPath         = NetUtils.getRequestPathInfo(httpRequest)
    val requestBody         = new String(byteArray, CharsetNames.Utf8)
    val inputStream         = new ByteArrayInputStream(byteArray)
    val servletInputStream  = new HttpLoggingFilter.ByteArrayServletInputStream(inputStream)
    val wrappedRequest      = new HttpLoggingFilter.LoggerRequestWrapper(httpRequest, servletInputStream)

    // Log path, headers, and body
    Logger.info(s"request path `$requestPath`")
    httpRequest.getHeaderNames.asScala.foreach { headerName =>
      val headerValue = httpRequest.getHeader(headerName)
      Logger.info(s"request header `$headerName: $headerValue`")
    }
    Logger.info(s"request body `$requestBody`")

    chain.doFilter(wrappedRequest, servletResponse)
  }

  // We need to provide those implementation of `init` and `destroy` as long as we support Servlet 3.1 / Tomcat 8.5
  override def init(config: FilterConfig): Unit = {}
  override def destroy(): Unit = {}
}

private object HttpLoggingFilter {

  class LoggerRequestWrapper(httpServletRequest: HttpServletRequest, servletInputStream: ServletInputStream)
      extends HttpServletRequestWrapper(httpServletRequest) {
    override def getInputStream: ServletInputStream = servletInputStream
  }

  class ByteArrayServletInputStream(byteArrayInputStream: ByteArrayInputStream)
      extends ServletInputStream {
    override def isFinished: Boolean                               = byteArrayInputStream.available() == 0
    override def isReady: Boolean                                  = true
    override def setReadListener(readListener: ReadListener): Unit = {}
    override def read(): Int                                       = byteArrayInputStream.read()
  }
}<commit_msg>Minor: indent, imports<commit_after>package org.orbeon.oxf.servlet

import org.orbeon.io.CharsetNames
import org.orbeon.oxf.util.NetUtils
import org.slf4j.{Logger, LoggerFactory}

import java.io.ByteArrayInputStream
import javax.servlet.http.{HttpServletRequest, HttpServletRequestWrapper}
import javax.servlet._
import scala.jdk.CollectionConverters._


// To enable, add the following to your `web.xml`, before all the other filters.
//
// <filter>
//     <filter-name>orbeon-http-logging-filter</filter-name>
//     <filter-class>org.orbeon.oxf.servlet.HttpLoggingFilter</filter-class>
// </filter>
// <filter-mapping>
//     <filter-name>orbeon-http-logging-filter</filter-name>
//     <url-pattern>/xforms-server</url-pattern>
//     <dispatcher>REQUEST</dispatcher>
// </filter-mapping>

class HttpLoggingFilter extends Filter {

  val Logger: Logger = LoggerFactory.getLogger("org.orbeon.filter.http-logging")

  override def doFilter(
    servletRequest  : ServletRequest,
    servletResponse : ServletResponse,
    chain           : FilterChain
  ): Unit = {

    val httpRequest        = servletRequest.asInstanceOf[HttpServletRequest]
    val byteArray          = NetUtils.inputStreamToByteArray(httpRequest.getInputStream)
    val requestPath        = NetUtils.getRequestPathInfo(httpRequest)
    val requestBody        = new String(byteArray, CharsetNames.Utf8)
    val inputStream        = new ByteArrayInputStream(byteArray)
    val servletInputStream = new HttpLoggingFilter.ByteArrayServletInputStream(inputStream)
    val wrappedRequest     = new HttpLoggingFilter.LoggerRequestWrapper(httpRequest, servletInputStream)

    // Log path, headers, and body
    Logger.info(s"request path `$requestPath`")
    httpRequest.getHeaderNames.asScala.foreach { headerName =>
      val headerValue = httpRequest.getHeader(headerName)
      Logger.info(s"request header `$headerName: $headerValue`")
    }
    Logger.info(s"request body `$requestBody`")

    chain.doFilter(wrappedRequest, servletResponse)
  }

  // We need to provide those implementation of `init` and `destroy` as long as we support Servlet 3.1 / Tomcat 8.5
  override def init(config: FilterConfig): Unit = ()
  override def destroy(): Unit = ()
}

private object HttpLoggingFilter {

  class LoggerRequestWrapper(httpServletRequest: HttpServletRequest, servletInputStream: ServletInputStream)
      extends HttpServletRequestWrapper(httpServletRequest) {
    override def getInputStream: ServletInputStream = servletInputStream
  }

  class ByteArrayServletInputStream(byteArrayInputStream: ByteArrayInputStream)
      extends ServletInputStream {
    def isFinished: Boolean                               = byteArrayInputStream.available() == 0
    def isReady: Boolean                                  = true
    def setReadListener(readListener: ReadListener): Unit = ()
    def read(): Int                                       = byteArrayInputStream.read()
  }
}
{'fraction_non_alphanumeric': 0.09399736147757255, 'fraction_numerical': 0.002308707124010554, 'mean_word_length': 3.633307868601986, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '136720', 'n_tokens_mistral': 1667, 'n_tokens_neox': 1644, 'n_words': 358}