UNPKG

7.61 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /**
8 * Copyright (c) 2015-present, Facebook, Inc.
9 * All rights reserved.
10 *
11 * This source code is licensed under the BSD-style license found in the
12 * LICENSE file in the root directory of this source tree. An additional grant
13 * of patent rights can be found in the PATENTS file in the same directory.
14 *
15 * strict
16 */
17
18exports.parseBody = parseBody;
19
20var _contentType = require('content-type');
21
22var _contentType2 = _interopRequireDefault(_contentType);
23
24var _rawBody = require('raw-body');
25
26var _rawBody2 = _interopRequireDefault(_rawBody);
27
28var _httpErrors = require('http-errors');
29
30var _httpErrors2 = _interopRequireDefault(_httpErrors);
31
32var _querystring = require('querystring');
33
34var _querystring2 = _interopRequireDefault(_querystring);
35
36var _zlib = require('zlib');
37
38var _zlib2 = _interopRequireDefault(_zlib);
39
40function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
41
42/**
43 * Provided a "Request" provided by express or connect (typically a node style
44 * HTTPClientRequest), Promise the body data contained.
45 */
46function parseBody(req) {
47 return new Promise(function (resolve, reject) {
48 var body = req.body;
49
50 // If express has already parsed a body as a keyed object, use it.
51 if ((typeof body === 'undefined' ? 'undefined' : _typeof(body)) === 'object' && !(body instanceof Buffer)) {
52 return resolve(body);
53 }
54
55 // Skip requests without content types.
56 if (req.headers['content-type'] === undefined) {
57 return resolve({});
58 }
59
60 var typeInfo = _contentType2.default.parse(req);
61
62 // If express has already parsed a body as a string, and the content-type
63 // was application/graphql, parse the string body.
64 if (typeof body === 'string' && typeInfo.type === 'application/graphql') {
65 return resolve(graphqlParser(body));
66 }
67
68 // Already parsed body we didn't recognise? Parse nothing.
69 if (body) {
70 return resolve({});
71 }
72
73 // Use the correct body parser based on Content-Type header.
74 switch (typeInfo.type) {
75 case 'application/graphql':
76 return read(req, typeInfo, graphqlParser, resolve, reject);
77 case 'application/json':
78 return read(req, typeInfo, jsonEncodedParser, resolve, reject);
79 case 'application/x-www-form-urlencoded':
80 return read(req, typeInfo, urlEncodedParser, resolve, reject);
81 }
82
83 // If no Content-Type header matches, parse nothing.
84 return resolve({});
85 });
86}
87
88function jsonEncodedParser(body) {
89 if (jsonObjRegex.test(body)) {
90 /* eslint-disable no-empty */
91 try {
92 return JSON.parse(body);
93 } catch (error) {}
94 // Do nothing
95
96 /* eslint-enable no-empty */
97 }
98 throw (0, _httpErrors2.default)(400, 'POST body sent invalid JSON.');
99}
100
101function urlEncodedParser(body) {
102 return _querystring2.default.parse(body);
103}
104
105function graphqlParser(body) {
106 return { query: body };
107}
108
109/**
110 * RegExp to match an Object-opening brace "{" as the first non-space
111 * in a string. Allowed whitespace is defined in RFC 7159:
112 *
113 * x20 Space
114 * x09 Horizontal tab
115 * x0A Line feed or New line
116 * x0D Carriage return
117 */
118var jsonObjRegex = /^[\x20\x09\x0a\x0d]*\{/;
119
120// Read and parse a request body.
121function read(req, typeInfo, parseFn, resolve, reject) {
122 var charset = (typeInfo.parameters.charset || 'utf-8').toLowerCase();
123
124 // Assert charset encoding per JSON RFC 7159 sec 8.1
125 if (charset.slice(0, 4) !== 'utf-') {
126 throw (0, _httpErrors2.default)(415, 'Unsupported charset "' + charset.toUpperCase() + '".');
127 }
128
129 // Get content-encoding (e.g. gzip)
130 var contentEncoding = req.headers['content-encoding'];
131 var encoding = typeof contentEncoding === 'string' ? contentEncoding.toLowerCase() : 'identity';
132 var length = encoding === 'identity' ? req.headers['content-length'] : null;
133 var limit = 100 * 1024; // 100kb
134 var stream = decompressed(req, encoding);
135
136 // Read body from stream.
137 (0, _rawBody2.default)(stream, { encoding: charset, length: length, limit: limit }, function (err, body) {
138 if (err) {
139 return reject(err.type === 'encoding.unsupported' ? (0, _httpErrors2.default)(415, 'Unsupported charset "' + charset.toUpperCase() + '".') : (0, _httpErrors2.default)(400, 'Invalid body: ' + err.message + '.'));
140 }
141
142 try {
143 // Decode and parse body.
144 return resolve(parseFn(body));
145 } catch (error) {
146 return reject(error);
147 }
148 });
149}
150
151// Return a decompressed stream, given an encoding.
152function decompressed(req, encoding) {
153 switch (encoding) {
154 case 'identity':
155 return req;
156 case 'deflate':
157 return req.pipe(_zlib2.default.createInflate());
158 case 'gzip':
159 return req.pipe(_zlib2.default.createGunzip());
160 }
161 throw (0, _httpErrors2.default)(415, 'Unsupported content-encoding "' + encoding + '".');
162}
\No newline at end of file