'a'
mh-two-thousand-and-two
2024-04-12 44d2c92345cd156a59fc327b3060292a282d2893
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
'use strict';
 
const parse = require('co-body');
const copy = require('copy-to');
const typeis = require('type-is');
 
/**
 * @param [Object] opts
 *   - {String} jsonLimit default '1mb'
 *   - {String} formLimit default '56kb'
 *   - {string} encoding default 'utf-8'
 *   - {Object} extendTypes
 */
 
module.exports = function(opts) {
  opts = opts || {};
  const {detectJSON} = opts;
  const {onerror} = opts;
 
  const enableTypes = opts.enableTypes || ['json', 'form'];
  const enableForm = checkEnable(enableTypes, 'form');
  const enableJson = checkEnable(enableTypes, 'json');
  const enableText = checkEnable(enableTypes, 'text');
  const enableXml = checkEnable(enableTypes, 'xml');
 
  opts.detectJSON = undefined;
  opts.onerror = undefined; // eslint-disable-line unicorn/prefer-add-event-listener
 
  // force co-body return raw body
  opts.returnRawBody = true;
 
  // default json types
  const jsonTypes = [
    'application/json',
    'application/json-patch+json',
    'application/vnd.api+json',
    'application/csp-report',
    'application/scim+json'
  ];
 
  // default form types
  const formTypes = ['application/x-www-form-urlencoded'];
 
  // default text types
  const textTypes = ['text/plain'];
 
  // default xml types
  const xmlTypes = ['text/xml', 'application/xml'];
 
  const jsonOpts = formatOptions(opts, 'json');
  const formOpts = formatOptions(opts, 'form');
  const textOpts = formatOptions(opts, 'text');
  const xmlOpts = formatOptions(opts, 'xml');
 
  const extendTypes = opts.extendTypes || {};
 
  extendType(jsonTypes, extendTypes.json);
  extendType(formTypes, extendTypes.form);
  extendType(textTypes, extendTypes.text);
  extendType(xmlTypes, extendTypes.xml);
 
  // eslint-disable-next-line func-names
  return async function bodyParser(ctx, next) {
    if (ctx.request.body !== undefined || ctx.disableBodyParser)
      return await next(); // eslint-disable-line no-return-await
    try {
      const res = await parseBody(ctx);
      ctx.request.body = 'parsed' in res ? res.parsed : {};
      if (ctx.request.rawBody === undefined) ctx.request.rawBody = res.raw;
    } catch (err) {
      if (onerror) {
        onerror(err, ctx);
      } else {
        throw err;
      }
    }
 
    await next();
  };
 
  async function parseBody(ctx) {
    if (
      enableJson &&
      ((detectJSON && detectJSON(ctx)) ||
        isTypes(ctx.request.get('content-type'), jsonTypes))
    ) {
      return await parse.json(ctx, jsonOpts); // eslint-disable-line no-return-await
    }
 
    if (enableForm && ctx.request.is(formTypes)) {
      return await parse.form(ctx, formOpts); // eslint-disable-line no-return-await
    }
 
    if (enableText && ctx.request.is(textTypes)) {
      return (await parse.text(ctx, textOpts)) || '';
    }
 
    if (enableXml && ctx.request.is(xmlTypes)) {
      return (await parse.text(ctx, xmlOpts)) || '';
    }
 
    return {};
  }
};
 
function formatOptions(opts, type) {
  const res = {};
  copy(opts).to(res);
  res.limit = opts[type + 'Limit'];
  return res;
}
 
function extendType(original, extend) {
  if (extend) {
    if (!Array.isArray(extend)) {
      extend = [extend];
    }
 
    extend.forEach(function(extend) {
      original.push(extend);
    });
  }
}
 
function checkEnable(types, type) {
  return types.includes(type);
}
 
function isTypes(contentTypeValue, types) {
  if (typeof contentTypeValue === 'string') {
    // trim extra semicolon
    contentTypeValue = contentTypeValue.replace(/;$/, '');
  }
 
  return typeis.is(contentTypeValue, types);
}