Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add route to fetch server information by IP and port #69

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

threehymns
Copy link

This PR adds an API route to fetch information about a particular server as opposed to fetching the whole list.

This will be useful for allowing server owners to add stats such as live player counters to their websites without having to load the whole server list.

@BuckarooBanzay
Copy link
Contributor

sounds interesting, have you considered enabling CORS too (not sure if that's already available)
that will enable people to use the api on the frontend/browser too

@sfan5
Copy link
Contributor

sfan5 commented Dec 12, 2024

I am inclined to reject this in the spirit of simplicity.

list.json is about 200KB compressed, easily cacheable by the server and contains all this data (and more, if you e.g. want to show multiple servers).
If the website is running on the same machine as the server you can also have the server export its data to a file. Even more resource and privacy-friendly :)

have you considered enabling CORS too

The production deployment already has CORS enabled via my nginx config.

@threehymns
Copy link
Author

I see what you mean, but for a site running on a static site generator, rebuilds don't usually happen until a new commit. On the site I was testing with, it takes several seconds after the page loads for the player count to load. 200kb is trivial for a desktop app, but is several times larger than the total file size I would shoot for on a website homepage.

Most API's have lots of ways to query the data as opposed to just dumping it all at once, so it wouldn't be unusual to have even more routes than this. The only thing I would be cautious with about with this implementation is that luanti-org/luanti#7400 (A custom URL protocol) has not decided on a schema for sure yet and it would be nice for the serverlist schema to match.

server.py Outdated Show resolved Hide resolved
server.py Outdated
@app.route("/server/<string:ip>/<int:port>")
def get_server(ip, port):
try:
# Validate IP address

Choose a reason for hiding this comment

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

Do we need this validation? Since it's just a lookup, you will just get a "server not found" if you pass an invalid address. (Furthermore, such validation may itself introduce DoS attack vectors: For example consider the string -> int conversion for arbitrarily large strings below. Arguably Python does limit that however.)

What I'm slightly more concerned about: This seems to not normalize IPv6 addresses; it seems to not allow omitting zeroes from IPv6 addresses (since then not part will be true below)?

This seems especially problematic as in the serverlist, there are IPv6 addresses with omitted zeroes (example: "Free For All Skywars" has 2a02:4780:28:71f7::1).

Copy link
Author

Choose a reason for hiding this comment

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

These are fair points. I thought about removing the IP validation, but only after I had committed. I can definitely still remove it considering the only advantage is slightly better error messages.

Copy link
Author

@threehymns threehymns Dec 12, 2024

Choose a reason for hiding this comment

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

0e7ba22 IP validation removed. Invalid addresses will now return "Server not found" with a status code of 404.

Copy link

@appgurueu appgurueu Dec 12, 2024

Choose a reason for hiding this comment

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

Okay, but that still leaves the point about normalization.

I feel like this should be relatively easy to do properly by using Python's built-in ipaddress library rather than manually juggling (generally not normalized) IP address strings everywhere.

(That would also give us validation back, essentially "for free".)

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure. How verbose and specific do we need error messages to be. Really it either finds a match or it doesn't. Wouldn't this be better implemented in the serverList.get() function itself?

Choose a reason for hiding this comment

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

I'm not concerned about the error messages. I think the serverlist should probably be using IP objects internally to begin with, and this PR should look up things in the server list by IP object equality.

Really it either finds a match or it doesn't.

The problem is that finding a match by string equality is bogus for IPv6 addresses since the same address can be written in different ways (you can not omit the zeroes, for example).

You could make the formatting part of the API but I don't like that.

Then again of course refactoring the serverlist to use IP address objects may be out of scope.

Copy link
Contributor

Choose a reason for hiding this comment

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

The ip field is really just filled with whatever "standard" represenstation Flask/Python or the C stdlib decide on for the client IP address.

@app.route("/server/<string:ip>/<int:port>")
def get_server(ip, port):
try:
server = serverList.get(ip, port)
Copy link
Contributor

Choose a reason for hiding this comment

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

there's another pitfall here: this matches on the "announce requester IP" and not "IP of the server domain name" (this info is not present) or "specified IP or hostname of server".

so /server/your-land.de/30000 would never return anything

and for dualstack servers it could happen that it randomly announces over v4 and not v6, e.g. you normally do /server/2001:db8::123/30000 except you could get unlucky and it returns nothing but /server/1.2.3.4/30000 would have been right

Copy link
Author

Choose a reason for hiding this comment

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

so /server/your-land.de/30000 would never return anything

Isn't that a serverList.get problem?

Copy link
Contributor

Choose a reason for hiding this comment

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

Sort of. It does what it's supposed to for serverlist-internal usage, but might not do what someone expects of the API you're intending.

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.

4 participants