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

com.twitter.finagle.http.QueryParamEncoder.scala Maven / Gradle / Ivy

package com.twitter.finagle.http

import java.io.UnsupportedEncodingException
import java.net.URLEncoder
import java.nio.charset.{StandardCharsets, UnsupportedCharsetException}
import java.util.regex.Pattern
import scala.annotation.tailrec

private object QueryParamEncoder {

  private[this] val regex = Pattern.compile("""\+""")
  private[this] val charsetName: String = StandardCharsets.UTF_8.name

  def encode(values: Iterable[(String, String)]): String = {
    try {
      if (values.isEmpty) ""
      else {
        val it = values.iterator
        // The default value of 16 bytes is unlikely to be enough
        // so we go with an unscientific guess of 128 to start
        val sb = new StringBuilder(128, "?")

        // Assumes that it has a `.next` value
        @tailrec
        def loop(): Unit = {
          val (k, v) = it.next()
          appendComponent(sb, k)
          sb.append('=')
          appendComponent(sb, v)

          if (it.hasNext) {
            sb.append('&')
            loop()
          }
        }

        loop() // We know that there is at least one element in the iterator
        replacePluses(sb.mkString)
      }
    } catch {
      case _: UnsupportedEncodingException =>
        throw new UnsupportedCharsetException(charsetName)
    }
  }

  private[this] def appendComponent(sb: StringBuilder, s: String): Unit = {
    // TODO: URLEncoder is known to be slow, explore more efficient options.
    val encoded = URLEncoder.encode(s, charsetName)
    sb.append(encoded)
  }

  private[this] def replacePluses(s: String): String = {
    // TODO: we can do better than a regex
    s.indexOf('+') match {
      case -1 => s // fast path
      case _ => regex.matcher(s).replaceAll("%20")
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy