Skip to content

Commit ac77285

Browse files
committedMay 14, 2020
Revert "removed HTTP parser, now its a dependency"
This reverts commit ad60362.
1 parent ad60362 commit ac77285

File tree

5 files changed

+143
-12
lines changed

5 files changed

+143
-12
lines changed
 

‎package-lock.json

-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
"@types/node": "^13.7.7",
4343
"axios": "^0.19.2",
4444
"express": "^4.17.1",
45-
"raw-request-parser": "^1.0.7",
4645
"readline": "^1.3.0",
4746
"xpath": "0.0.27",
4847
"yargs": "^15.3.0"

‎src/HTTPParser.ts

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import fs from 'fs';
2+
import * as Axios from 'axios';
3+
import { Method } from 'axios';
4+
import { json } from 'express';
5+
6+
export interface RequestStruct {
7+
headers: Record<string, string>;
8+
body: string;
9+
url: string;
10+
method: Method;
11+
}
12+
13+
export class HTTPParser {
14+
15+
public static fromString(fileContent: string): RequestStruct | null {
16+
try {
17+
fileContent = fileContent.replace(/(\r\n|\n|\r)/gm, "\n");
18+
19+
let firstNewLineIndex = fileContent.indexOf("\n");
20+
if (firstNewLineIndex === -1) firstNewLineIndex = fileContent.length;
21+
22+
const startLine = fileContent.substring(0, firstNewLineIndex);
23+
const startLineArray = startLine.split(" ");
24+
const method: Method = startLineArray[0] as Method;
25+
const path = startLineArray[1];
26+
let emptyLine = fileContent.indexOf("\n\n");
27+
if (emptyLine === -1) emptyLine = fileContent.length;
28+
29+
const headersArray = fileContent.substring(firstNewLineIndex + 1, emptyLine).split("\n");
30+
const headers: Record<string, string> = {};
31+
const body = fileContent.substring(emptyLine + 2);
32+
33+
headersArray.forEach(h => {
34+
const delimiterIndex = h.indexOf(":");
35+
const name = h.substring(0, delimiterIndex);
36+
const value = h.substring(delimiterIndex + 2);
37+
headers[name] = value;
38+
});
39+
40+
//if full url is not in the request lets assume an http to host header
41+
let url: string;
42+
if (path.startsWith("http"))
43+
url = path;
44+
else
45+
url = "http://" + (headers['host'] || headers['Host']) + path;
46+
47+
//let axios calculate the length, since we may change it
48+
delete headers["content-length"];
49+
delete headers["Content-Length"];
50+
return { body: body, headers: headers, method: method, url: url };
51+
}
52+
catch (ex) {
53+
console.error("[HTTPParser] - Error parsing request file");
54+
console.error(ex);
55+
return null;
56+
}
57+
}
58+
59+
public static async fromFile(filepath: string): Promise<RequestStruct | null> {
60+
return new Promise((resolve, reject) => {
61+
fs.readFile(filepath, (err, data: Buffer) => {
62+
if (err) { reject(err); return; }
63+
64+
resolve(HTTPParser.fromString(data.toString()));
65+
66+
});
67+
});
68+
}
69+
70+
71+
72+
public static axiosResponseToString(r: Axios.AxiosResponse): string {
73+
let responseString = `HTTP/${r.request.res.httpVersion} ${r.status} ${r.statusText}\n`;
74+
75+
Object.entries(r.headers).forEach(([key, value]) => {
76+
responseString += `${key}: ${value}\n`;
77+
});
78+
79+
const body = (typeof r.data === "object") ? JSON.stringify(r.data) : r.data;
80+
responseString += `\n${body}`;
81+
82+
return responseString;
83+
}
84+
}

‎src/Requester.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import fs from 'fs';
12
import * as Axios from 'axios';
23
import * as Https from 'https';
34
import { PayloadGenerator } from './PayloadGenerator';
4-
import { RawRequestParser, RequestStruct } from 'raw-request-parser';
5+
import { HTTPParser, RequestStruct } from './HTTPParser';
56

67

78

@@ -11,7 +12,7 @@ export class Requester {
1112
public static async fromFile(templatePath: string, payload: string, line?: string): Promise<[Axios.AxiosResponse, string?]> {
1213

1314
return new Promise((resolve, reject) => {
14-
RawRequestParser.fromFile(templatePath).then((parsedRequest) => {
15+
HTTPParser.fromFile(templatePath).then((parsedRequest) => {
1516
Requester.doRequest(parsedRequest, payload).then((response) => { resolve([response, line]); }).catch(err => reject([err, line]));
1617
});
1718

@@ -21,7 +22,7 @@ export class Requester {
2122
public static async fromString(requestContent: string, payload: string, line?: string): Promise<[Axios.AxiosResponse, string?]> {
2223

2324
return new Promise((resolve, reject) => {
24-
const parsedRequest = RawRequestParser.fromString(requestContent);
25+
const parsedRequest = HTTPParser.fromString(requestContent);
2526
Requester.doRequest(parsedRequest, payload).then((response) => { resolve([response, line]); }).catch(err => reject([err, line]));
2627
});
2728
}

‎tests/HTTPParser.test.ts

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
import { expect } from "chai";
3+
import * as Axios from 'axios';
4+
import { XXEServer } from "../src/XXEServer";
5+
import { HTTPParser } from "../src/HTTPParser";
6+
import fs from 'fs';
7+
import path from 'path';
8+
9+
describe('HTTPParser', () => {
10+
11+
12+
it('should parse http request from string', () => {
13+
const request = `GET http://localhost:8000/ HTTP/1.1\nHost: localhost:8000\nConnection: keep-alive\n\ntest`;
14+
15+
const parsed = HTTPParser.fromString(request);
16+
expect(Object.keys(parsed?.headers || {}).length).to.eq(2);
17+
expect(parsed?.body).to.eq("test");
18+
expect(parsed?.url).to.eq("http://localhost:8000/");
19+
expect(parsed?.method).to.eq("GET");
20+
21+
});
22+
23+
it("should parse axios response object", () => {
24+
const s = new XXEServer("127.0.0.1", 18363);
25+
s.addRoute("/", "body");
26+
s.start();
27+
28+
const r = Axios.default({
29+
method: "GET",
30+
url: "http://127.0.0.1:18363/",
31+
});
32+
33+
r.then(resp => {
34+
const respString = HTTPParser.axiosResponseToString(resp);
35+
expect(respString).to.contain("HTTP/1.1 200 OK");
36+
expect(respString).to.contain("x-powered-by: Express");
37+
expect(respString).to.contain("\n\nbody");
38+
expect(respString).to.contain("connection: close");
39+
40+
s.stop();
41+
});
42+
43+
});
44+
45+
46+
it("should parse request from filepath", () => {
47+
HTTPParser.fromFile("tests/data/request.txt").then((parsed) => {
48+
expect(Object.keys(parsed?.headers || {}).length).to.eq(2);
49+
expect(parsed?.body).to.eq("test");
50+
expect(parsed?.url).to.eq("http://localhost:8000/");
51+
expect(parsed?.method).to.eq("GET");
52+
});
53+
});
54+
55+
});

0 commit comments

Comments
 (0)
Please sign in to comment.