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

io.gatling.recorder.har.HarMapping.scala Maven / Gradle / Ivy

There is a newer version: 3.13.1
Show newest version
/**
 * Copyright 2011-2016 GatlingCorp (http://gatling.io)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gatling.recorder.har

import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat

import scala.util.Try

import io.gatling.commons.util.StringHelper.RichString
import io.gatling.recorder.har.Json.{ JsonToInt, JsonToString }

import org.asynchttpclient.util.Base64

private[har] object HarMapping {

  private val ProtectedValue = """"(.*)\"""".r
  private val Iso8601ZonedDateAndTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX")

  // HAR files are required to be saved in UTF-8 encoding, other encodings are forbidden
  val Charset = StandardCharsets.UTF_8

  def jsonToHttpArchive(json: Json): HttpArchive = HttpArchive(buildLog(json.log))

  private def parseMillisFromIso8601DateTime(time: String): Long = Iso8601ZonedDateAndTime.parse(time).getTime

  private def buildLog(log: Json): Log = {
    val entries = log.entries.iterator
      // Filter out all non-HTTP protocols (eg: ws://)
      .filter(_.request.url.toString.toLowerCase.startsWith("http"))
      // Filter out all HTTP request with status=0, http://www.w3.org/TR/XMLHttpRequest/#the-status-attribute
      .filter(_.response.status.toInt != 0)
      .map(buildEntry)
      .toVector

    Log(entries)
  }

  private def buildEntry(entry: Json): Entry = {
    val startTime = parseMillisFromIso8601DateTime(entry.startedDateTime)
    // what a thing of beauty!!!
    val time = Try(entry.time.toLong).getOrElse(entry.time.toDouble.toLong)
    Entry(
      startTime,
      startTime + time,
      buildRequest(entry.request), buildResponse(entry.response)
    )
  }

  private def buildRequest(request: Json): Request = {
    val postData = request.postData.toOption
    Request(request.method, request.url, request.headers.map(buildHeader), postData.map(buildPostData))
  }
  private def unprotected(string: String) = string match {
    case ProtectedValue(unprotected) => unprotected
    case _                           => string
  }

  private def buildResponse(response: Json) = {
    val mimeType = response.content.mimeType
    assert(mimeType.toOption.isDefined, s"Response content ${response.content} does not contains a mimeType")

    val rawText = response.content.text.toOption.map(_.toString.trim).filter(!_.isEmpty)
    val encoding = response.content.encoding.toOption.map(_.toString.trim)
    val text = rawText flatMap (_.trimToOption) map { trimmedText =>
      encoding match {
        case Some("base64") => new String(Base64.decode(trimmedText), HarMapping.Charset)
        case _              => trimmedText
      }
    }

    val content = Content(mimeType, text)
    Response(response.status, content)
  }

  private def buildHeader(header: Json) = Header(header.name, unprotected(header.value))

  private def buildPostData(postData: Json) = PostData(postData.mimeType, postData.text, postData.params.map(buildPostParam))

  private def buildPostParam(postParam: Json) = PostParam(postParam.name, unprotected(postParam.value))
}

/*
 * HAR mapping is incomplete, as we deserialize only what is strictly necessary for building a simulation
 */
private[har] case class HttpArchive(log: Log)

private[har] case class Log(exchanges: Seq[Entry])

private[har] case class Entry(sendTime: Long, arrivalTime: Long, request: Request, response: Response)

private[har] case class Request(method: String, url: String, headers: Seq[Header], postData: Option[PostData])

private[har] case class Response(status: Int, content: Content)

private[har] case class Content(mimeType: String, text: Option[String]) {
  def textAsBytes = text.map(_.getBytes(HarMapping.Charset))
}

private[har] case class Header(name: String, value: String)

private[har] case class PostData(mimeType: String, text: String, params: Seq[PostParam]) {
  def textAsBytes = text.trimToOption.map(_.getBytes(HarMapping.Charset))
}

private[har] case class PostParam(name: String, value: String)




© 2015 - 2025 Weber Informatics LLC | Privacy Policy