Skip to content

Commit 4413919

Browse files
authored
Fixed parsing messages with no headers (#14)
2 parents b9f6088 + 4b3376b commit 4413919

File tree

3 files changed

+64
-7
lines changed

3 files changed

+64
-7
lines changed

src/Component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@ export class Component implements Part {
2222
* @param data Component byte representation to parse
2323
*/
2424
public static parse(data: Uint8Array): Component {
25-
const headersEndIndex = Multipart.findSequenceIndex(data, Multipart.combineArrays([Multipart.CRLF, Multipart.CRLF]));
25+
const hasHeaders = Multipart.findSequenceIndex(data, Multipart.CRLF) !== 0;
26+
const headersEndIndex = hasHeaders ? Multipart.findSequenceIndex(data, Multipart.combineArrays([Multipart.CRLF, Multipart.CRLF])) + 2 : 0;
2627

2728
const headersBuffer = data.slice(0, headersEndIndex);
28-
const body = data.slice(headersEndIndex + 4);
29+
const body = data.slice(headersEndIndex + 2);
2930

3031
const headersString = new TextDecoder().decode(headersBuffer);
3132
const headers = new Headers();

test/Component.test.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {expect} from "chai";
2-
import {Component} from "../dist/index.js";
2+
import {Multipart, Component} from "../dist/index.js";
33

44
describe("Component", () => {
55

@@ -35,7 +35,7 @@ describe("Component", () => {
3535
it("should parse headers and body correctly from Uint8Array", () => {
3636
const headers = "Content-Type: text/plain\r\nContent-Length: 5\r\n\r\n";
3737
const body = new Uint8Array([1, 2, 3, 4, 5]);
38-
const data = new Uint8Array([...headers.split("").map(c => c.charCodeAt(0)), ...body]);
38+
const data = Multipart.combineArrays([new TextEncoder().encode(headers), body]);
3939

4040
const component = Component.parse(data);
4141

@@ -45,8 +45,8 @@ describe("Component", () => {
4545
expect(component.body).to.deep.equal(body);
4646
});
4747

48-
it("should handle missing headers and body", () => {
49-
const data = new Uint8Array([0x0D, 0x0A, 0x0D, 0x0A]);
48+
it("should handle missing headers and empty body", () => {
49+
const data = new Uint8Array([0x0D, 0x0A]);
5050

5151
const component = Component.parse(data);
5252

@@ -57,14 +57,25 @@ describe("Component", () => {
5757

5858
it("should handle headers with no body", () => {
5959
const headers = "Content-Type: text/plain\r\n\r\n";
60-
const data = new Uint8Array([...headers.split("").map(c => c.charCodeAt(0))]);
60+
const data = new TextEncoder().encode(headers);
6161

6262
const component = Component.parse(data);
6363

6464
expect(component.headers.get("Content-Type")).to.equal("text/plain");
6565

6666
expect(component.body).to.deep.equal(new Uint8Array(0));
6767
});
68+
69+
it("should handle body with no headers", () => {
70+
const body = "\r\nGoal: No headers!\r\n\r\nReally none.\r\n";
71+
const data = new TextEncoder().encode(body);
72+
73+
const component = Component.parse(data);
74+
75+
expect(component.headers).to.be.empty;
76+
77+
expect(new TextDecoder().decode(component.body)).to.equal("Goal: No headers!\r\n\r\nReally none.\r\n");
78+
});
6879
});
6980

7081
describe("#bytes", () => {

test/Multipart.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,51 @@ describe("Multipart", function () {
4141

4242
expect(parsedMultipart).to.be.an.instanceof(Multipart);
4343
expect(parsedMultipart.parts.length).to.equal(2);
44+
const part1 = parsedMultipart.parts[0];
45+
expect(part1.headers.get("x-foo")).to.equal("bar");
46+
expect(part1.body).to.deep.equal(component1.body);
47+
const part2 = parsedMultipart.parts[1];
48+
expect(part2.headers.get("content-type")).to.equal("text/plain");
49+
expect(part2.body).to.deep.equal(component2.body);
50+
});
51+
52+
it("should parse Multipart data from RFC 2046 5.1.1 example body", function () {
53+
const string =
54+
'From: Nathaniel Borenstein <[email protected]>\r\n' +
55+
'To: Ned Freed <[email protected]>\r\n' +
56+
'Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)\r\n' +
57+
'Subject: Sample message\r\n' +
58+
'MIME-Version: 1.0\r\n' +
59+
'Content-type: multipart/mixed; boundary="simple boundary"\r\n' +
60+
'\r\n' +
61+
'This is the preamble. It is to be ignored, though it\n' +
62+
'is a handy place for composition agents to include an\r\n' +
63+
'explanatory note to non-MIME conformant readers.\n' +
64+
'\r\n' +
65+
'--simple boundary\r\n' +
66+
'\r\n' +
67+
'This is implicitly typed plain US-ASCII text.\r\n' +
68+
'It does NOT end with a linebreak.\r\n' +
69+
'--simple boundary\r\n' +
70+
'Content-type: text/plain; charset=us-ascii\r\n' +
71+
'\r\n' +
72+
'This is explicitly typed plain US-ASCII text.\r\n' +
73+
'It DOES end with a linebreak.\r\n' +
74+
'\r\n' +
75+
'--simple boundary--\r\n' +
76+
'\r\n' +
77+
'This is the epilogue. It is also to be ignored.';
78+
79+
const bytes = new TextEncoder().encode(string);
80+
const parsedMultipart = Multipart.parse(bytes);
81+
82+
expect(parsedMultipart).to.be.an.instanceof(Multipart);
83+
expect(parsedMultipart.parts.length).to.equal(2);
84+
const part1 = parsedMultipart.parts[0];
85+
expect(new TextDecoder().decode(part1.body)).to.equal("This is implicitly typed plain US-ASCII text.\r\nIt does NOT end with a linebreak.");
86+
const part2 = parsedMultipart.parts[1];
87+
expect(part2.headers.get("content-type")).to.equal("text/plain; charset=us-ascii");
88+
expect(new TextDecoder().decode(part2.body)).to.equal("This is explicitly typed plain US-ASCII text.\r\nIt DOES end with a linebreak.\r\n");
4489
});
4590

4691
it("should handle nested multiparts", function () {

0 commit comments

Comments
 (0)