edicl / drakma

HTTP client written in Common Lisp
http://edicl.github.io/drakma/
249 stars 58 forks source link

Requests using ":verify :required" eats RAM when error is signalled #118

Closed cage2 closed 2 years ago

cage2 commented 2 years ago

Hi!

I am bit confused about what is happening but if i perform some requests using TLS to an host that presents an invalid certificate (in my case a self signed one), the system seems suffering from a memory leak or something and the ram used has a steady increase.

I paste here the code to reproduce the problem and a plot.

Honestly i do not know if the problem is in drakma or in cl+ssl (or in my code perhaps!).

Important note: I am using cl+ssl from the git repository, not from quicklisp because the latest version of the library fixed this problem:

https://github.com/cl-plus-ssl/cl-plus-ssl/issues/151

(ql:quickload "drakma")

(defparameter *uri* "https://localhost/")

;;sbcl --load "drakma-report.lisp" --eval "(main)"

(defun memory-used ()
  "Assuming only a process whom command line terminate with main)"
  (string-trim '(#\Newline)
               (with-output-to-string (stream)
                 (uiop:run-program "ps aux |  awk -- '/main)$/ {print $6}'"
                                   :output stream))))

(defun main ()
  (loop for i from 0 below 2000 do
    (ignore-errors
     ;; Condition CL+SSL::UNABLE-TO-MATCH-COMMON-NAME was signalled.
     (drakma:http-request *uri*
                          :want-stream nil
                          :verify      :required))
    (sb-ext:gc :full t)
    (format t "~a ~a~%" i (memory-used)))
  (uiop:quit))

Bye! C.

plot

cage2 commented 2 years ago

Hi!

After inspecting the source seems that, in file util.lisp the function make-ssl-stream could be the cause of the leak.

I noticed that the callback :close-callback is not called when the certificate verification process fails, so the TLS context in not freed (i.e. the function cl+ssl:ssl-ctx-free is not called).

If i set the keyword value :auto-free-p to t in this line:

https://github.com/edicl/drakma/blob/master/util.lisp#L337

(cl+ssl:with-global-context (ctx)

with:

(cl+ssl:with-global-context (ctx :auto-free-p t)

The leak seems gone but now i wonder, if the callback is not called, if the stream is properly closed (seems yes but i can not understand where, maybe in the cleanup form of the unwind-protect of the function http-request?).

Hope this helps! Bye! C.