har.js 4.7 KB

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