zhangjian
2023-08-07 6b009b0f6d3ef3aee97c362cebcd679d1b9088a3
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
'use strict';
 
var util = require('util');
var EventEmitter = require('events').EventEmitter;
 
function Hose(socket, options, filter) {
  EventEmitter.call(this);
 
  if (typeof options === 'function') {
    filter = options;
    options = {};
  }
 
  this.socket = socket;
  this.options = options;
  this.filter = filter;
 
  this.buffer = null;
 
  var self = this;
  this.listeners = {
    error: function(err) {
      return self.onError(err);
    },
    data: function(chunk) {
      return self.onData(chunk);
    },
    end: function() {
      return self.onEnd();
    }
  };
 
  this.socket.on('error', this.listeners.error);
  this.socket.on('data', this.listeners.data);
  this.socket.on('end', this.listeners.end);
}
util.inherits(Hose, EventEmitter);
module.exports = Hose;
 
Hose.create = function create(socket, options, filter) {
  return new Hose(socket, options, filter);
};
 
Hose.prototype.detach = function detach() {
  // Stop the flow
  this.socket.pause();
 
  this.socket.removeListener('error', this.listeners.error);
  this.socket.removeListener('data', this.listeners.data);
  this.socket.removeListener('end', this.listeners.end);
};
 
Hose.prototype.reemit = function reemit() {
  var buffer = this.buffer;
  this.buffer = null;
 
  // Modern age
  if (this.socket.unshift) {
    this.socket.unshift(buffer);
    if (this.socket.listeners('data').length > 0)
      this.socket.resume();
    return;
  }
 
  // Rusty node v0.8
  if (this.socket.ondata)
    this.socket.ondata(buffer, 0, buffer.length);
  this.socket.emit('data', buffer);
  this.socket.resume();
};
 
Hose.prototype.onError = function onError(err) {
  this.detach();
  this.emit('error', err);
};
 
Hose.prototype.onData = function onData(chunk) {
  if (this.buffer)
    this.buffer = Buffer.concat([ this.buffer, chunk ]);
  else
    this.buffer = chunk;
 
  var self = this;
  this.filter(this.buffer, function(err, protocol) {
    if (err)
      return self.onError(err);
 
    // No protocol selected yet
    if (!protocol)
      return;
 
    self.detach();
    self.emit('select', protocol, self.socket);
    self.reemit();
  });
};
 
Hose.prototype.onEnd = function onEnd() {
  this.detach();
  this.emit('error', new Error('Not enough data to recognize protocol'));
};