Improve support for headerless freezing (docs, error msgs, etc.)

This commit is contained in:
Peter Taoussanis 2014-01-21 20:36:09 +07:00
parent e580fc6b2b
commit 5690a9611e

View file

@ -383,67 +383,83 @@
(defn- try-parse-header [ba] (defn- try-parse-header [ba]
(when-let [[head-ba data-ba] (utils/ba-split ba 4)] (when-let [[head-ba data-ba] (utils/ba-split ba 4)]
(let [[head-sig* [meta-id]] (utils/ba-split head-ba 3)] (let [[head-sig* [meta-id]] (utils/ba-split head-ba 3)]
(when (utils/ba= head-sig* head-sig) (when (utils/ba= head-sig* head-sig) ; Appears to be well-formed
[data-ba (head-meta meta-id {:unrecognized-header? true})])))) [data-ba (head-meta meta-id {:unrecognized-meta? true})]))))
(defn thaw (defn thaw
"Deserializes a frozen object from given byte array to its original Clojure "Deserializes a frozen object from given byte array to its original Clojure
data type. Supports data frozen with current and all previous versions of data type. By default[1] supports data frozen with current and all previous
Nippy. For custom types extend the Clojure reader or see `extend-thaw`." versions of Nippy. For custom types extend the Clojure reader or see
[^bytes ba & [{:keys [password compressor encryptor headerless-opts] `extend-thaw`.
:or {headerless-opts {:compressed? true}
compressor snappy-compressor [1] :headerless-meta provides a fallback facility for data frozen without a
encryptor aes128-encryptor} standard Nippy header (notably all Nippy v1 data). A default is provided for
Nippy v1 thaw compatibility, but it's recommended that you _disable_ this
fallback (`{:headerless-meta nil}`) if you're certain you won't be thawing
headerless data."
[^bytes ba & [{:keys [password compressor encryptor headerless-meta]
:or {compressor snappy-compressor
encryptor aes128-encryptor
headerless-meta ; Recommend set to nil when possible
{:version 1
:compressed? true
:encrypted? false}}
:as opts}]] :as opts}]]
(let [;; headerless-opts may be nil, or contain any head-meta keys: (let [headerless-meta (merge headerless-meta (:legacy-opts opts)) ; Deprecated
headerless-opts (merge headerless-opts (:legacy-opts opts)) ; Deprecated
ex (fn [msg & [e]] (throw (Exception. (str "Thaw failed: " msg) e))) ex (fn [msg & [e]] (throw (Exception. (str "Thaw failed: " msg) e)))
try-thaw-data try-thaw-data
(fn [data-ba {:keys [compressed? encrypted?] :as head-meta}] (fn [data-ba {:keys [compressed? encrypted?] :as _head-or-headerless-meta}]
(let [password (when encrypted? password) ; => also head-meta (let [password (when encrypted? password)
compressor (if head-meta compressor (when compressed? compressor)]
(when compressed? compressor)
(when (:compressed? headerless-opts) snappy-compressor))]
(try (try
(let [ba data-ba (let [ba data-ba
ba (if password (encryption/decrypt encryptor password ba) ba) ba (if password (encryption/decrypt encryptor password ba) ba)
ba (if compressor (compression/decompress compressor ba) ba) ba (if compressor (compression/decompress compressor ba) ba)
stream (DataInputStream. (ByteArrayInputStream. ba))] stream (DataInputStream. (ByteArrayInputStream. ba))]
(thaw-from-stream! stream)) (thaw-from-stream! stream))
(catch Exception e (catch Exception e
(cond (cond
password (ex "Wrong password/encryptor?" e) password (if head-meta (ex "Wrong password/encryptor?" e)
(ex "Unencrypted data?" e))
compressor (if head-meta (ex "Encrypted data or wrong compressor?" e) compressor (if head-meta (ex "Encrypted data or wrong compressor?" e)
(ex "Uncompressed data?" e)) (ex "Uncompressed data?" e))
:else (if head-meta (ex "Corrupt data?" e) :else (if head-meta (ex "Corrupt data?" e)
(ex "Compressed data?" e)))))))] (ex "Data may be unfrozen, corrupt, compressed &/or encrypted.")))))))]
(if-let [[data-ba {:keys [unrecognized-header? compressed? encrypted?] (if-let [[data-ba {:keys [unrecognized-meta? compressed? encrypted?]
:as head-meta}] (try-parse-header ba)] :as head-meta}] (try-parse-header ba)]
(cond ; Header _appears_ okay (cond ; A well-formed header _appears_ to be present
(and (not headerless-opts) unrecognized-header?) ; Conservative (and (not headerless-meta) ; Cautious. It's unlikely but possible the
(ex "Unrecognized header. Data frozen with newer Nippy version?") ; header sig match was a fluke and not an
; indication of a real, well-formed header.
; May really be headerless.
unrecognized-meta?)
(ex "Unrecognized (but apparently well-formed) header. Data frozen with newer Nippy version?")
;;; It's still possible below that the header match was a fluke, but it's
;;; _very_ unlikely. Therefore _not_ going to incl.
;;; `(not headerless-meta)` conditions below.
(and compressed? (not compressor)) (and compressed? (not compressor))
(ex "Compressed data. Try again with compressor.") (ex "Compressed data? Try again with compressor.")
(and encrypted? (not password)) (and encrypted? (not password))
(if (::tools-thaw? opts) ::need-password (if (::tools-thaw? opts) ::need-password
(ex "Encrypted data. Try again with password.")) (ex "Encrypted data? Try again with password."))
:else (try (try-thaw-data data-ba head-meta) :else (try (try-thaw-data data-ba head-meta)
(catch Exception e (catch Exception e
(if headerless-opts (if headerless-meta
(try (try-thaw-data ba nil) (try (try-thaw-data ba headerless-meta)
(catch Exception _ (catch Exception _
(throw e))) (throw e)))
(throw e))))) (throw e)))))
;; Header definitely not okay ;; Well-formed header definitely not present
(if headerless-opts (if headerless-meta
(try-thaw-data ba nil) (try-thaw-data ba headerless-meta)
(ex "Unfrozen or corrupt data?"))))) (ex "Data may be unfrozen, corrupt, compressed &/or encrypted.")))))
(comment (thaw (freeze "hello")) (comment (thaw (freeze "hello"))
(thaw (freeze "hello" {:compressor nil})) (thaw (freeze "hello" {:compressor nil}))
@ -624,4 +640,5 @@
[ba & {:keys [compressed?] [ba & {:keys [compressed?]
:or {compressed? true}}] :or {compressed? true}}]
(thaw ba {:headerless-opts {:compressed? compressed?} (thaw ba {:headerless-opts {:compressed? compressed?}
:compressor snappy-compressor
:password nil})) :password nil}))