=htp Apply remaining review feedback

This commit is contained in:
Mathias 2014-08-06 00:20:12 +02:00
parent 3747196356
commit e7ef23f6f8
6 changed files with 89 additions and 40 deletions

View file

@ -5,11 +5,15 @@
package akka.http.model
import java.io.File
import akka.stream.FlowMaterializer
import akka.stream.scaladsl.Flow
import org.reactivestreams.Publisher
import akka.stream.impl.SynchronousPublisherFromIterable
import scala.collection.immutable
import headers._
import scala.concurrent.{ ExecutionContext, Future }
trait MultipartParts {
def parts: Publisher[BodyPart]
}
@ -47,8 +51,26 @@ object MultipartByteRanges {
* All parts must contain a Content-Disposition header with a type form-data
* and a name parameter that is unique.
*/
final case class MultipartFormData(parts: Publisher[BodyPart]) extends MultipartParts {
// def get(partName: String): Option[BodyPart] = fields.find(_.name.exists(_ == partName))
case class MultipartFormData(parts: Publisher[BodyPart]) extends MultipartParts {
/**
* Turns this instance into its strict specialization using the given `maxFieldCount` as the field number cut-off
* hint.
*/
def toStrict(materializer: FlowMaterializer, maxFieldCount: Int = 1000)(implicit ec: ExecutionContext): Future[StrictMultipartFormData] =
Flow(parts).grouped(maxFieldCount).toFuture(materializer).map(new StrictMultipartFormData(_))
}
/**
* A specialized `MultipartFormData` that allows full random access to its parts.
*/
class StrictMultipartFormData(val fields: immutable.Seq[BodyPart]) extends MultipartFormData(SynchronousPublisherFromIterable(fields)) {
/**
* Returns the BodyPart with the given name, if found.
*/
def get(partName: String): Option[BodyPart] = fields.find(_.name.exists(_ == partName))
override def toStrict(materializer: FlowMaterializer, maxFieldCount: Int)(implicit ec: ExecutionContext): Future[StrictMultipartFormData] =
Future.successful(this)
}
object MultipartFormData {

View file

@ -37,12 +37,15 @@ private[http] final class BodyPartParser(defaultContentType: ContentType,
sealed trait StateResult // phantom type for ensuring soundness of our parsing method setup
private[this] val needle = new Array[Byte](boundary.length + 4)
needle(0) = '\r'.toByte
needle(1) = '\n'.toByte
needle(2) = '-'.toByte
needle(3) = '-'.toByte
boundary.getAsciiBytes(needle, 4)
private[this] val needle: Array[Byte] = {
val array = new Array[Byte](boundary.length + 4)
array(0) = '\r'.toByte
array(1) = '\n'.toByte
array(2) = '-'.toByte
array(3) = '-'.toByte
boundary.getAsciiBytes(array, 4)
array
}
// we use the Boyer-Moore string search algorithm for finding the boundaries in the multipart entity,
// TODO: evaluate whether an upgrade to the more efficient FJS is worth the implementation cost
@ -64,13 +67,14 @@ private[http] final class BodyPartParser(defaultContentType: ContentType,
try state(input)
catch {
case e: ParsingException fail(e.info)
case NotEnoughDataException throw new IllegalStateException // we are missing a try/catch{continue} wrapper somewhere
case NotEnoughDataException throw new IllegalStateException(NotEnoughDataException) // we are missing a try/catch{continue} wrapper somewhere
}
result.toList
}
def tryParseInitialBoundary(input: ByteString): StateResult = {
// we don't use boyerMoore here because we are looking for the boundary *without* a preceding CRLF
// we don't use boyerMoore here because we are testing for the boundary *without* a
// preceding CRLF and at a known location (the very beginning of the entity)
try {
@tailrec def rec(ix: Int): StateResult =
if (ix < needle.length) {