curlparser.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. import * as cookie from "cookie"
  2. import * as URL from "url"
  3. import * as querystring from "querystring"
  4. import parser from "yargs-parser"
  5. /**
  6. * given this: [ 'msg1=value1', 'msg2=value2' ]
  7. * output this: 'msg1=value1&msg2=value2'
  8. * @param dataArguments
  9. */
  10. const joinDataArguments = (dataArguments) => {
  11. let data = ""
  12. dataArguments.forEach((argument, i) => {
  13. if (i === 0) {
  14. data += argument
  15. } else {
  16. data += `&${argument}`
  17. }
  18. })
  19. return data
  20. }
  21. const parseCurlCommand = (curlCommand) => {
  22. let newlineFound = /\\/gi.test(curlCommand)
  23. if (newlineFound) {
  24. // remove '\' and newlines
  25. curlCommand = curlCommand.replace(/\\/gi, "")
  26. curlCommand = curlCommand.replace(/\n/g, "")
  27. }
  28. // yargs parses -XPOST as separate arguments. just prescreen for it.
  29. curlCommand = curlCommand.replace(/ -XPOST/, " -X POST")
  30. curlCommand = curlCommand.replace(/ -XGET/, " -X GET")
  31. curlCommand = curlCommand.replace(/ -XPUT/, " -X PUT")
  32. curlCommand = curlCommand.replace(/ -XPATCH/, " -X PATCH")
  33. curlCommand = curlCommand.replace(/ -XDELETE/, " -X DELETE")
  34. curlCommand = curlCommand.trim()
  35. let parsedArguments = parser(curlCommand)
  36. let cookieString
  37. let cookies
  38. let url = parsedArguments._[1]
  39. if (!url) {
  40. for (let argName in parsedArguments) {
  41. if (typeof parsedArguments[argName] === "string") {
  42. if (["http", "www."].includes(parsedArguments[argName])) {
  43. url = parsedArguments[argName]
  44. }
  45. }
  46. }
  47. }
  48. let headers
  49. const parseHeaders = (headerFieldName) => {
  50. if (parsedArguments[headerFieldName]) {
  51. if (!headers) {
  52. headers = {}
  53. }
  54. if (!Array.isArray(parsedArguments[headerFieldName])) {
  55. parsedArguments[headerFieldName] = [parsedArguments[headerFieldName]]
  56. }
  57. parsedArguments[headerFieldName].forEach((header) => {
  58. if (header.includes("Cookie")) {
  59. // stupid javascript tricks: closure
  60. cookieString = header
  61. } else {
  62. let colonIndex = header.indexOf(":")
  63. let headerName = header.substring(0, colonIndex)
  64. let headerValue = header.substring(colonIndex + 1).trim()
  65. headers[headerName] = headerValue
  66. }
  67. })
  68. }
  69. }
  70. parseHeaders("H")
  71. parseHeaders("header")
  72. if (parsedArguments.A) {
  73. if (!headers) {
  74. headers = []
  75. }
  76. headers["User-Agent"] = parsedArguments.A
  77. } else if (parsedArguments["user-agent"]) {
  78. if (!headers) {
  79. headers = []
  80. }
  81. headers["User-Agent"] = parsedArguments["user-agent"]
  82. }
  83. if (parsedArguments.b) {
  84. cookieString = parsedArguments.b
  85. }
  86. if (parsedArguments.cookie) {
  87. cookieString = parsedArguments.cookie
  88. }
  89. let multipartUploads
  90. if (parsedArguments.F) {
  91. multipartUploads = {}
  92. if (!Array.isArray(parsedArguments.F)) {
  93. parsedArguments.F = [parsedArguments.F]
  94. }
  95. parsedArguments.F.forEach((multipartArgument) => {
  96. // input looks like key=value. value could be json or a file path prepended with an @
  97. const [key, value] = multipartArgument.split("=", 2)
  98. multipartUploads[key] = value
  99. })
  100. }
  101. if (cookieString) {
  102. const cookieParseOptions = {
  103. decode: (s) => s,
  104. }
  105. // separate out cookie headers into separate data structure
  106. // note: cookie is case insensitive
  107. cookies = cookie.parse(cookieString.replace(/^Cookie: /gi, ""), cookieParseOptions)
  108. }
  109. let method
  110. if (parsedArguments.X === "POST") {
  111. method = "post"
  112. } else if (parsedArguments.X === "PUT" || parsedArguments["T"]) {
  113. method = "put"
  114. } else if (parsedArguments.X === "PATCH") {
  115. method = "patch"
  116. } else if (parsedArguments.X === "DELETE") {
  117. method = "delete"
  118. } else if (parsedArguments.X === "OPTIONS") {
  119. method = "options"
  120. } else if (
  121. (parsedArguments["d"] ||
  122. parsedArguments["data"] ||
  123. parsedArguments["data-ascii"] ||
  124. parsedArguments["data-binary"] ||
  125. parsedArguments["F"] ||
  126. parsedArguments["form"]) &&
  127. !(parsedArguments["G"] || parsedArguments["get"])
  128. ) {
  129. method = "post"
  130. } else if (parsedArguments["I"] || parsedArguments["head"]) {
  131. method = "head"
  132. } else {
  133. method = "get"
  134. }
  135. let compressed = !!parsedArguments.compressed
  136. let urlObject = URL.parse(url) // eslint-disable-line
  137. // if GET request with data, convert data to query string
  138. // NB: the -G flag does not change the http verb. It just moves the data into the url.
  139. if (parsedArguments["G"] || parsedArguments["get"]) {
  140. urlObject.query = urlObject.query ? urlObject.query : ""
  141. let option = "d" in parsedArguments ? "d" : "data" in parsedArguments ? "data" : null
  142. if (option) {
  143. let urlQueryString = ""
  144. if (!url.includes("?")) {
  145. url += "?"
  146. } else {
  147. urlQueryString += "&"
  148. }
  149. if (typeof parsedArguments[option] === "object") {
  150. urlQueryString += parsedArguments[option].join("&")
  151. } else {
  152. urlQueryString += parsedArguments[option]
  153. }
  154. urlObject.query += urlQueryString
  155. url += urlQueryString
  156. delete parsedArguments[option]
  157. }
  158. }
  159. let query = querystring.parse(urlObject.query, null, null, {
  160. maxKeys: 10000,
  161. })
  162. urlObject.search = null // Clean out the search/query portion.
  163. const request = {
  164. url,
  165. urlWithoutQuery: URL.format(urlObject),
  166. }
  167. if (compressed) {
  168. request["compressed"] = true
  169. }
  170. if (Object.keys(query).length > 0) {
  171. request.query = query
  172. }
  173. if (headers) {
  174. request.headers = headers
  175. }
  176. request["method"] = method
  177. if (cookies) {
  178. request.cookies = cookies
  179. request.cookieString = cookieString.replace("Cookie: ", "")
  180. }
  181. if (multipartUploads) {
  182. request.multipartUploads = multipartUploads
  183. }
  184. if (parsedArguments.data) {
  185. request.data = parsedArguments.data
  186. } else if (parsedArguments["data-binary"]) {
  187. request.data = parsedArguments["data-binary"]
  188. request.isDataBinary = true
  189. } else if (parsedArguments["d"]) {
  190. request.data = parsedArguments["d"]
  191. } else if (parsedArguments["data-ascii"]) {
  192. request.data = parsedArguments["data-ascii"]
  193. }
  194. if (parsedArguments["u"]) {
  195. request.auth = parsedArguments["u"]
  196. }
  197. if (parsedArguments["user"]) {
  198. request.auth = parsedArguments["user"]
  199. }
  200. if (Array.isArray(request.data)) {
  201. request.dataArray = request.data
  202. request.data = joinDataArguments(request.data)
  203. }
  204. if (parsedArguments["k"] || parsedArguments["insecure"]) {
  205. request.insecure = true
  206. }
  207. return request
  208. }
  209. export default parseCurlCommand