(maint) Define our own entropy in `open-port-num`.
The current implementation of `open-port-num` relies on the JDK
implementation to select a currently-open port. Anecdotally, some
implementations have low amounts of entropy for this operation. Change
the implementation to uniformly select a random port number in the
traditional ephemeral port range of 49152 through 65535, so we're sure
we're using the maximum available range regardless of JDK
implementation.
The anecdotal evidence for the entropy comes from the frequency of port
collisions seen in the classifier's integration tests. Up through
2016.4.x, we used a custom random port selection function with entropy
equivalent to this one (but which did *not* check to see if the selected
port was open), and we saw closed ports in the ~12 ports bound less than
once every fifteen runs. Starting in 2017.2.x, we switched to using
kitchensink's open-port-num, and we've observed the frequency of
collisions to go up dramatically: now we see them, on average, once
every other run.
This anecdotal evidence is backed up by observing that this new
implementation of `open-port-num` returns over twice as many distinct
port numbers as the previous one when invoked 60k times during the
tests: ~15.9k vs. ~7k. However, this is only testing my JDK
implementation; that spread may vary depending on the particular JDK
used.
Dan Lidral-Porter
6 years ago
1066 | 1066 | ~@(interleave (repeat g) (map pstep forms))] |
1067 | 1067 | ~g))) |
1068 | 1068 | |
1069 | (defn- port-open? | |
1070 | [port] | |
1071 | (try | |
1072 | (with-open [_ (java.net.ServerSocket. port)] | |
1073 | port) | |
1074 | (catch java.net.BindException e | |
1075 | (when-not (re-find #"already in use" (.getMessage e)) | |
1076 | (throw e))))) | |
1077 | ||
1069 | 1078 | (defn open-port-num |
1070 | "Returns a currently open port number" | |
1079 | "Returns a currently open port number in the traditional ephemeral port range | |
1080 | of 49152 through 65535." | |
1071 | 1081 | [] |
1072 | (with-open [s (java.net.ServerSocket. 0)] | |
1073 | (.getLocalPort s))) | |
1082 | (let [lo 49152 | |
1083 | hi 65536] ; one higher because the upper limit is exclusive | |
1084 | (if-let [open-port (some port-open? (shuffle (range lo hi)))] | |
1085 | open-port | |
1086 | (throw (java.net.BindException. | |
1087 | "All ephemeral ports are already in use (Bind failed)"))))) | |
1074 | 1088 | |
1075 | 1089 | (defmacro assoc-if-new |
1076 | 1090 | "Assocs the provided values with the corresponding keys if and only |