The repository provides an API and implementation for the Nerves Project's Internet connectivity check server, whenwhere.nerves-project.org. The server provides a highly available endpoint that is inexpensive to maintain. Of course, this is an open source project and no uptime guarantees are being made. Luckily, the source is available here so that you can set one up yourself.
Why does this project exist? We were defeated by captive portals tricking devices that they had Internet when they didn't.
Implementations are provided for AWS CloudFront Functions and Cloudflare Workers. It's certainly possible to replicate the API elsewhere. Please send a PR or repository pointer if you'd like to share another way.
Features include:
- Multiple ways to avoid being tricked by overly aggressive HTTP caches
- Millisecond timestamp for setting clocks
- JSON and Erlang Term Format responses
- Optional device geo-location
- Optional public IP address and port used to make the request
If you're not using Elixir, run this:
$ curl https://whenwhere.nerves-project.org | jq .
{
"now": "2024-06-20T02:19:59.878Z",
"time_zone": "Australia/Brisbane",
"latitude": "-27.46790",
"longitude": "153.03250",
"country": "AU",
"country_region": "QLD",
"city": "Brisbane",
"address": "103.216.220.102:11591"
}In Elixir, here's an example using Req:
Mix.install([
{:req, "~> 0.5.0"}
])
Req.get!("http://whenwhere.nerves-project.org/").body
#=> %{
# "now" => "2024-05-29T00:43:08.383Z",
# "time_zone" => "Australia/Brisbane",
# "latitude" => "-27.46790",
# "longitude" => "153.03250",
# "country" => "AU",
# "city" => "Brisbane",
# "address" => "144.48.39.229:1403"
# }Here's a minimal :httpc example that requests an Erlang binary term instead:
Application.ensure_all_started(:inets)
{:ok, {_, _, body}} = :httpc.request(:get, {"http://whenwhere.nerves-project.org/", [{~c"content-type", "application/x-erlang-binary"}]}, [], [])
body |> IO.iodata_to_binary() |> :erlang.binary_to_term([:safe])
#=> %{
# "now" => "2024-05-29T00:43:08.383Z",
# "time_zone" => "Australia/Brisbane",
# "latitude" => "-27.46790",
# "longitude" => "153.03250",
# "country" => "AU",
# "city" => "Brisbane",
# "address" => "144.48.39.229:1403"
# }There's only one endpoint and the path to it isn't specified.
Only HTTP GET and HEAD methods are supported. Clients should make HEAD requests when they don't need any information in the body of the response.
One query parameter is supported:
nonce- an up to 31 character alphanumeric string. Non-alphanumeric or longer strings result in an error response.
Both HTTP GET and HEAD methods return the following headers:
x-now- ISO 8601 date/time string with millisecond precision in UTC (e.g.,2024-05-29T00:22:09.890Z)x-nonce- The value of thenoncequery parameter if specified
The content-type request header selects JSON-encoded (application/json) or
Erlang Term Format-encoded (application/x-erlang-binary) results. JSON is the
default. The contents will be a map with the following fields. Note that many
are optional.
"now"- ISO 8601 data/time string with millisecond precision in UTC"time_zone"- Optional geo-located time zone in IANA time zone database format"latitude"- Optional approximate latitude"longitude"- Optional approximate longitude"country"- Optional ISO 3166-1 alpha-2 country code"country_region"- Optional ISO 3166-1 alpha-2 country code"city"- Optional name of the nearest city"address"- Optional IP address and optional port that made the request
Consult the AWS CloudFront Function setup and use the provided
cloudfront/index.js.
For geo-location support, attach the AllViewerAndCloudFrontHeaders-2022-06
origin request policy.
The Cloudflare Worker implementation is in cloudflare/worker.js with
configuration in cloudflare/wrangler.toml.
-
Install Wrangler:
npm install -g wrangler
-
Authenticate with your Cloudflare account:
wrangler login
-
Edit
cloudflare/wrangler.tomlto set the worker name and optionally configure a custom domain or route. -
Deploy:
cd cloudflare wrangler deploy
Note: Unlike the CloudFront implementation, the Cloudflare Worker address
field contains only the client IP address without a port number.
This project comes with simple tests to verify that the server is responding correctly. Make sure Elixir is installed and then run:
./validate.exs [url]The default URL is http://whenwhere.nerves-project.org. Pass a URL to test a
different server, e.g., ./validate.exs http://localhost:8787.