har.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. 'use strict'
  2. var fs = require('fs')
  3. var qs = require('querystring')
  4. var validate = require('har-validator')
  5. var util = require('util')
  6. function Har (request) {
  7. this.request = request
  8. }
  9. Har.prototype.reducer = function (obj, pair) {
  10. // new property ?
  11. if (obj[pair.name] === undefined) {
  12. obj[pair.name] = pair.value
  13. return obj
  14. }
  15. // existing? convert to array
  16. var arr = [
  17. obj[pair.name],
  18. pair.value
  19. ]
  20. obj[pair.name] = arr
  21. return obj
  22. }
  23. Har.prototype.prep = function (data) {
  24. // construct utility properties
  25. data.queryObj = {}
  26. data.headersObj = {}
  27. data.postData.jsonObj = false
  28. data.postData.paramsObj = false
  29. // construct query objects
  30. if (data.queryString && data.queryString.length) {
  31. data.queryObj = data.queryString.reduce(this.reducer, {})
  32. }
  33. // construct headers objects
  34. if (data.headers && data.headers.length) {
  35. // loweCase header keys
  36. data.headersObj = data.headers.reduceRight(function (headers, header) {
  37. headers[header.name] = header.value
  38. return headers
  39. }, {})
  40. }
  41. // construct Cookie header
  42. if (data.cookies && data.cookies.length) {
  43. var cookies = data.cookies.map(function (cookie) {
  44. return cookie.name + '=' + cookie.value
  45. })
  46. if (cookies.length) {
  47. data.headersObj.cookie = cookies.join('; ')
  48. }
  49. }
  50. // prep body
  51. switch (data.postData.mimeType) {
  52. case 'multipart/mixed':
  53. case 'multipart/related':
  54. case 'multipart/form-data':
  55. case 'multipart/alternative':
  56. // reset values
  57. data.postData.mimeType = 'multipart/form-data'
  58. break
  59. case 'application/x-www-form-urlencoded':
  60. if (!data.postData.params) {
  61. data.postData.text = ''
  62. } else {
  63. data.postData.paramsObj = data.postData.params.reduce(this.reducer, {})
  64. // always overwrite
  65. data.postData.text = qs.stringify(data.postData.paramsObj)
  66. }
  67. break
  68. case 'text/json':
  69. case 'text/x-json':
  70. case 'application/json':
  71. case 'application/x-json':
  72. data.postData.mimeType = 'application/json'
  73. if (data.postData.text) {
  74. try {
  75. data.postData.jsonObj = JSON.parse(data.postData.text)
  76. } catch (e) {
  77. this.request.debug(e)
  78. // force back to text/plain
  79. data.postData.mimeType = 'text/plain'
  80. }
  81. }
  82. break
  83. }
  84. return data
  85. }
  86. Har.prototype.options = function (options) {
  87. // skip if no har property defined
  88. if (!options.har) {
  89. return options
  90. }
  91. var har = util._extend({}, options.har)
  92. // only process the first entry
  93. if (har.log && har.log.entries) {
  94. har = har.log.entries[0]
  95. }
  96. // add optional properties to make validation successful
  97. har.url = har.url || options.url || options.uri || options.baseUrl || '/'
  98. har.httpVersion = har.httpVersion || 'HTTP/1.1'
  99. har.queryString = har.queryString || []
  100. har.headers = har.headers || []
  101. har.cookies = har.cookies || []
  102. har.postData = har.postData || {}
  103. har.postData.mimeType = har.postData.mimeType || 'application/octet-stream'
  104. har.bodySize = 0
  105. har.headersSize = 0
  106. har.postData.size = 0
  107. if (!validate.request(har)) {
  108. return options
  109. }
  110. // clean up and get some utility properties
  111. var req = this.prep(har)
  112. // construct new options
  113. if (req.url) {
  114. options.url = req.url
  115. }
  116. if (req.method) {
  117. options.method = req.method
  118. }
  119. if (Object.keys(req.queryObj).length) {
  120. options.qs = req.queryObj
  121. }
  122. if (Object.keys(req.headersObj).length) {
  123. options.headers = req.headersObj
  124. }
  125. switch (req.postData.mimeType) {
  126. case 'application/x-www-form-urlencoded':
  127. options.form = req.postData.paramsObj
  128. break
  129. case 'application/json':
  130. if (req.postData.jsonObj) {
  131. options.body = req.postData.jsonObj
  132. options.json = true
  133. }
  134. break
  135. case 'multipart/form-data':
  136. options.formData = {}
  137. req.postData.params.forEach(function (param) {
  138. var attachment = {}
  139. if (!param.fileName && !param.fileName && !param.contentType) {
  140. options.formData[param.name] = param.value
  141. return
  142. }
  143. // attempt to read from disk!
  144. if (param.fileName && !param.value) {
  145. attachment.value = fs.createReadStream(param.fileName)
  146. } else if (param.value) {
  147. attachment.value = param.value
  148. }
  149. if (param.fileName) {
  150. attachment.options = {
  151. filename: param.fileName,
  152. contentType: param.contentType ? param.contentType : null
  153. }
  154. }
  155. options.formData[param.name] = attachment
  156. })
  157. break
  158. default:
  159. if (req.postData.text) {
  160. options.body = req.postData.text
  161. }
  162. }
  163. return options
  164. }
  165. exports.Har = Har