Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

《看源码学web》【如何解析form(二)】 #6

Open
gamebody opened this issue Feb 19, 2019 · 0 comments
Open

《看源码学web》【如何解析form(二)】 #6

gamebody opened this issue Feb 19, 2019 · 0 comments

Comments

@gamebody
Copy link
Owner

gamebody commented Feb 19, 2019

node-formiable分析

代码分类

lib/file.js 文件上传
lib/incoming_form.js 分层
lib/index.js 入口文件
lib/json_parser.js 处理json
lib/multipart_parser.js 处理 multipart 表单
lib/octet_parser.js 处理未分类的二进制
lib/querystring_parser.js 处理 querystring

incoming_form.js

这个文件是主要的逻辑文件,用来注册各个parser的回调处理和各种类型数据的处理。

IncomingForm.prototype._parseContentType = function() {
    if (this.bytesExpected === 0) {
        this._parser = dummyParser(this);
        return;
    }

    if (!this.headers['content-type']) {
        this._error(new Error('bad content-type header, no content-type'));
        return;
    }

    if (this.headers['content-type'].match(/octet-stream/i)) {
        this._initOctetStream();
        return;
    }

    if (this.headers['content-type'].match(/urlencoded/i)) {
        this._initUrlencoded();
        return;
    }

    if (this.headers['content-type'].match(/multipart/i)) {
        var m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i);
        if (m) {
        this._initMultipart(m[1] || m[2]);
        } else {
        this._error(new Error('bad content-type header, no multipart boundary'));
        }
        return;
    }

    if (this.headers['content-type'].match(/json/i)) {
        this._initJSONencoded();
        return;
    }

    this._error(new Error('bad content-type header, unknown content-type: '+this.headers['content-type']));
};

可以看到主要处理了四种格式的数据,对于这四种数据有四种parser。
lib/json_parser.js 处理json
lib/multipart_parser.js 处理 multipart 表单
lib/octet_parser.js 处理未分类的二进制
lib/querystring_parser.js 处理 querystring

json_parser.js

if (global.GENTLY) require = GENTLY.hijack(require);

var Buffer = require('buffer').Buffer;

function JSONParser(parent) {
    this.parent = parent;
    this.chunks = [];
    this.bytesWritten = 0;
}
exports.JSONParser = JSONParser;

JSONParser.prototype.write = function(buffer) {
    this.bytesWritten += buffer.length;
    this.chunks.push(buffer);
    return buffer.length;
};

JSONParser.prototype.end = function() {
    try {
        var fields = JSON.parse(Buffer.concat(this.chunks));
        for (var field in fields) {
            this.onField(field, fields[field]);
        }
    } catch (e) {
        this.parent.emit('error', e);
    }
    this.data = null;

    this.onEnd();
};

json_parser的主要逻辑 JSON.parse(Buffer.concat(this.chunks)),this.onField()是注册进来的。

octet_parser.js

var EventEmitter = require('events').EventEmitter
	, util = require('util');

function OctetParser(options){
	if(!(this instanceof OctetParser)) return new OctetParser(options);
	EventEmitter.call(this);
}

util.inherits(OctetParser, EventEmitter);

exports.OctetParser = OctetParser;

OctetParser.prototype.write = function(buffer) {
    this.emit('data', buffer);
	return buffer.length;
};

OctetParser.prototype.end = function() {
	this.emit('end');
};

只是 this.emit('data', buffer);

querystring_parser.js

if (global.GENTLY) require = GENTLY.hijack(require);

// This is a buffering parser, not quite as nice as the multipart one.
// If I find time I'll rewrite this to be fully streaming as well
var querystring = require('querystring');

function QuerystringParser(maxKeys) {
  this.maxKeys = maxKeys;
  this.buffer = '';
}
exports.QuerystringParser = QuerystringParser;

QuerystringParser.prototype.write = function(buffer) {
  this.buffer += buffer.toString('ascii');
  return buffer.length;
};

QuerystringParser.prototype.end = function() {
  var fields = querystring.parse(this.buffer, '&', '=', { maxKeys: this.maxKeys });
  for (var field in fields) {
    this.onField(field, fields[field]);
  }
  this.buffer = '';

  this.onEnd();
};

主要的逻辑 querystring.parse(this.buffer, '&', '=', { maxKeys: this.maxKeys }),
然后触发从外部注册的 this.onField()

multipart_parser.js

    s = 0,
    S =
    { PARSER_UNINITIALIZED: s++,
      START: s++,
      START_BOUNDARY: s++,
      HEADER_FIELD_START: s++,
      HEADER_FIELD: s++,
      HEADER_VALUE_START: s++,
      HEADER_VALUE: s++,
      HEADER_VALUE_ALMOST_DONE: s++,
      HEADERS_ALMOST_DONE: s++,
      PART_DATA_START: s++,
      PART_DATA: s++,
      PART_END: s++,
      END: s++
    },

    f = 1,
    F =
    { PART_BOUNDARY: f,
      LAST_BOUNDARY: f *= 2
    },

    LF = 10,
    CR = 13,
    SPACE = 32,
    HYPHEN = 45,
    COLON = 58,
    A = 97,
    Z = 122,

    lower = function(c) {
      return c | 0x20;
    };

multipart_parser.js的逻辑比较复杂,就是遍历每个传进来的字符,记录不同的状态,分割数据,写入流,自行观看源码。

主要的流程逻辑。

req
    .on('error', function(err) {
      self._error(err);
    })
    .on('aborted', function() {
      self.emit('aborted');
      self._error(new Error('Request aborted'));
    })
    .on('data', function(buffer) {
      self.write(buffer);
    })
    .on('end', function() {
      if (self.error) {
        return;
      }

      var err = self._parser.end();
      if (err) {
        self._error(err);
      }
    });

这个库的源码还是不错的,依赖很少,学到一些http协议,node的req和流相关的一些知识,挖个坑,改天自己写一个formiable。😁

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant