Skip to content

Conversation

ruroru
Copy link

@ruroru ruroru commented Aug 2, 2025

No description provided.

Copy link
Member

@weavejester weavejester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, I've made some comments on things to fix.

@ruroru ruroru force-pushed the master branch 2 times, most recently from e1095ed to d227ba7 Compare August 2, 2025 17:22
@ruroru ruroru requested a review from weavejester August 2, 2025 17:27
@ruroru ruroru force-pushed the master branch 3 times, most recently from 6c80eef to aca114b Compare August 4, 2025 09:35
Comment on lines 121 to 131
(cond
(bytes? value)
(.addBinaryBody builder ^String key-name ^bytes value
^ContentType mimetype ^String filename)
(file? value)
(.addBinaryBody builder ^String key-name ^File value
^ContentType mimetype ^String filename)
(instance? InputStream value)
(.addBinaryBody builder ^String key-name ^InputStream value
^ContentType mimetype ^String filename)
:else builder)))
Copy link
Member

@weavejester weavejester Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking about this change, and I think we should go about this in a different way:

(set! *warn-on-reflection* false)

(defn add-binary-body
  [^MultipartEntityBuilder builder key value ^ContentType mimetype
   ^String filename]
  (.addBinaryBody builder (name key) value mimetype filename))

(set! *warn-on-reflection* true)

The reason for this is that a reflection lookup should, in theory, be faster than checking the type with cond, and the code is clearer and more concise.

Setting the *warn-on-reflection* var in this way should only affect the current namespace (since it's reset on load).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't checked, but I doubt reflection will be faster than a conditional. Also hiding a problem behind manual handling of warn-on-reflection looks like a rather dodgy solution. I would argue that even String check can be added to conditional check, which will explicitly show supported types without trying to obfuscate them behind reflection . example could be:

(string?  value)
(.addBinaryBody builder ^String key-name ^bytes (.getBytes ^String (:value param) ^Charset default-charset)
                          ^ContentType mimetype ^String filename)

Copy link
Member

@weavejester weavejester Aug 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ran some benchmarks, and you're correct. I was getting 80µs for the cond, and 94µs with reflection.

I also think you're correct about the string check. In which case, perhaps we factor it out to a function:

(defn- str->bytes ^bytes [^String s]
  (.getBytes s ^Charset default-charset))

(defn- add-binary-body
  [^MultipartEntityBuilder builder key value ^ContentType mimetype
   ^String filename]
  (let [k (name key)]
    (cond
      (string? value)
      (.addBinaryBody builder k (str->bytes value) mimetype filename)
      (bytes? value)
      (.addBinaryBody builder k ^bytes value mimetype filename)
      (file? value)
      (.addBinaryBody builder k ^File value mimetype filename)
      (instance? InputStream value)
      (.addBinaryBody builder k ^InputStream value mimetype filename)
      :else
      (throw (IllegalArgumentException.
              (str "Cannot encode a value of type " (type value)
                   "as a multipart body."))))))

(defn- add-multipart-part [^MultipartEntityBuilder builder k v]
  (let [param    (if (map? v) v {:value v})
        value    (if (map? v) (:value v) v)
        mimetype (ContentType/parse
                  (or (:content-type param)
                      (when (file? value)
                        (mime/ext-mime-type (.getName ^File value)))
                      (if (string? value)
                        "text/plain; charset=UTF-8"
                        "application/octet-stream")))
        filename (or (:filename param)
                     (when (file? value) (.getName ^File value)))]
    (add-binary-body builder (name k) value mimetype filename)))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the way it looks now. Would be a bit more happy if instead of throwing an exception, same builder would be returned, but apart from that I am happy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your reasoning for not throwing an exception?

@ruroru ruroru force-pushed the master branch 4 times, most recently from 549895d to 1a6ffee Compare August 5, 2025 18:29
Comment on lines 129 to 134
(or (:content-type param)
(when (file? value)
(mime/ext-mime-type (.getName ^File value)))
(if (string? (:value param))
"text/plain; charset=UTF-8"
"application/octet-stream")))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like these lines have been accidentally indented.

@weavejester weavejester merged commit 908ba71 into ring-clojure:master Aug 6, 2025
1 check passed
@weavejester
Copy link
Member

Looks good; merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants