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 extend = require('extend')
  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. function some (arr) {
  52. return arr.some(function (type) {
  53. return data.postData.mimeType.indexOf(type) === 0
  54. })
  55. }
  56. if (some([
  57. 'multipart/mixed',
  58. 'multipart/related',
  59. 'multipart/form-data',
  60. 'multipart/alternative'])) {
  61. // reset values
  62. data.postData.mimeType = 'multipart/form-data'
  63. } else if (some([
  64. 'application/x-www-form-urlencoded'])) {
  65. if (!data.postData.params) {
  66. data.postData.text = ''
  67. } else {
  68. data.postData.paramsObj = data.postData.params.reduce(this.reducer, {})
  69. // always overwrite
  70. data.postData.text = qs.stringify(data.postData.paramsObj)
  71. }
  72. } else if (some([
  73. 'text/json',
  74. 'text/x-json',
  75. 'application/json',
  76. 'application/x-json'])) {
  77. data.postData.mimeType = 'application/json'
  78. if (data.postData.text) {
  79. try {
  80. data.postData.jsonObj = JSON.parse(data.postData.text)
  81. } catch (e) {
  82. this.request.debug(e)
  83. // force back to text/plain
  84. data.postData.mimeType = 'text/plain'
  85. }
  86. }
  87. }
  88. return data
  89. }
  90. Har.prototype.options = function (options) {
  91. // skip if no har property defined
  92. if (!options.har) {
  93. return options
  94. }
  95. var har = {}
  96. extend(har, options.har)
  97. // only process the first entry
  98. if (har.log && har.log.entries) {
  99. har = har.log.entries[0]
  100. }
  101. // add optional properties to make validation successful
  102. har.url = har.url || options.url || options.uri || options.baseUrl || '/'
  103. har.httpVersion = har.httpVersion || 'HTTP/1.1'
  104. har.queryString = har.queryString || []
  105. har.headers = har.headers || []
  106. har.cookies = har.cookies || []
  107. har.postData = har.postData || {}
  108. har.postData.mimeType = har.postData.mimeType || 'application/octet-stream'
  109. har.bodySize = 0
  110. har.headersSize = 0
  111. har.postData.size = 0
  112. if (!validate.request(har)) {
  113. return options
  114. }
  115. // clean up and get some utility properties
  116. var req = this.prep(har)
  117. // construct new options
  118. if (req.url) {
  119. options.url = req.url
  120. }
  121. if (req.method) {
  122. options.method = req.method
  123. }
  124. if (Object.keys(req.queryObj).length) {
  125. options.qs = req.queryObj
  126. }
  127. if (Object.keys(req.headersObj).length) {
  128. options.headers = req.headersObj
  129. }
  130. function test (type) {
  131. return req.postData.mimeType.indexOf(type) === 0
  132. }
  133. if (test('application/x-www-form-urlencoded')) {
  134. options.form = req.postData.paramsObj
  135. } else if (test('application/json')) {
  136. if (req.postData.jsonObj) {
  137. options.body = req.postData.jsonObj
  138. options.json = true
  139. }
  140. } else if (test('multipart/form-data')) {
  141. options.formData = {}
  142. req.postData.params.forEach(function (param) {
  143. var attachment = {}
  144. if (!param.fileName && !param.contentType) {
  145. options.formData[param.name] = param.value
  146. return
  147. }
  148. // attempt to read from disk!
  149. if (param.fileName && !param.value) {
  150. attachment.value = fs.createReadStream(param.fileName)
  151. } else if (param.value) {
  152. attachment.value = param.value
  153. }
  154. if (param.fileName) {
  155. attachment.options = {
  156. filename: param.fileName,
  157. contentType: param.contentType ? param.contentType : null
  158. }
  159. }
  160. options.formData[param.name] = attachment
  161. })
  162. } else {
  163. if (req.postData.text) {
  164. options.body = req.postData.text
  165. }
  166. }
  167. return options
  168. }
  169. exports.Har = Har