UNPKG

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