Skip to content

Commit e2c55c1

Browse files
committed
Minor patch
1 parent 89ddc5a commit e2c55c1

3 files changed

Lines changed: 32 additions & 5 deletions

File tree

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.7.38"
23+
VERSION = "1.10.7.39"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

lib/request/connect.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,17 @@ class _(dict):
760760
warnMsg = "problem occurred during connection closing ('%s')" % getSafeExString(ex)
761761
logger.warning(warnMsg)
762762

763+
# Keep-alive: dispose the response explicitly. Its wrapped close() hands the socket
764+
# back to the pool when the body was fully drained, otherwise drops it (a size-capped
765+
# partial read must not be reused). This avoids leaning on GC to reclaim it (delayed on
766+
# non-refcounting runtimes like PyPy). Guarded by the handler's marker so the HTTP/2
767+
# reuse pool is left untouched.
768+
elif conn is not None and getattr(conn, "_keepaliveManaged", False):
769+
try:
770+
conn.close()
771+
except Exception:
772+
pass
773+
763774
except SqlmapConnectionException as ex:
764775
if conf.proxyList and not kb.threadException:
765776
warnMsg = "unable to connect to the target URL ('%s')" % getSafeExString(ex)

lib/request/keepalive.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,11 @@ def _instrument(self, response, key, conn, count, keep):
185185
a connection is never reused.
186186
"""
187187

188-
state = {"handled": False}
188+
# 'eof' is raised only on a genuine end-of-body (observed while reading), never
189+
# merely because close() nulled the file object; the socket is handed back to the
190+
# pool solely on that signal, so a half-read response can never be reused (which
191+
# would splice its leftover bytes onto the next request - response desynchronization)
192+
state = {"handled": False, "reading": False, "eof": False}
189193
_read = response.read
190194
_close = response.close
191195

@@ -200,7 +204,7 @@ def drained():
200204

201205
def settle():
202206
# Once (and only once) the body is fully drained, decide the socket's fate
203-
if state["handled"] or not drained():
207+
if state["handled"] or not state["eof"]:
204208
return
205209
state["handled"] = True
206210
if keep:
@@ -212,12 +216,23 @@ def settle():
212216
pass
213217

214218
def read(*args, **kwargs):
215-
data = _read(*args, **kwargs)
219+
state["reading"] = True
220+
try:
221+
data = _read(*args, **kwargs)
222+
finally:
223+
state["reading"] = False
224+
if drained():
225+
state["eof"] = True
216226
settle()
217227
return data
218228

219229
def close():
220-
# Note: on Python 2 httplib.read() calls close() itself upon EOF
230+
# Note: on Python 2 httplib.read() calls close() itself upon EOF, i.e. from inside
231+
# our read(); such an in-read close marks a real end-of-body. An external close()
232+
# on a not-yet-drained body means the caller abandoned it (e.g. sqlmap hitting a
233+
# size cap), so the socket must be dropped rather than returned to the pool.
234+
if state["reading"]:
235+
state["eof"] = True
221236
_close()
222237
settle()
223238
if not state["handled"]:
@@ -228,6 +243,7 @@ def close():
228243
except Exception:
229244
pass
230245

246+
response._keepaliveManaged = True
231247
response.read = read
232248
response.close = close
233249

0 commit comments

Comments
 (0)