* @see https://andreubotella.github.io/multipart-form-data/#multipart-form-data-parser * @param {Buffer} input * @param {ReturnType } mimeType
(input, mimeType)
| 62 | * @param {ReturnType<import('./data-url')['parseMIMEType']>} mimeType |
| 63 | */ |
| 64 | function multipartFormDataParser (input, mimeType) { |
| 65 | // 1. Assert: mimeType’s essence is "multipart/form-data". |
| 66 | assert(mimeType !== 'failure' && mimeType.essence === 'multipart/form-data') |
| 67 | |
| 68 | const boundaryString = mimeType.parameters.get('boundary') |
| 69 | |
| 70 | // 2. If mimeType’s parameters["boundary"] does not exist, return failure. |
| 71 | // Otherwise, let boundary be the result of UTF-8 decoding mimeType’s |
| 72 | // parameters["boundary"]. |
| 73 | if (boundaryString === undefined) { |
| 74 | throw parsingError('missing boundary in content-type header') |
| 75 | } |
| 76 | |
| 77 | const boundary = Buffer.from(`--${boundaryString}`, 'utf8') |
| 78 | |
| 79 | // 3. Let entry list be an empty entry list. |
| 80 | const entryList = [] |
| 81 | |
| 82 | // 4. Let position be a pointer to a byte in input, initially pointing at |
| 83 | // the first byte. |
| 84 | const position = { position: 0 } |
| 85 | |
| 86 | // Note: Per RFC 2046 Section 5.1.1, we must ignore anything before the |
| 87 | // first boundary delimiter line (preamble). Search for the first boundary. |
| 88 | const firstBoundaryIndex = input.indexOf(boundary) |
| 89 | |
| 90 | if (firstBoundaryIndex === -1) { |
| 91 | throw parsingError('no boundary found in multipart body') |
| 92 | } |
| 93 | |
| 94 | // Start parsing from the first boundary, ignoring any preamble |
| 95 | position.position = firstBoundaryIndex |
| 96 | |
| 97 | // 5. While true: |
| 98 | while (true) { |
| 99 | // 5.1. If position points to a sequence of bytes starting with 0x2D 0x2D |
| 100 | // (`--`) followed by boundary, advance position by 2 + the length of |
| 101 | // boundary. Otherwise, return failure. |
| 102 | // Note: boundary is padded with 2 dashes already, no need to add 2. |
| 103 | if (input.subarray(position.position, position.position + boundary.length).equals(boundary)) { |
| 104 | position.position += boundary.length |
| 105 | } else { |
| 106 | throw parsingError('expected a value starting with -- and the boundary') |
| 107 | } |
| 108 | |
| 109 | // 5.2. If position points to the sequence of bytes 0x2D 0x2D 0x0D 0x0A |
| 110 | // (`--` followed by CR LF) followed by the end of input, return entry list. |
| 111 | // Note: Per RFC 2046 Section 5.1.1, we must ignore anything after the |
| 112 | // final boundary delimiter (epilogue). Check for -- or --CRLF and return |
| 113 | // regardless of what follows. |
| 114 | if (bufferStartsWith(input, dd, position)) { |
| 115 | // Found closing boundary delimiter (--), ignore any epilogue |
| 116 | return entryList |
| 117 | } |
| 118 | |
| 119 | // 5.3. If position does not point to a sequence of bytes starting with 0x0D |
| 120 | // 0x0A (CR LF), return failure. |
| 121 | if (input[position.position] !== 0x0d || input[position.position + 1] !== 0x0a) { |
no test coverage detected