Skip to content

Instantly share code, notes, and snippets.

@lancegatlin
Created October 9, 2019 16:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lancegatlin/4af3e5b7d1472569a2509902e515beee to your computer and use it in GitHub Desktop.
Save lancegatlin/4af3e5b7d1472569a2509902e515beee to your computer and use it in GitHub Desktop.
HAR file
import java.time.Instant
import play.api.libs.json._
/*
* HTTP Archive Format 1.2
* note: HAR files can be exported/imported from the Chrome dev-tools network page
*
* Source: http://www.softwareishard.com/blog/har-12-spec
*/
/**
* @param url Absolute URL of the request (fragments are not included).
* @param headersSize Total number of bytes from the start of the HTTP request message until (and including) the double CRLF before the body. Set to -1 if the info is not available.
* @param bodySize Size of the request body (POST data payload) in bytes. Set to -1 if the info is not available.
*/
case class HarRequest(
url: String,
headersSize: Int = -1,
bodySize: Int = -1
/*
method [string] - Request method (GET, POST, ...).
httpVersion [string] - Request HTTP Version.
cookies [array] - List of cookie objects.
headers [array] - List of header objects.
queryString [array] - List of query parameter objects.
postData [object, optional] - Posted data info.
comment [string, optional] (new in 1.2) - A comment provided by the user or the application.
*/
)
object HarRequest {
implicit val formatHarRequest = Json.format[HarRequest]
}
/**
* @param size Length of the returned content in bytes. Should be equal to response.bodySize if there is no compression and bigger when the content has been compressed.
* @param mimeType MIME type of the response text (value of the Content-Type response header). The charset attribute of the MIME type is included (if available).
*/
case class HarResponseContent(
size: Long = -1,
mimeType: String = ""
/*
compression [number, optional] - Number of bytes saved. Leave out this field if the information is not available.
text [string, optional] - Response body sent from the server or loaded from the browser cache. This field is populated with textual content only. The text field is either HTTP decoded text or a encoded (e.g. "base64") representation of the response body. Leave out this field if the information is not available.
encoding [string, optional] (new in 1.2) - Encoding used for response text field e.g "base64". Leave out this field if the text field is HTTP decoded (decompressed & unchunked), than trans-coded from its original character set into UTF-8.
comment [string, optional] (new in 1.2) - A comment provided by the user or the application.
*/
)
object HarResponseContent {
implicit val formatHarResponseContent = Json.format[HarResponseContent]
}
/**
* @param status Response status.
* @param httpVersion Response HTTP Version.
* @param content Details about the response body.
* @param headersSize Total number of bytes from the start of the HTTP response message until (and including) the double CRLF before the body. Set to -1 if the info is not available.
* @param bodySize Size of the received response body in bytes. Set to zero in case of responses coming from the cache (304). Set to -1 if the info is not available.
*/
case class HarResponse(
status: Int = -1,
httpVersion: String = "",
content: HarResponseContent,
headersSize: Int = -1,
bodySize: Int = -1
/*
statusText [string] - Response status description.
cookies [array] - List of cookie objects.
headers [array] - List of header objects.
content [object] -
redirectURL [string] - Redirection target URL from the Location response header.
comment [string, optional] (new in 1.2) - A comment provided by the user or the application.
*/
)
object HarResponse {
implicit val formatHarResponse = Json.format[HarResponse]
}
/**
* @param blocked Time spent in a queue waiting for a network connection. Use -1 if the timing does not apply to the current request.
* @param dns DNS resolution time. The time required to resolve a host name. Use -1 if the timing does not apply to the current request.
* @param connect Time required to create TCP connection. Use -1 if the timing does not apply to the current request.
* @param send Time required to send HTTP request to the server.
* @param _wait Waiting for a response from the server.
* @param receive Time required to read entire response from the server (or cache).
* @param ssl Time required for SSL/TLS negotiation. If this field is defined then the time is also included in the connect field (to ensure backward compatibility with HAR 1.1). Use -1 if the timing does not apply to the current request.
*/
case class HarEntryTimings(
blocked: Int = -1,
dns: Int = -1,
connect: Int = -1,
send: Int = -1,
_wait: Int = -1, // unfortunate name, see https://stackoverflow.com/questions/46740550/cannot-define-a-property-called-wait-in-scala-case-class
receive: Int = -1,
ssl: Int = -1
/*
comment [string, optional] (new in 1.2) - A comment provided by the user or the application.
*/
)
object HarEntryTimings {
// doesn't work because of _wait
//implicit val formatHarEntryTimings = Json.format[HarEntryTimings]
import play.api.libs.functional.syntax._
implicit val locationReads: Reads[HarEntryTimings] = (
(JsPath \ "blocked").read[Int] and
(JsPath \ "dns").read[Int] and
(JsPath \ "connect").read[Int] and
(JsPath \ "send").read[Int] and
(JsPath \ "wait").read[Int] and
(JsPath \ "receive").read[Int] and
(JsPath \ "ssl").read[Int]
)(HarEntryTimings.apply _)
implicit val locationWrites: Writes[HarEntryTimings] = (
(JsPath \ "blocked").write[Int] and
(JsPath \ "dns").write[Int] and
(JsPath \ "connect").write[Int] and
(JsPath \ "send").write[Int] and
(JsPath \ "wait").write[Int] and
(JsPath \ "receive").write[Int] and
(JsPath \ "ssl").write[Int]
)(unlift(HarEntryTimings.unapply))
}
/**
* @param pageref Reference to the parent page. Leave out this field if the application does not support grouping by pages.
* @param startedDateTime Date and time stamp of the request start (ISO 8601 - YYYY-MM-DDThh:mm:ss.sTZD).
* @param time Total elapsed time of the request in milliseconds. This is the sum of all timings available in the timings object (i.e. not including -1 values) .
* @param request Detailed info about the request.
* @param response Detailed info about the response.
* @param timings Detailed timing info about request/response round trip.
*/
case class HarEntry(
pageref: Option[String] = None,
startedDateTime: Instant, // Date and time stamp of the request start (ISO 8601 - YYYY-MM-DDThh:mm:ss.sTZD)
time: Int, // time [number] - Total elapsed time of the request in milliseconds
request: HarRequest,
response: HarResponse,
timings: HarEntryTimings = HarEntryTimings()
/*
cache [object] - Info about cache usage.
serverIPAddress [string, optional] (new in 1.2) - IP address of the server that was connected (result of DNS resolution).
connection [string, optional] (new in 1.2) - Unique ID of the parent TCP/IP connection, can be the client or server port number. Note that a port number doesn't have to be unique identifier in cases where the port is shared for more connections. If the port isn't available for the application, any other unique connection ID can be used instead (e.g. connection index). Leave out this field if the application doesn't support this info.
comment [string, optional] (new in 1.2) - A comment provided by the user or the application.
*/
)
object HarEntry {
implicit val formatHarEntry = Json.format[HarEntry]
}
case class HarCreator(
name: String = "",
version: String = "",
comment: String = ""
)
object HarCreator {
implicit val formatHarCreator = Json.format[HarCreator]
}
/**
* @param creator Name and version info of the log creator application.
* @param entries List of all exported (tracked) requests.
*/
case class HarLog(
creator: HarCreator = HarCreator(),
entries: Seq[HarEntry]
// note: only implementing required fields
/*
version [string] - Version number of the format. If empty, string "1.1" is assumed by default.
browser [object, optional] - Name and version info of used browser.
pages [array, optional] - List of all exported (tracked) pages. Leave out this field if the application does not support grouping by pages.
comment [string, optional] (new in 1.2) - A comment provided by the user or the application.
*/
)
object HarLog {
implicit val formatHarLog = Json.format[HarLog]
}
case class HarFile(
log: HarLog
)
object HarFile {
implicit val formatHarFile = Json.format[HarFile]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment