Skip to content

Commit

Permalink
Redesign Body and BodyParts APIs, close #1238
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephane Landelle committed Jun 24, 2013
1 parent 686e2bb commit 441883e
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 62 deletions.
Expand Up @@ -165,7 +165,7 @@ and (select count(*) from usr_account where usr_id=id) >=2""")
.exec(http("Url from session").get("/aaaa"))
.pause(1000 milliseconds, 3000 milliseconds)
// Second request to be repeated
.exec(http("Create Thing blabla").post("/things").queryParam("login", "${login}").queryParam("password", "${password}").elFileBody("create_thing.txt").asJSON)
.exec(http("Create Thing blabla").post("/things").queryParam("login", "${login}").queryParam("password", "${password}").body(ELFileBody("create_thing.txt")).asJSON)
.pause(pause1)
// Third request to be repeated
.exec(http("Liste Articles").get("/things").queryParam("firstname", "${firstname}").queryParam("lastname", "${lastname}"))
Expand All @@ -179,7 +179,7 @@ and (select count(*) from usr_account where usr_id=id) >=2""")
55 -> exec(http("Possibility 2").get("/p2")) // last 5% bypass
)
.exec(http("Create Thing omgomg")
.post("/things").queryParam("postTest", "${sessionParam}").elFileBody("create_thing.txt").asJSON
.post("/things").queryParam("postTest", "${sessionParam}").body(RawFileBody("create_thing.txt")).asJSON
.check(status.is(201).saveAs("status")))
}
// Head request
Expand Down
14 changes: 13 additions & 1 deletion gatling-http/src/main/scala/io/gatling/http/Predef.scala
Expand Up @@ -25,7 +25,7 @@ import io.gatling.http.check.status.HttpStatusCheckBuilder
import io.gatling.http.check.time.HttpResponseTimeCheckBuilder
import io.gatling.http.check.url.CurrentLocationCheckBuilder
import io.gatling.http.config.{ HttpProtocol, HttpProtocolBuilder, HttpProxyBuilder }
import io.gatling.http.request.BodyProcessors
import io.gatling.http.request.{ BodyProcessors, ELFileBodies, FileBodyPart, RawFileBodies, StringBodyPart }
import io.gatling.http.request.builder.{ AbstractHttpRequestBuilder, HttpRequestBaseBuilder }

object Predef {
Expand Down Expand Up @@ -71,4 +71,16 @@ object Predef {
case KO => List(session.toString)
case _ => Nil
}

def ELFileBody = io.gatling.http.request.ELFileBody
def StringBody = io.gatling.http.request.StringBody
def RawFileBody = io.gatling.http.request.RawFileBody
def ByteArrayBody = io.gatling.http.request.ByteArrayBody
def InputStreamBody = io.gatling.http.request.InputStreamBody

def StringBodyPart = io.gatling.http.request.StringBodyPart
def ByteArrayBodyPart = io.gatling.http.request.ByteArrayBodyPart
def FileBodyPart = io.gatling.http.request.FileBodyPart
def RawFileBodyPart = io.gatling.http.request.RawFileBodyPart
def ELFileBodyPart = io.gatling.http.request.ELFileBodyPart
}
37 changes: 34 additions & 3 deletions gatling-http/src/main/scala/io/gatling/http/request/Body.scala
Expand Up @@ -17,26 +17,57 @@ package io.gatling.http.request

import java.io.{ File => JFile, InputStream }

import org.apache.commons.io.FileUtils

import com.ning.http.client.RequestBuilder
import com.ning.http.client.generators.InputStreamBodyGenerator

import io.gatling.core.config.GatlingConfiguration.configuration
import io.gatling.core.session.{ Expression, Session }
import io.gatling.core.validation.Validation

object ELFileBody {
def apply(filePath: Expression[String]) = StringBody(ELFileBodies.asString(filePath))
}

trait Body {
def setBody(requestBuilder: RequestBuilder, session: Session): Validation[RequestBuilder]
}

