12
12
NpipeSocket = type (None )
13
13
14
14
15
+ STDOUT = 1
16
+ STDERR = 2
17
+
18
+
15
19
class SocketError (Exception ):
16
20
pass
17
21
@@ -51,28 +55,43 @@ def read_exactly(socket, n):
51
55
return data
52
56
53
57
54
- def next_frame_size (socket ):
58
+ def next_frame_header (socket ):
55
59
"""
56
- Returns the size of the next frame of data waiting to be read from socket,
57
- according to the protocol defined here:
60
+ Returns the stream and size of the next frame of data waiting to be read
61
+ from socket, according to the protocol defined here:
58
62
59
- https://docs.docker.com/engine/reference/ api/docker_remote_api_v1 .24/#/ attach-to-a-container
63
+ https://docs.docker.com/engine/api/v1 .24/#attach-to-a-container
60
64
"""
61
65
try :
62
66
data = read_exactly (socket , 8 )
63
67
except SocketError :
64
- return - 1
68
+ return (- 1 , - 1 )
69
+
70
+ stream , actual = struct .unpack ('>BxxxL' , data )
71
+ return (stream , actual )
72
+
65
73
66
- _ , actual = struct .unpack ('>BxxxL' , data )
67
- return actual
74
+ def frames_iter (socket , tty ):
75
+ """
76
+ Return a generator of frames read from socket. A frame is a tuple where
77
+ the first item is the stream number and the second item is a chunk of data.
78
+
79
+ If the tty setting is enabled, the streams are multiplexed into the stdout
80
+ stream.
81
+ """
82
+ if tty :
83
+ return ((STDOUT , frame ) for frame in frames_iter_tty (socket ))
84
+ else :
85
+ return frames_iter_no_tty (socket )
68
86
69
87
70
- def frames_iter (socket ):
88
+ def frames_iter_no_tty (socket ):
71
89
"""
72
- Returns a generator of frames read from socket
90
+ Returns a generator of data read from the socket when the tty setting is
91
+ not enabled.
73
92
"""
74
93
while True :
75
- n = next_frame_size (socket )
94
+ ( stream , n ) = next_frame_header (socket )
76
95
if n < 0 :
77
96
break
78
97
while n > 0 :
@@ -84,17 +103,67 @@ def frames_iter(socket):
84
103
# We have reached EOF
85
104
return
86
105
n -= data_length
87
- yield result
106
+ yield ( stream , result )
88
107
89
108
90
- def socket_raw_iter (socket ):
109
+ def frames_iter_tty (socket ):
91
110
"""
92
- Returns a generator of data read from the socket.
93
- This is used for non-multiplexed streams .
111
+ Return a generator of data read from the socket when the tty setting is
112
+ enabled .
94
113
"""
95
114
while True :
96
115
result = read (socket )
97
116
if len (result ) == 0 :
98
117
# We have reached EOF
99
118
return
100
119
yield result
120
+
121
+
122
+ def consume_socket_output (frames , demux = False ):
123
+ """
124
+ Iterate through frames read from the socket and return the result.
125
+
126
+ Args:
127
+
128
+ demux (bool):
129
+ If False, stdout and stderr are multiplexed, and the result is the
130
+ concatenation of all the frames. If True, the streams are
131
+ demultiplexed, and the result is a 2-tuple where each item is the
132
+ concatenation of frames belonging to the same stream.
133
+ """
134
+ if demux is False :
135
+ # If the streams are multiplexed, the generator returns strings, that
136
+ # we just need to concatenate.
137
+ return six .binary_type ().join (frames )
138
+
139
+ # If the streams are demultiplexed, the generator yields tuples
140
+ # (stdout, stderr)
141
+ out = [None , None ]
142
+ for frame in frames :
143
+ # It is guaranteed that for each frame, one and only one stream
144
+ # is not None.
145
+ assert frame != (None , None )
146
+ if frame [0 ] is not None :
147
+ if out [0 ] is None :
148
+ out [0 ] = frame [0 ]
149
+ else :
150
+ out [0 ] += frame [0 ]
151
+ else :
152
+ if out [1 ] is None :
153
+ out [1 ] = frame [1 ]
154
+ else :
155
+ out [1 ] += frame [1 ]
156
+ return tuple (out )
157
+
158
+
159
+ def demux_adaptor (stream_id , data ):
160
+ """
161
+ Utility to demultiplex stdout and stderr when reading frames from the
162
+ socket.
163
+ """
164
+ if stream_id == STDOUT :
165
+ return (data , None )
166
+ elif stream_id == STDERR :
167
+ return (None , data )
168
+ else :
169
+ raise ValueError ('{0} is not a valid stream' .format (stream_id ))
0 commit comments