nonnative 2.11.0 → 2.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +2 -2
- data/Gemfile.lock +1 -1
- data/lib/nonnative/invalid_data_socket_pair.rb +27 -37
- data/lib/nonnative/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37bb994055052e4b4e901466b837ba3b74944340a32abf820cebe66dc8fd7f37
|
|
4
|
+
data.tar.gz: 5c99b070397381627c5543189f9cc107c089a1b886a3144423a083c77e13960a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f1f3a920b31c3d77b16b73e6c63bd8adc0f1b2029db821186879ea6fb25daff094970f9dccdea8285c23bbf074cf081626d51847b447255c910a79049dec6965
|
|
7
|
+
data.tar.gz: 93c9efe976dea298fbfd0e187b125991945e715d59fc55c05d7f692c38f90fa8e0e60fa2283cfdec7f89e580253824bae6139e37ddbcf519581ea25979c9e050
|
data/.circleci/config.yml
CHANGED
|
@@ -25,8 +25,8 @@ jobs:
|
|
|
25
25
|
- vendor
|
|
26
26
|
- run: make lint
|
|
27
27
|
- run: make sec
|
|
28
|
-
- run:
|
|
29
|
-
- run:
|
|
28
|
+
- run: make features
|
|
29
|
+
- run: make benchmarks
|
|
30
30
|
- store_test_results:
|
|
31
31
|
path: test/reports
|
|
32
32
|
- store_artifacts:
|
data/Gemfile.lock
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Nonnative
|
|
4
4
|
# Socket-pair variant used by the fault-injection proxy to simulate corrupted/incoherent traffic.
|
|
5
5
|
#
|
|
6
|
-
# When active,
|
|
7
|
-
# before
|
|
6
|
+
# When active, client requests still pass through unchanged, but responses flowing back from the
|
|
7
|
+
# upstream socket are corrupted before they reach the client.
|
|
8
8
|
#
|
|
9
9
|
# This behavior is enabled by calling {Nonnative::FaultInjectionProxy#invalid_data}.
|
|
10
10
|
#
|
|
@@ -12,16 +12,28 @@ module Nonnative
|
|
|
12
12
|
# @see Nonnative::SocketPairFactory
|
|
13
13
|
# @see Nonnative::SocketPair
|
|
14
14
|
class InvalidDataSocketPair < SocketPair
|
|
15
|
-
|
|
15
|
+
LINE_DELIMITERS = ["\r\n", "\n", "\r"].freeze
|
|
16
|
+
|
|
17
|
+
# Track the accepted client socket so we can corrupt only the response path.
|
|
18
|
+
def connect(local_socket)
|
|
19
|
+
@local_socket = local_socket
|
|
20
|
+
|
|
21
|
+
super
|
|
22
|
+
ensure
|
|
23
|
+
@local_socket = nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Writes corrupted data to the socket by mutating payload bytes.
|
|
16
27
|
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
28
|
+
# Client requests are forwarded unchanged so the upstream service can still parse them.
|
|
29
|
+
# Responses flowing back to the client are corrupted in-place, which keeps line-based clients
|
|
30
|
+
# from hanging while ensuring echoed data does not come back unchanged.
|
|
19
31
|
#
|
|
20
32
|
# @param socket [IO] the socket to write to
|
|
21
33
|
# @param data [String] the original payload
|
|
22
34
|
# @return [Integer] number of bytes written
|
|
23
35
|
def write(socket, data)
|
|
24
|
-
|
|
36
|
+
return super unless socket.equal?(@local_socket)
|
|
25
37
|
|
|
26
38
|
super(socket, corrupt(data))
|
|
27
39
|
end
|
|
@@ -29,40 +41,18 @@ module Nonnative
|
|
|
29
41
|
private
|
|
30
42
|
|
|
31
43
|
def corrupt(data)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"#{corrupt_payload(payload)}#{delimiter}"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def split_trailing_delimiter(data)
|
|
39
|
-
index = trailing_delimiter_start(data)
|
|
40
|
-
payload = data.byteslice(0, index)
|
|
41
|
-
delimiter = data.byteslice(index, data.bytesize - index) || ''
|
|
44
|
+
# Preserve a final line ending so line-oriented clients still finish their reads.
|
|
45
|
+
delimiter = LINE_DELIMITERS.find { |candidate| data.end_with?(candidate) } || ''
|
|
46
|
+
payload = data.delete_suffix(delimiter)
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def trailing_delimiter_start(data)
|
|
47
|
-
index = data.bytesize
|
|
48
|
-
|
|
49
|
-
while index.positive?
|
|
50
|
-
byte = data.getbyte(index - 1)
|
|
51
|
-
break unless [10, 13].include?(byte)
|
|
52
|
-
|
|
53
|
-
index -= 1
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
index
|
|
57
|
-
end
|
|
48
|
+
# A delimiter-only response still needs one byte we can corrupt without losing the terminator.
|
|
49
|
+
payload = "\0" if payload.empty?
|
|
58
50
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return corrupted.pack('C*') unless corrupted == bytes
|
|
51
|
+
# Flip the first byte so the payload is definitely wrong without mangling the whole
|
|
52
|
+
# response structure and turning an invalid response into a timeout.
|
|
53
|
+
payload.setbyte(0, payload.getbyte(0).zero? ? 1 : 0)
|
|
63
54
|
|
|
64
|
-
|
|
65
|
-
corrupted.pack('C*')
|
|
55
|
+
"#{payload}#{delimiter}"
|
|
66
56
|
end
|
|
67
57
|
end
|
|
68
58
|
end
|
data/lib/nonnative/version.rb
CHANGED