case class StringBody(string: Expression[String]) extends Body {

def asBytes: ByteArrayBody = {
val bytes = (session: Session) => string(session).map(_.getBytes(configuration.core.encoding))
ByteArrayBody(bytes)
}

def setBody(requestBuilder: RequestBuilder, session: Session): Validation[RequestBuilder] = string(session).map(requestBuilder.setBody)
}

case class RawFileBody(file: Expression[JFile]) extends Body {
object RawFileBody {

def apply(filePath: Expression[String]) = new RawFileBody(RawFileBodies.asFile(filePath))

def unapply(b: RawFileBody) = Some(b.file)
}

class RawFileBody(val file: Expression[JFile]) extends Body {

def asString: StringBody = {
val string = (session: Session) => file(session).map(FileUtils.readFileToString(_, configuration.core.encoding))
StringBody(string)
}

def asBytes: ByteArrayBody = {
val bytes = (session: Session) => file(session).map(FileUtils.readFileToByteArray)
ByteArrayBody(bytes)
}

def setBody(requestBuilder: RequestBuilder, session: Session): Validation[RequestBuilder] = file(session).map(requestBuilder.setBody)
}

case class ByteArrayBody(byteArray: Expression[Array[Byte]]) extends Body {
def setBody(requestBuilder: RequestBuilder, session: Session): Validation[RequestBuilder] = byteArray(session).map(requestBuilder.setBody)
case class ByteArrayBody(bytes: Expression[Array[Byte]]) extends Body {
def setBody(requestBuilder: RequestBuilder, session: Session): Validation[RequestBuilder] = bytes(session).map(requestBuilder.setBody)
}

case class InputStreamBody(is: Expression[InputStream]) extends Body {
Expand Down
68 changes: 60 additions & 8 deletions gatling-http/src/main/scala/io/gatling/http/request/BodyPart.scala
Expand Up @@ -23,40 +23,92 @@ import io.gatling.core.config.GatlingConfiguration.configuration
import io.gatling.core.session.{ Expression, Session }
import io.gatling.core.validation.Validation

object RawFileBodyPart {

def apply(name: Expression[String], filePath: Expression[String], contentType: String) = FileBodyPart(name, RawFileBodies.asFile(filePath), contentType)
}

object ELFileBodyPart {

def apply(name: Expression[String], filePath: Expression[String]) = StringBodyPart(name, ELFileBodies.asString(filePath))
}

sealed trait BodyPart {

def toMultiPart(session: Session): Validation[Part]
}

case class StringBodyPart(name: Expression[String], value: Expression[String], contentId: Option[String] = None) extends BodyPart {
case class StringBodyPart(
name: Expression[String],
value: Expression[String],
charset: String = configuration.core.encoding,
contentType: Option[String] = None,
transferEncoding: Option[String] = None,
contentId: Option[String] = None) extends BodyPart {

def withCharset(charset: String) = copy(charset = charset)
def withContentId(contentId: String) = copy(contentId = Some(contentId))
def withContentType(contentType: String) = copy(contentType = Some(contentType))
def withTransferEncoding(transferEncoding: String) = copy(transferEncoding = Some(transferEncoding))

def toMultiPart(session: Session): Validation[Part] =
for {
name <- name(session)
value <- value(session)
} yield new StringPart(name, value, configuration.core.encoding, contentId.getOrElse(null))
} yield {
val part = new StringPart(name, value, charset, contentId.getOrElse(null))
contentType.map(part.setContentType)
transferEncoding.map(part.setTransferEncoding)
part
}
}

case class ByteArrayBodyPart(name: Expression[String], data: Expression[Array[Byte]], mimeType: String, contentId: Option[String] = None) extends BodyPart {
case class ByteArrayBodyPart(
name: Expression[String],
data: Expression[Array[Byte]],
contentType: String,
charset: String = configuration.core.encoding,
fileName: Option[String] = None,
transferEncoding: Option[String] = None,
contentId: Option[String] = None) extends BodyPart {

def withCharset(charset: String) = copy(charset = charset)
def withFileName(fileName: String) = copy(fileName = Some(fileName))
def withContentId(contentId: String) = copy(contentId = Some(contentId))

def toMultiPart(session: Session): Validation[Part] =
for {
name <- name(session)
data <- data(session)
} yield {
val source = new ByteArrayPartSource(null, data)
new FilePart(name, source, mimeType, configuration.core.encoding, contentId.getOrElse(null))
val source = new ByteArrayPartSource(fileName.getOrElse(null), data)
val part = new FilePart(name, source, contentType, charset, contentId.getOrElse(null))
transferEncoding.map(part.setTransferEncoding)
part
}
}

case class FileBodyPart(name: Expression[String], file: Expression[File], mimeType: String, contentId: Option[String] = None) extends BodyPart {
case class FileBodyPart(
name: Expression[String],
file: Expression[File],
contentType: String,
charset: String = configuration.core.encoding,
fileName: Option[String] = None,
transferEncoding: Option[String] = None,
contentId: Option[String] = None) extends BodyPart {

def withCharset(charset: String) = copy(charset = charset)
def withFileName(fileName: String) = copy(fileName = Some(fileName))
def withContentId(contentId: String) = copy(contentId = Some(contentId))

def toMultiPart(session: Session): Validation[Part] =
for {
name <- name(session)
file <- file(session)
} yield {
val source = new FilePartSource(null, file)
new FilePart(name, source, mimeType, configuration.core.encoding, contentId.getOrElse(null))
val source = new FilePartSource(fileName.getOrElse(null), file)
val part = new FilePart(name, source, contentType, charset, contentId.getOrElse(null))
transferEncoding.map(part.setTransferEncoding)
part
}
}
Expand Up @@ -22,8 +22,8 @@ import org.apache.commons.io.IOUtils
import io.gatling.core.config.GatlingConfiguration.configuration
import io.gatling.core.config.GatlingFiles
import io.gatling.core.session.{ EL, Expression, Session }
import io.gatling.core.validation.Validation
import io.gatling.core.util.IOHelper.withCloseable
import io.gatling.core.validation.Validation

object ELFileBodies {

Expand All @@ -35,14 +35,10 @@ object ELFileBodies {
.map(resource => withCloseable(resource.inputStream)(IOUtils.toString(_, configuration.core.encoding)))
.map(EL.compile[String])

def buildExpression[T](filePath: Expression[String], f: String => T): Expression[T] = (session: Session) =>
def asString(filePath: Expression[String]): Expression[String] = (session: Session) =>
for {
path <- filePath(session)
expression <- cached(path)
body <- expression(session)
} yield f(body)

def asString(filePath: Expression[String]) = buildExpression(filePath, identity)

def asBytes(filePath: Expression[String]) = buildExpression(filePath, _.getBytes(configuration.core.encoding))
} yield body
}
Expand Up @@ -19,8 +19,6 @@ import java.io.File

import scala.collection.mutable

import org.apache.commons.io.FileUtils

import io.gatling.core.config.GatlingConfiguration.configuration
import io.gatling.core.config.GatlingFiles
import io.gatling.core.session.{ Expression, Session }
Expand All @@ -31,15 +29,9 @@ object RawFileBodies {
private val cache = mutable.Map.empty[String, Validation[File]]
def cached(path: String) = if (configuration.http.cacheRawFileBodies) cache.getOrElseUpdate(path, GatlingFiles.requestBodyResource(path).map(_.jfile)) else GatlingFiles.requestBodyResource(path).map(_.jfile)

def buildExpression[T](filePath: Expression[String], f: File => T): Expression[T] = (session: Session) =>
def asFile(filePath: Expression[String]): Expression[File] = (session: Session) =>
for {
path <- filePath(session)
file <- cached(path)
} yield f(file)

def asFile(filePath: Expression[String]): Expression[File] = buildExpression(filePath, identity)

def asString(filePath: Expression[String]): Expression[String] = buildExpression(filePath, FileUtils.readFileToString(_, configuration.core.encoding))

def asBytes(filePath: Expression[String]): Expression[Array[Byte]] = buildExpression(filePath, FileUtils.readFileToByteArray)
} yield file
}
Expand Up @@ -43,42 +43,12 @@ abstract class AbstractHttpRequestWithBodyBuilder[B <: AbstractHttpRequestWithBo

private[http] def newInstance(httpAttributes: HttpAttributes): B = newInstance(httpAttributes, bodyAttributes)

def requestBody(bd: Body): B = newInstance(httpAttributes, bodyAttributes.copy(body = Some(bd)))

// String
def body(bd: Expression[String]): B = requestBody(StringBody(bd))
def rawFileBodyAsString(filePath: Expression[String]): B = body(RawFileBodies.asString(filePath))
def elFileBody(filePath: Expression[String]): B = body(ELFileBodies.asString(filePath))

// Bytes
def byteArrayBody(byteArray: Expression[Array[Byte]]): B = requestBody(new ByteArrayBody(byteArray))
def rawFileBodyAsBytes(filePath: Expression[String]): B = byteArrayBody(RawFileBodies.asBytes(filePath))
def elFileBodyAsBytes(filePath: Expression[String]): B = byteArrayBody(ELFileBodies.asBytes(filePath))
def bodyAsBytes(bd: Expression[String]): B = byteArrayBody(StringBodies.asBytes(bd))

// File
def rawFileBody(filePath: Expression[String]): B = requestBody(RawFileBody(RawFileBodies.asFile(filePath)))

// InputStream
def inputStreamBody(is: Expression[InputStream]): B = requestBody(new InputStreamBody(is))
def body(bd: Body): B = newInstance(httpAttributes, bodyAttributes.copy(body = Some(bd)))

def processRequestBody(processor: Body => Body): B = newInstance(httpAttributes, bodyAttributes.copy(body = bodyAttributes.body.map(processor)))

def bodyPart(bodyPart: BodyPart): B = newInstance(httpAttributes, bodyAttributes.copy(bodyParts = bodyPart :: bodyAttributes.bodyParts))

def bodyPart(name: Expression[String], value: Expression[String]): B = bodyPart(StringBodyPart(name, value))
def bodyPart(name: Expression[String], value: Expression[String], contentId: String): B = bodyPart(StringBodyPart(name, value, Some(contentId)))

def rawFileBodyPart(name: Expression[String], filePath: Expression[String], mimeType: String) = bodyPart(FileBodyPart(name, RawFileBodies.asFile(filePath), mimeType))
def rawFileBodyPart(name: Expression[String], filePath: Expression[String], mimeType: String, contentId: String) = bodyPart(FileBodyPart(name, RawFileBodies.asFile(filePath), mimeType, Some(contentId)))

def byteArrayBodyPart(name: Expression[String], data: Expression[Array[Byte]], mimeType: String) = bodyPart(ByteArrayBodyPart(name, data, mimeType))
def byteArrayBodyPart(name: Expression[String], data: Expression[Array[Byte]], mimeType: String, contentId: String) = bodyPart(ByteArrayBodyPart(name, data, mimeType, Some(contentId)))

def elFileBodyPart(name: Expression[String], filePath: Expression[String]): B = bodyPart(name, ELFileBodies.asString(filePath))
def elFileBodyPart(name: Expression[String], filePath: Expression[String], mimeType: String): B = byteArrayBodyPart(name, ELFileBodies.asBytes(filePath), mimeType)
def elFileBodyPart(name: Expression[String], filePath: Expression[String], mimeType: String, contentId: String): B = byteArrayBodyPart(name, ELFileBodies.asBytes(filePath), mimeType, contentId)

protected def configureParts(session: Session, requestBuilder: RequestBuilder): Validation[RequestBuilder] = {
require(!bodyAttributes.body.isDefined || bodyAttributes.bodyParts.isEmpty, "Can't have both a body and body parts!")

Expand Down

0 comments on commit 441883e

Please sign in to comment.