This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-04-05
Channels
- # announcements (7)
- # beginners (10)
- # calva (14)
- # clj-otel (8)
- # clojure (42)
- # clojure-europe (20)
- # clojure-nl (1)
- # clojure-norway (22)
- # clojure-spec (8)
- # clojure-uk (7)
- # core-async (10)
- # cursive (1)
- # events (1)
- # hyperfiddle (20)
- # introduce-yourself (1)
- # jobs-discuss (11)
- # lsp (48)
- # missionary (3)
- # music (1)
- # off-topic (7)
- # overtone (9)
- # pedestal (21)
- # rdf (1)
- # releases (3)
- # shadow-cljs (22)
- # specter (13)
- # squint (1)
- # yamlscript (3)
Hey all! I've been using Clojure with Apache Daemon at work for some time now. I've had an issue for some time that I've been using a slightly janky workaround for, and in a rare quiet period in the office I thought I'd try to fix it properly... Cue me breaking everything I touch. Details to follow in the thread, but could someone take a look and let me know what I'm missing?
The Daemon interface specifies four methods (init, start, stop, destroy). According to https://commons.apache.org/proper/commons-daemon/apidocs/org/apache/commons/daemon/Daemon.html, init takes one arg which is a DaemonContext, and the other three take no args. Using the interface "properly" with this entry in my gen-class:
(:implements org.apache.commons.daemon.Daemon)
Results in four functions matching that definition. However, applications compiled this way fail to start. The docs for Procrun both say the start and stop functions must take (String args[]) as their argument. This leaves me with the decidedly less elegant:
(:methods [^:static [start ["[Ljava.lang.String;"] void]
^:static [stop ["[Ljava.lang.String;"] void]
^:static [init ["[Ljava.lang.String;" org.apache.commons.daemon.DaemonContext] void]
^:static [destroy ["[Ljava.lang.String;"] void]])
Which I don't at all like the look of, especially since a) Apache Daemon doesn't seem to respect its own Interface and b) IntelliJ gives me >probably meaningless but still annoying< warnings that start, stop, init, and void cannot be resolved, since their definitions are actually below the ns declaration.
Have I misunderstood how Daemon is supposed to work? Is there a fundamental bug in what I assume is a very widely-used tool? Is it not actually that widely-used and should I be using something better? Help will be greatly appreciated :)> Apache Daemon doesn't seem to respect its own Interface Are you sure the things are related? Where is the documentation for Procrun? > IntelliJ gives me >probably meaningless but still annoying< warnings The cause depends on what you use in IDEA. Should probably be brought up to the attention of the maintainers of the Clojure plugin that you're using.
The Daemon interface requires init, destroy, start, and stop; init takes a DaemonContext, but the other three take no args. Procrun however appears to only utilise start and stop, both of which take a string array. Following your question, I removed commons-daemon as a dependency and solely defined -start and -stop, and my service still worked with Procrun as expected. It seems that although Procrun is a part of Apache Daemon, it’s the only part I need to use to build a service - so I feel like I’ve misunderstood what commons-daemon actually does. Manages daemons within a running Java application maybe? Procrun (docs https://commons.apache.org/proper/commons-daemon/procrun.html) is the executable that controls the services - in my case providing an interface to Windows Service Manager. Regarding the errors, I use Cursive. Thanks for your advice - I’ve reported this to the maintainer. Is there a different/better way to export the necessary static functions, or is gen-class methods the only way?
> Is there a different/better way to export the necessary static functions, or is gen-class methods the only way? If you're willing to replace Clojure AOT compilation with Java compilation, just just add it as a separate step, you can create a regular Java class that will be used by Procrun and that calls your Clojure implementation.
Anyone know of a good resource for "patterns" for reduce? E.g., I just had to process a sequence of maps, and make an aggregate sum of one of their keys partitioned by a different key. My solution is fine, but I am sure there is a more elegant way, and it'd be cool if there were a toolbox I could take a look at to see what other people have done in similar situations. Something in clojure would be most preferable, but really any language would work.
is this a job for #C0FVDQLQ5 or #CFFTD7R6Z?
https://github.com/cgrand/xforms has a bunch of utilities that you can use with transduce that should help.
There's also https://github.com/henrygarner/redux
You can also use datasets https://github.com/scicloj/tablecloth
I'm seeing in an e2e test suite Exception: java.net.ProtocolException: Invalid status line: "0"
(or a different wording of the same problem if I try swapping the http client lib).
By any chance it rings a bell why would a clj http server return :status 0
?
We don't have dynamic :status building - it's always some or other fixed number.
I tried "-
which while showed some response http headers, those appeared to be well-formed
If I had to guess, it's because we use an async server. But of course async !== buggy. It generally works.
Does this sound in any way relevant? https://stackoverflow.com/a/26451773/564509
Maybe :) Debugging this client-side sounds prohibitively hard so a good bet seems to just add logging / asserts in the server-side middleware stack, Or tentatively change the server to be async->sync (which unfortunately isn't a small change)
One bell this rings is CORS. I believe that can cause an HTTP status of 0. It's really the browser rejecting the server based on that.
I was reading: "why would a clj http server return :status 0" as it didn't really return anything. The browser just blocked it.
Also, since it's in e2e tests and the OP mentions swapping the HTTP client lib, I would assume that a browser isn't involved at all.
I'm nailing the issue down to http-kit async. if I swap the whole server to run in sync mode, it works (as opposed to hanging and responding with literally 0
and nothing else. i.e. invalid http response)
Most likely, something is badly setup in the project. I'll try swapping httpkit (server) with some other async server, to see if anything changes
> Debugging this client-side sounds prohibitively hard
Why though? Should be rather trivial with a stepping debugger.
I haven't used it myself, but sound like with flowstorm you would even be able to basically ask it something like "where did :status 0
appear first?". And then, when it most likely points to some Java call, you'd be able to study its (potentially decompiled) source code and see what could possibly be responsible for that 0.
oh IDK, it sounded like I'd have to debug the Java http internals. There was no Clojure-level :status 0 anywhere in the stack - only the non-http-compliant 0
that the server decided to respond with
Oh, so 0 actually makes its way through the network? Or do you see 0 on the client side and assume that that's what the server returned? Or do you see 0 somewhere on the server side?
So debugging a client is probably meaningless then since you were able to get the same behavior via cUrl. If you're curious enough, you might double-check with WireShark.
according to curl -v it was literally just 0
i.e. lacking even HTTP/*
so I was forced to use -v --http0.9
just to get a response
... in case you or another curious mind appreciates it, probably one can get async http-kit (2.7.0) to produce such a malformed reponse given a nil
input. I don't have enough time in my hands to craft the perfect issue report upstream
What do you mean by "async"? The :ring-async?
parameter to run-server
has appeared only in 2.8.0.
hmm, here the plot thickens, while we use httpkit, it's through a thin, very hacky wrapper. At this point sadly I cannot provide more details as it's proprietary material.
Tried a simple server on 2.7.0 - can't reproduce. Running cUrls in three instances of while true
.
Found code like this in tests, also can't reproduce:
(def server
(hk-server/run-server
(fn [req]
(hk-server/as-channel
req
{:on-open #(hk-server/send!
% {:status 200 :body nil})}))
{:port 8888}))
if there is a way to reproduce I can give it a try. I would instrument all http-kit with flowstorm and get as deeper as I can and then if necessary get further with the intellij stepper into the java code
@U45T93RA6 Can't reproduce it either. But using nil
as a response map is against Ring spec anyway. I didn't realize it before, but not including :headers
is apparently also against the spec, oops.