Permalink
Browse files

[split] Changes for finagle-http.Message and util.NetUtil

* finagle-http/Message.scala:
    mediaType=: no longer sets charset to utf-8
    add Message.charset, Message.charset=
* finagle-http/MediaType.scala:
    add OctetStream

* util-core/NetUtil.scala:
   Add inetAddressToInt, isInetAddressInBlock, isInetAddressInBlocks
  • Loading branch information...
1 parent 7ec22c0 commit 2d37e7b25b06c6281c76381cec644acd89e1956f @dhelder dhelder committed Feb 8, 2012
View
@@ -1,3 +1,9 @@
+1.11.0 ?
+
+ * http: Message.mediaType=: no longer sets charset to utf-8
+ * http: Message.setContentType: defaults charset to utf-8
+ * http: added Message.charset, Message.charset=
+
1.10.0 2012/01/24
* http: Message.withX methods now have parameterized return types
@@ -2,11 +2,12 @@ package com.twitter.finagle.http
object MediaType {
// Common media types
- val Atom = "application/atom+xml"
- val Html = "text/html"
- val Javascript = "application/javascript"
- val Json = "application/json"
- val Rss = "application/rss+xml"
- val WwwForm = "application/x-www-form-urlencoded"
- val Xml = "application/xml"
-}
+ val Atom = "application/atom+xml"
+ val Html = "text/html"
+ val Javascript = "application/javascript"
+ val Json = "application/json"
+ val OctetStream = "application/octet-stream"
+ val Rss = "application/rss+xml"
+ val WwwForm = "application/x-www-form-urlencoded"
+ val Xml = "application/xml"
+}
@@ -5,6 +5,7 @@ import java.io.{InputStream, InputStreamReader, OutputStream, OutputStreamWriter
import java.util.{Iterator => JIterator}
import java.nio.charset.Charset
import java.util.{Date, TimeZone}
+import org.apache.commons.lang.StringUtils
import org.apache.commons.lang.time.FastDateFormat
import org.jboss.netty.buffer._
import org.jboss.netty.handler.codec.http.{Cookie, HttpMessage, HttpHeaders, HttpMethod,
@@ -91,6 +92,56 @@ abstract class Message extends HttpMessage {
cacheControl = "max-age=" + maxAge.inSeconds.toString + ", must-revalidate"
}
+ /** Get charset from Content-Type header */
+ def charset: Option[String] = {
+ contentType.foreach { contentType =>
+ val parts = StringUtils.split(contentType, ';')
+ 1.to(parts.length - 1) foreach { i =>
+ val part = parts(i).trim
+ if (part.startsWith("charset=")) {
+ val equalsIndex = part.indexOf('=')
+ val charset = part.substring(equalsIndex + 1)
+ return Some(charset)
+ }
+ }
+ }
+ None
+ }
+ /** Set charset in Content-Type header. This does not change the content. */
+ def charset_=(value: String) {
+ val contentType = this.contentType.getOrElse("")
+ val parts = StringUtils.split(contentType, ';')
+ if (parts.isEmpty) {
+ this.contentType = ";charset=" + value // malformed
+ return
+ }
+
+ val builder = new StringBuilder(parts(0))
+ if (!(parts.exists { _.trim.startsWith("charset=") })) {
+ // No charset parameter exist, add charset after media type
+ builder.append(";charset=")
+ builder.append(value)
+ // Copy other parameters
+ 1.to(parts.length - 1) foreach { i =>
+ builder.append(";")
+ builder.append(parts(i))
+ }
+ } else {
+ // Replace charset= parameter(s)
+ 1.to(parts.length - 1) foreach { i =>
+ val part = parts(i)
+ if (part.trim.startsWith("charset=")) {
+ builder.append(";charset=")
+ builder.append(value)
+ } else {
+ builder.append(";")
+ builder.append(part)
+ }
+ }
+ }
+ this.contentType = builder.toString
+ }
+
/** Get Content-Length header. Use length to get the length of actual content. */
def contentLength: Option[Long] =
Option(getHeader(HttpHeaders.Names.CONTENT_LENGTH)).map { _.toLong }
@@ -105,7 +156,7 @@ abstract class Message extends HttpMessage {
/** Set Content-Type header */
def contentType_=(value: String) { setHeader(HttpHeaders.Names.CONTENT_TYPE, value) }
/** Set Content-Type header by media-type and charset */
- def setContentType(mediaType: String, charset: String) {
+ def setContentType(mediaType: String, charset: String = "utf-8") {
setHeader(HttpHeaders.Names.CONTENT_TYPE, mediaType + ";charset=" + charset)
}
/** Set Content-Type header to application/json;charset=utf-8 */
@@ -156,8 +207,23 @@ abstract class Message extends HttpMessage {
else
None
}
- /** Set media-type in Content-Type heaer */
- def mediaType_=(value: String) { setContentType(value, "utf-8") }
+ /**
+ * Set media-type in Content-Type header. Charset and parameter values are
+ * preserved, though may not be appropriate for the new media type.
+ */
+ def mediaType_=(value: String) {
+ contentType match {
+ case Some(contentType) =>
+ val parts = StringUtils.split(contentType, ";", 2)
+ if (parts.length == 2) {
+ this.contentType = value + ";" + parts(1)
+ } else {
+ this.contentType = value
+ }
+ case None =>
+ this.contentType = value
+ }
+ }
/** Get Referer [sic] header */
def referer: Option[String] = Option(getHeader(HttpHeaders.Names.REFERER))
@@ -29,6 +29,42 @@ object MessageSpec extends Specification with DataTables {
response.acceptMediaTypes.toList must_== "a" :: "c" :: Nil
}
+ "charset" in {
+ "header" | "charset" |>
+ "x; charset=a" ! "a" |
+ "x;charset=a" ! "a" |
+ "x; charset=a " ! "a" |
+ "x;y;charset=a" ! "a" |
+ "x; charset=" ! "" |
+ "x; charset==" ! "=" |
+ "x; charset" ! null |
+ "x" ! null |
+ ";;;;;;" ! null |
+ { (header: String, expected: String) =>
+ val request = Request()
+ request.headers("Content-Type") = header
+ request.charset must_== Option(expected)
+ }
+ }
+
+ "charset=" in {
+ "header" | "charset" | "expected" |>
+ "x; charset=a" ! "b" ! "x;charset=b" |
+ "x" ! "b" ! "x;charset=b" |
+ "x;p1" ! "b" ! "x;charset=b;p1" |
+ "x;p1; p2 ;p3" ! "b" ! "x;charset=b;p1; p2 ;p3" |
+ "x;p1;charset=a;p3" ! "b" ! "x;p1;charset=b;p3" |
+ "x;" ! "b" ! "x;charset=b" |
+ ";" ! "b" ! ";charset=b" |
+ "" ! "b" ! ";charset=b" |
+ { (header: String, charset: String, expected: String) =>
+ val request = Request()
+ request.headers("Content-Type") = header
+ request.charset = charset
+ request.headers.get("Content-Type") must_== Option(expected)
+ }
+ }
+
"mediaType" in {
"header" | "type" |>
"application/json" ! "application/json" |
@@ -52,6 +88,22 @@ object MessageSpec extends Specification with DataTables {
request.mediaType must_== None
}
+ "mediaType=" in {
+ "header" | "mediaType" | "expected" |>
+ "x" ! "y" ! "y" |
+ "x; charset=a" ! "y" ! "y; charset=a" |
+ "x;p1; p2 ;p3" ! "y" ! "y;p1; p2 ;p3" |
+ "x;" ! "y" ! "y" |
+ ";" ! "y" ! "y" |
+ "" ! "y" ! "y" |
+ { (header: String, mediaType: String, expected: String) =>
+ val request = Request()
+ request.headers("Content-Type") = header
+ request.mediaType = mediaType
+ request.headers.get("Content-Type") must_== Option(expected)
+ }
+ }
+
"clearContent" in {
val response = Response()
@@ -3,6 +3,6 @@
project.organization=com.twitter
project.name=finagle
sbt.version=0.7.5
-project.version=1.10.1-SNAPSHOT
+project.version=1.11.0-SNAPSHOT
build.scala.versions=2.8.1
project.initialize=false

0 comments on commit 2d37e7b

Please sign in to comment.