This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-12-27
Channels
- # announcements (2)
- # babashka (24)
- # beginners (116)
- # cider (7)
- # cljsrn (6)
- # clojure (38)
- # clojure-bay-area (4)
- # clojure-europe (3)
- # clojure-losangeles (1)
- # clojure-norway (10)
- # clojurescript (171)
- # datomic (16)
- # honeysql (3)
- # improve-getting-started (1)
- # introduce-yourself (2)
- # java (12)
- # malli (5)
- # membrane (2)
- # pedestal (3)
- # shadow-cljs (79)
- # spacemacs (6)
- # xtdb (10)
Working on clientside browser cljs playback of click events. Every time someone "votes' on a thing, I have a musical note surf up the page. It is easy to spawn them because I add them to an atom and watch the atom for changes, but when someone loads the page it shows them all at once marching across like a gigantic company front. I would like to have them "play back" in a sequential and not-simultaneous fashion when people visit the page a'fresh.
(This is not a question, but a report on the via dolorosa I am undergoing while trying to start using Clojure - with the hope that documentation and maybe software will be improve to make it easier for later newbies to start using Clojure.) While I am an experienced Linux user, I am newbie to Clojure and so I ran into difficulties when trying to use it. I'm reporting here about them. I use Linux Debian 11 (Debian Bullseye, currently oldstable). I wanted to install the Debian way and installed the 'clojure' package (which is version 1.10.2-1 in Debian Bullseye). The 'clojure -r' command worked for me. I prefer to use Emacs and so installed the Clojure/CIDER related packages. Then I found that when I try to start a Clojure REPL from cider-mode, I get the error message: "Execution error (FileNotFoundException) at http://java.io.FileInputStream/open0 (FileInputStream.java:-2).\n-Sdeps (No such file or directory)\n" This slack has some other people report similar bugs but I saw no relevant suggestions to resolve the bug. I looked in http://bugs.debian.org for bugs in the Clojure package, and it turns out that this package is not well maintained. The recommended way to install is via the non-apt based Linux script installer.
-Sdeps (No such file or directory)
could indicate a quote escaping issue.
I'd make sure that:
• The clojure
CLI tool is the latest (not from debian)
• CIDER (the Emacs package) is the latest stable release (https://github.com/clojure-emacs/cider/releases)
If it persists, feel free to create an issue in the cider tracker
Unrelated, but we have a debian-specific step that can avoid a frequent pitfall: https://docs.cider.mx/cider/troubleshooting.html#navigation-to-jdk-sources-doesnt-work
I installed Clojure in my Debian Bullseye (Debian 11, oldstable) according to the recommended way of using Linux script.
When trying to run 'clj' and 'clojure', I got the error message:
"Error building classpath. Failed to read artifact descriptor for org.clojure:clojure:1.11.1" (had to add underscores to turn off Slack's iconization).
According to error reports from other people, it seems that they experience this error when their machines have a network connection problem, which does not seem to be my case.
What do I need to check?
UPDATE:
In my case, the problem turned out to be a missing certificate for http://repo1.maven.org.
1. Using your browser, browse to https://repo1.maven.org
2. Click on the Lock button in the URL box and view the domain's certificate.
3. Look for the "export" button and save the exported certificate in a file, let's call it repo1-maven.cacert
4. Import the certificate into the keystore used by Java. It is the file $JAVA_HOME/lib/security/cacerts
(assuming that your $JAVA_HOME is set correctly). In Debian, this file is a symbolic link to /etc/ssl/certs/java/cacerts
.
The command for importing is: sudo keytool -keystore $JAVA_HOME/lib/security/cacerts -import -file repo1-maven.cacert
It should be trying to retrieve this file https://repo1.maven.org/maven2/org/clojure/clojure/1.11.1/clojure-1.11.1.pom - can you access it? If so, can you write to ~/.m2/repository? Might also be good to check java -version
(should be at least Java 1.8)
$ ls -al ~/.m2/repository/org/clojure/clojure/1.11.1/ total 8 drwxr-xr-x 2 omer omer 4096 Dec 27 10:10 . drwxr-xr-x 3 omer omer 4096 Dec 27 09:09 .. $ java --version openjdk 17.0.9 2023-10-17 OpenJDK Runtime Environment (build 17.0.9+9-Debian-1deb11u1) OpenJDK 64-Bit Server VM (build 17.0.9+9-Debian-1deb11u1, mixed mode, sharing)
Seems that I do not have <https:clojure-1.11.1.pom|clojure-1.11.1.pom> How to retrieve it?
I manually retrieved the above .pom file and placed it in ~/.m2/repository/org/clojure/clojure/1.11.1/ Now clj fails to read org.clojure:spec.alpha:jar:0.3.218 (I added underscores). Seems that I have a problem getting all *.pom files that clj needs. What to check now?
Downloading individual dependencies will add many hours (week) of work to use Clojure (and most other programming languages My assumption is that some certificates are outdated in the older version of Debian being used. The current stable version is Debian 12 (bookworm). Suggest updating to Debian 12 to see if these network challenges are resolved Bookworm is working correctly for me (and also by others in the community). Or it could be a network, firewall or proxy issue, but seems like an OS issue rather than Clojure or Java install.
Agreed, I think something like that is a good guess, although the carts should be part of the Java install and this seems new enough
There is a separate ca-certificates-java package which is installed as a dependency of openjdk packages. It could be the certs in were not updated sufficiently as bullseye is no longer the current stable version. It could be a matter of reinstalling the ca-certificates-java (or dpkg-reconfigure) Otherwise it may be simpler in the long run to update the Debian distribution to the current stable version and benefit from all the recent security updates
Furthermore, in case of a problem retrieving something, Maven annoyingly saves a placeholder reminding it "this doesn't work" and it won't really try again until you do something extraordinary. This can delay your discovery that you have fixed the root-cause problem. (The placeholders are files in your ~/.m2/repository
directory tree.)
by default it will re-try once a day. deps.edn does support settings to override that to always if needed
What would qualify as "something extraordinary" in this context? Deletion of the entire ~/.m2/repository tree?
you can do a more targeted deletion of course - ~/.m2/repository/org/clojure/clojure/1.11.1 for example
Am I write in saying "In Clojure, when a thread throws an exception, the thread that calls it will not crash, even without handling the exception"? That's what I gathered from this experiment
(comment (let [f (fn [] (throw (ex-info "Fake exception" {})))]
(thread-call f)
(+ 1 2 3)) => 6)
Exceptions as phenomena are local to a thread, they unwind the stack on the thread where they are thrown
Also, I used the https://github.com/reborg/parallel?tab=readme-ov-file#ppmap library to run through a list of operations with 100 concurrent threads. When an operation threw an exception, the main thread also threw. That's the origin of why I was confused about this
thread-call doesn't get used much, future is used much more often, so I don't recall the implementation of thread-call, but I believe it actually uses a thread pool under the covers, so the thread may hang around for a bit waiting to see if there is any more work to pick up before getting stopped
If you are trying to use real threads with core.async, use clojure.core.async/thread
It is like go, but uses a real thread so it is fine for blocking operations (and you have to use the blocking channel ops)
I must be doing something wrong but I have spent an hour on this and I am out of ideas: I have a service calling an API endpoint that started throwing 403/forbidden errors. I assumed it hit some kind of rate limit so I paused it for 10, then 20 minutes. I also swapped out tokens, but nothing helped. Before reaching out to the API owners to see if everything is OK, I ran through the Jupyter Lab that they give out as documentation to make sure it's also broken there, as that's what they'd have me do to verify. Using the same token, same query, it works perfectly well in Jupyter Lab/python 3.11 with the requests library. I hard-coded all the parameters (queries, headers, address, etc.) and copied over to the Clojure repl. Using clj-http, I get 403. Using Python, I get 200. I repeated this several times and made sure I hard-copied all the right parameters and that both requests use the exact same parameters. Not sure how this could be possible.
How sure are you that the query messages are indeed equal (encodings, new line handline, escaping special characters, etc.)? You may want to sniff the communications using Wireshark and compare the outputs of your scripts.
100% sure, I simplified it to just a couple of strings so there are no lines, no special characters, etc. It's also a brand new thing, script has been running on a droplet for about 8 hours now without issues and suddenly 403s. Local REPL running queries that previously worked also 403s.
Some wild guesses: 1. Software upgrades done in the background degraded certificates. 2. A certificate has just expired and needs to be renewed.
Haven't been able to see anything useful with Wireshark but I am not exactly a power user
Here's the two requests:
(client/get url {:query-params {"p" "001:1"
"format" "files"
}
:headers {"Authorization" auth}
}
)
r = requests.get(url,
params= {"p": "001:1", "format": "files"},
headers={"Authorization": auth})
where url and auth are the same stringIs it possible for there to be some kind of fingerprinting based on the HTTP client?
The python code doesn't have valid quoting of strings, so hard to tell if it matches
Probably worth dumping the raw requests ala curl -v in both cases and compare closely. A common mistake is to forget "Bearer " before the token auth, but that's usually responded with 401, not 403.
Given that the python code isn't valid, it seems like you aren't just copying and pasting the code you are actually running, which means you may be correcting whatever the error is when transcribing the code to share here
If you have been running full tilt for 8 hours it is entirely possible they decided your traffic from that ip was abusive
I checked with them for limits before I ran it, so it should be good. However, the clojure code also failed on my local machine. I ran the curl on both machines and got 200. Yea, this Python wasn't copy-pasted as I had to omit the tokens for slack, but it was in my testing
@U04CRS4B49L is there a way to get the raw request from clj-http?
The python code just isn't syntactically valid, so it is hard to tell if the query parameter names are the same
It's an HTML page saying I don't have access:
"\n<html><head>\n<title>403 Forbidden</title>\n</head><body>\n<h1>Forbidden</h1>\n<p>You don't have permission to access this resource.</p>\n</body></html>\n",
And what do you get if you make the http request from python without the auth token?
Soemthing different:
b'{"error": "User guest is not authorized to perform runapi with parameters operation=read,endpoint=search"}'
I have my local operation spit out the auth token when it's making the request just to make sure
Good question — not sure what's the easiest way to inspect the request in clj-http top of my head.
It will just return something the repl will print or throw an exception which the repl will print if you type in *e
clj-http throws exceptions on not successful http codes by default but there is an option you can pass in to not do that
The same HTML page as before, meaning that the API probably didn't do an auth check against my token at all
That wouldn't explain why a jar that's been working for hours suddenly started throwing that error though
The fact that the clojure side and python side get different responses when not authed
I get the same HTML response when I put in this:
(client/get url {:query-params {"p" "001:1"
"format" "files"
}
:headers {"Authorization" "Token abc"}
})
Probably the easiest way is not in clj-http itself, but you can just capture the request by swapping the URL for a dummy HTTP server running locally, that will just print it. SimpleHttpServer in Python, for example
Does the http server you are making requests give off the vibe of being home grown? I am extremely hesitant to suggest it, but maybe try a lower case A for the authorization header 😔
127.0.0.1 - - [27/Dec/2023 15:34:09] "GET / HTTP/1.1" 200 -
ERROR:root:Host: 127.0.0.1:8000
User-Agent: curl/8.1.2
Accept: */*
Authorization: Token <my-token>
127.0.0.1 - - [27/Dec/2023 15:34:35] "GET /?p=001:1&format=files HTTP/1.1" 200 -
ERROR:root:Connection: close
Authorization: Token <my-token>
accept-encoding: gzip, deflate
Host: 127.0.0.1:8000
User-Agent: Apache-HttpClient/4.5.13 (Java/21.0.1)
127.0.0.1 - - [27/Dec/2023 15:34:46] "GET /?p=001%3A1&format=files HTTP/1.1" 200 -
ERROR:root:Host: 127.0.0.1:8000
User-Agent: python-requests/2.31.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Authorization: Token <my-token>
This is the result of Alex's suggestion to use Simple HTTPServer (TIL) to print out the headers
The only thing altered in the above was replacing the token with <my-token>
In order, they originate from:
1. Curl
2. Clojure
3. Python
Recall that 1 and 3 currently work on the actual prod server, and 2 doesn'tThe code running the server has been lifted without change from https://gist.github.com/phrawzty/62540f146ee5e74ea1ab
I would expect that passing them in the correct slot :query-params
would automatically encode them, no?
Also, still unsure about how it very suddenly stopped working? Is it possible that a server update made that happen?
Because it can't be the client library, I got an uberjar that's been working for hours and then stopped working
Here's the code that calls the local simple HTTP server:
(client/get local-url {:query-params {"p" "001:1"
"format" "files"
}
:headers {"Authorization" auth}
})
If you are just going be the difference in the python request above and the clojure one that is it
So the python code and the clojure code(likely just calling some java code) may just have differing interpretations of that
I encoded the query string to look like this in the simple server:
127.0.0.1 - - [27/Dec/2023 15:47:36] "GET /?p=001%3A1&format=files HTTP/1.1" 200 -
ERROR:root:Connection: close
Authorization: Token <my-token>
accept-encoding: gzip, deflate
Host: 127.0.0.1:8000
User-Agent: Apache-HttpClient/4.5.13 (Java/21.0.1)
Against the prod server, it gives the same 403 error with an HTML pageWild theory, is it possible for someone to read "user agent" as an identifier of the user and to gate them from an API based on that header, maybe thinking it's a bot
Is there a universe where that makes sense, or is it always a mistake on the part of the person that would do that
thanks both, I learned a lot. Is there a universe where a server deciding to block someone on this header makes sense? To me it appears nonsensical because of how easy it is to bypass?
I have a love/hate relationship with spending a long time debugging something purely under the illusion of there's no way the right solution is this dumb...
It is more annoying that they would block the default for the java apache client and not for the python lib
I literally asked them for a rate limit and they said they didn't think there was one, although they seem to think that a rate limit was a defect in the way they talked about it 🤷 I have a lot of work I need from their system but I was happy to do it on their terms, but if there are no terms (and the only thing I got from my initial testing was a 5 minute gateway timeout) I wanted to do it as fast as possible
It is very easy to bypass, but some people consider it good etiquette to set a custom user agent if you are going to be making lots of requests
Does this mean all Apache clients on this version are blocked unless they manually set their user agents?
That's good to know, although I did notify them in advance that I will want to and asked if there's anything I should do
Depending on the structure of the team the blocking behavior may even belong to someone else
If you are taking to the devs of code serving whatever, it could be an ops team running a proxy in the middle somewhere that is blocking traffic they think is suspicious
I get web crawlers because they're protecting a public resource, but this is auth-protected. Why not disable the token?
Good question — not sure what's the easiest way to inspect the request in clj-http top of my head.
am I implementing a code smell, or is there a better way? I've got an app using clojure in the backend and clojurescript in the front. In the backend, my data is using fully qualified keywords like :pigeon-scoops.components.grocery-manager/type
. This is fine in the backend where ::type
or ::gm/type
can be used, but in the front end, it seems like it needs to be typed out completely.
Should I not be using fully qualified names on data that gets sent out of the backend or is there a better mechanism for using these keywords?
You can use a shorter prefix such as grocery-manager
, instead of the full namespace. So both in the back end and the front end you would be typing eg. :grocery-manager/type
.
But I have to ask, what is at about the front end that makes it impossible to use ::type
or ::gm/type
?
it could be a lack of knowledge. ::gm/type
works in backend code where I'm requiring [pigeon-scoops.components.grocery-manager :as gm]
at the top. I don't think I can import the namespace from clj in cljs?
There is an :as-alias
option to the require
that should help you out 🙂 Example
(ns front-end-ns
(:require [pigeon-scoops.components.grocery-manager :as-alias gm]))
::gm/type ;; => :pigeon-scoops.components.grocery-manager/type
Learn something new every day
I think using keyword namespaces that are the same as code namespaces is not very good
Keywords become part of the definition of data that is passed between programs and can be stored externally out living the process that creates it
hmm that's a good point
Defining specs in a .cljc
(Clojure common) file would seem a useful approach when using Clojure and ClojureScript in the same project.