@@ -69,6 +69,43 @@ def update_progress(progress):
69
69
sys .stderr .write ('.' )
70
70
sys .stderr .flush ()
71
71
72
+ def rle (data ):
73
+ "RLE compress the input bitstream and return it as a list"
74
+ buf = [0 ] * 128
75
+ ret = []
76
+ src = 0
77
+ runlen = 0
78
+ repeat = False
79
+ while src < len (data ):
80
+ buf [runlen ] = data [src ]
81
+ runlen = runlen + 1
82
+ src = src + 1
83
+ if runlen < 2 :
84
+ continue
85
+ if repeat :
86
+ if buf [runlen - 1 ] != buf [runlen - 2 ]:
87
+ repeat = False
88
+ if (not repeat ) or (runlen == 128 ):
89
+ ret += [runlen - 1 ] + [buf [0 ]]
90
+ buf [0 ] = buf [runlen - 1 ]
91
+ runlen = 1
92
+ else :
93
+ if buf [runlen - 1 ] == buf [runlen - 2 ]:
94
+ repeat = True
95
+ if runlen > 2 :
96
+ ret += [128 + runlen - 2 ] + buf [0 :runlen - 2 ]
97
+ buf [0 ] = buf [runlen - 1 ]
98
+ buf [1 ] = buf [runlen - 1 ]
99
+ runlen = 2
100
+ continue
101
+ if runlen == 128 :
102
+ ret += [128 + runlen - 1 ] + buf [0 :runlen ]
103
+ runlen = 0
104
+ if runlen :
105
+ ret += [128 + runlen ] + buf [0 :runlen ]
106
+ return ret
107
+
108
+
72
109
def serve (remoteAddr , localAddr , remotePort , localPort , password , filename , command = FLASH ):
73
110
# Create a TCP/IP socket
74
111
sock = socket .socket (socket .AF_INET , socket .SOCK_STREAM )
@@ -89,12 +126,18 @@ def serve(remoteAddr, localAddr, remotePort, localPort, password, filename, comm
89
126
sys .stderr .flush ()
90
127
logging .info (file_check_msg )
91
128
92
- content_size = os .path .getsize (filename )
93
- f = open (filename ,'rb' )
94
- file_md5 = hashlib .md5 (f .read ()).hexdigest ()
95
- f .close ()
129
+ with open (filename , "rb" ) as f :
130
+ content = f .read ()
131
+ content_size = len (content )
132
+ content_rle = rle (content )
133
+ request_rle = len (content_rle ) < content_size
134
+ # request_rle = True
135
+ file_md5 = hashlib .md5 (content ).hexdigest ()
96
136
logging .info ('Upload size: %d' , content_size )
97
137
message = '%d %d %d %s\n ' % (command , localPort , content_size , file_md5 )
138
+ if request_rle :
139
+ # Add a request for compression, ignored on earlier ArduinoOTAs
140
+ message += 'COMPRESSRLE\n '
98
141
99
142
# Wait for a connection
100
143
logging .info ('Sending invitation to: %s' , remoteAddr )
@@ -103,42 +146,50 @@ def serve(remoteAddr, localAddr, remotePort, localPort, password, filename, comm
103
146
sent = sock2 .sendto (message .encode (), remote_address )
104
147
sock2 .settimeout (10 )
105
148
try :
106
- data = sock2 .recv (128 ).decode ()
149
+ data = sock2 .recv (256 ).decode ()
107
150
except :
108
151
logging .error ('No Answer' )
109
152
sock2 .close ()
110
153
return 1
111
- if (data != "OK" ):
112
- if (data .startswith ('AUTH' )):
113
- nonce = data .split ()[1 ]
114
- cnonce_text = '%s%u%s%s' % (filename , content_size , file_md5 , remoteAddr )
115
- cnonce = hashlib .md5 (cnonce_text .encode ()).hexdigest ()
116
- passmd5 = hashlib .md5 (password .encode ()).hexdigest ()
117
- result_text = '%s:%s:%s' % (passmd5 ,nonce , cnonce )
118
- result = hashlib .md5 (result_text .encode ()).hexdigest ()
119
- sys .stderr .write ('Authenticating...' )
120
- sys .stderr .flush ()
121
- message = '%d %s %s\n ' % (AUTH , cnonce , result )
122
- sock2 .sendto (message .encode (), remote_address )
123
- sock2 .settimeout (10 )
124
- try :
125
- data = sock2 .recv (32 ).decode ()
126
- except :
127
- sys .stderr .write ('FAIL\n ' )
128
- logging .error ('No Answer to our Authentication' )
129
- sock2 .close ()
130
- return 1
131
- if (data != "OK" ):
132
- sys .stderr .write ('FAIL\n ' )
133
- logging .error ('%s' , data )
134
- sock2 .close ()
135
- sys .exit (1 );
136
- return 1
137
- sys .stderr .write ('OK\n ' )
154
+
155
+ if data == "COMPOK" :
156
+ compress = True
157
+ elif data == "OK" :
158
+ compress = False
159
+ elif data .startswith ('AUTH' ):
160
+ nonce = data .split ()[1 ]
161
+ cnonce_text = '%s%u%s%s' % (filename , content_size , file_md5 , remoteAddr )
162
+ cnonce = hashlib .md5 (cnonce_text .encode ()).hexdigest ()
163
+ passmd5 = hashlib .md5 (password .encode ()).hexdigest ()
164
+ result_text = '%s:%s:%s' % (passmd5 ,nonce , cnonce )
165
+ result = hashlib .md5 (result_text .encode ()).hexdigest ()
166
+ sys .stderr .write ('Authenticating...' )
167
+ sys .stderr .flush ()
168
+ message = '%d %s %s\n ' % (AUTH , cnonce , result )
169
+ sock2 .sendto (message .encode (), remote_address )
170
+ sock2 .settimeout (10 )
171
+ try :
172
+ data = sock2 .recv (32 ).decode ()
173
+ except :
174
+ sys .stderr .write ('FAIL\n ' )
175
+ logging .error ('No Answer to our Authentication' )
176
+ sock2 .close ()
177
+ return 1
178
+ if data == "OK" :
179
+ compress = False
180
+ elif data == "COMPOK" :
181
+ compress = True
138
182
else :
139
- logging .error ('Bad Answer: %s' , data )
183
+ sys .stderr .write ('FAIL\n ' )
184
+ logging .error ('%s' , data )
140
185
sock2 .close ()
186
+ sys .exit (1 );
141
187
return 1
188
+ sys .stderr .write ('OK\n ' )
189
+ else :
190
+ logging .error ('Bad Answer: %s' , data )
191
+ sock2 .close ()
192
+ return 1
142
193
sock2 .close ()
143
194
144
195
logging .info ('Waiting for device...' )
@@ -155,16 +206,17 @@ def serve(remoteAddr, localAddr, remotePort, localPort, password, filename, comm
155
206
received_ok = False
156
207
157
208
try :
158
- f = open (filename , "rb" )
159
209
if (PROGRESS ):
160
210
update_progress (0 )
161
211
else :
162
212
sys .stderr .write ('Uploading' )
163
213
sys .stderr .flush ()
164
214
offset = 0
215
+ chunk_size = 1460 # MTU-safe
165
216
while True :
166
- chunk = f .read (1460 )
167
- if not chunk : break
217
+ if offset >= content_size :
218
+ break
219
+ chunk = content [offset :min (content_size , offset + chunk_size )]
168
220
offset += len (chunk )
169
221
update_progress (offset / float (content_size ))
170
222
connection .settimeout (10 )
0 commit comments