docs: function bodyReader(options) {
      var opts = options || {};
      assert.object(opts, 'opts');
  
      var maxBodySize = opts.maxBodySize || 0;
  
      function readBody(req, res, next) {
          // #100 don't read the body again if we've read it once
          if (req._readBody) {
              next();
              return;
          } else {
              req._readBody = true;
          }
  
          if ((req.getContentLength() === 0 && !req.isChunked()) ||
              req.contentType() === 'multipart/form-data' ||
              req.contentType() === 'application/octet-stream') {
              next();
              return;
          }
          var bodyWriter = createBodyWriter(req);
  
          var bytesReceived = 0;
          var digest;
          var gz;
          var hash;
          var md5;
  
          var unsupportedCompression;
  
          if ((md5 = req.headers['content-md5'])) {
              hash = crypto.createHash('md5');
          }
  
          function done() {
              bodyWriter.end();
  
              if (unsupportedCompression) {
                  var error = unsupportedCompression + ' not supported';
                  next(new UnsupportedMediaTypeError(error));
                  return;
              }
  
              if (maxBodySize && bytesReceived > maxBodySize) {
                  var msg = 'Request body size exceeds ' + maxBodySize;
                  var err;
  
                  // Between Node 0.12 and 4 http status code messages changed
                  // RequestEntityTooLarge was changed to PayloadTooLarge
                  // this check is to maintain backwards compatibility
                  if (PayloadTooLargeError !== undefined) {
                      err = new PayloadTooLargeError(msg);
                  } else {
                      err = new RequestEntityTooLargeError(msg);
                  }
  
                  next(err);
                  return;
              }
  
              if (!req.body.length) {
                  next();
                  return;
              }
  
              if (hash && md5 !== (digest = hash.digest('base64'))) {
                  next(new BadDigestError(MD5_MSG, md5, digest));
                  return;
              }
  
              next();
          }
  
          if (req.headers['content-encoding'] === undefined) {
              // This handles the original else branch
              req.once('end', done);
          } else if (req.headers['content-encoding'] === 'gzip') {
              gz = zlib.createGunzip();
              gz.on('data', bodyWriter.write);
              gz.once('end', done);
              req.once('end', gz.end.bind(gz));
          } else {
              unsupportedCompression = req.headers['content-encoding'];
              res.setHeader('Accept-Encoding', 'gzip');
              req.once('end', done);
          }
  
          req.on('data', function onRequestData(chunk) {
              if (maxBodySize) {
                  bytesReceived += chunk.length;
  
                  if (bytesReceived > maxBodySize) {
                      return;
                  }
              }
  
              if (hash) {
                  hash.update(chunk, 'binary');
              }
  
              if (gz) {
                  gz.write(chunk);
              } else {
                  bodyWriter.write(chunk);
              }
          });
  
          req.once('error', next);
          req.resume();
      }
  
      return (readBody);
  }