Question

I am building an event collector in Clojure for Snowplow (using Ring/Compojure) and am having some trouble serving a transparent pixel with Ring. This is my code for sending the pixel:

(ns snowplow.clojure-collector.responses
  (:import (org.apache.commons.codec.binary Base64)
           (java.io ByteArrayInputStream)))

(def pixel-bytes (Base64/decodeBase64 (.getBytes "R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==")))
(def pixel (ByteArrayInputStream. pixel-bytes))

(defn send-pixel
   []
    {:status  200
     :headers {"Content-Type"   "image/gif"}
     :body    pixel})

When I start up my server, the first time I hit the path for send-pixel, the pixel is successfully delivered to my browser. But the second time - and every time afterwards - Ring sends no body (and content-length 0). Restart the server and it's the same pattern.

A few things it's not:

  1. I have replicated this using wget, to confirm the intermittent-ness isn't a browser caching issue
  2. I generated the "R01GOD..." base64 string at the command-line (cat original.gif | base64) so know there is no issue there
  3. When the pixel is successfully sent, I have verified its contents are correct (diff original.gif received-pixel.gif)

I'm new to Clojure - my guess is there's some embarrassing dynamic gremlin in my code, but I need help spotting it!

Was it helpful?

Solution

I figured out the problem in the REPL shortly after posting:

user=> (import (org.apache.commons.codec.binary Base64) (java.io ByteArrayInputStream))
java.io.ByteArrayInputStream
user=> (def pixel-bytes (Base64/decodeBase64 (.getBytes "R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==")))
#'user/pixel-bytes
user=> (def pixel (ByteArrayInputStream. pixel-bytes))
#'user/pixel
user=> (slurp pixel-bytes)
"GIF89a!�\n,L;"
user=> (slurp pixel-bytes)
"GIF89a!�\n,L;"
user=> (slurp pixel)
"GIF89a!�\n,L;"
user=> (slurp pixel)
""

So basically the problem was that the ByteArrayInputStream was getting emptied after the first call. Mutable data structures!

I fixed the bug by generating a new ByteArrayInputStream for each response, with:

    :body    (ByteArrayInputStream. pixel-bytes)}))

OTHER TIPS

The problem is your pixel variable holds a stream. Once it has been read, there is no possibility to re-read it again.

Moreover, you do not need to deal with encoding issues. Ring serves static files as well. Just return:

(file-response "/path/to/pixel.gif")

It handles non-existing files as well. See the docs also.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top