- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathWebhookServer.java
More file actions
Latest commit
67 lines (58 loc) · 2.73 KB
/
Copy pathWebhookServer.java
File metadata and controls
67 lines (58 loc) · 2.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/*
* A webhook receiver on the JDK's own HTTP server — no framework.
*
* export VISION_WEBHOOK_SECRET=whsec_… # dashboard → Webhooks
* java -cp target/visionapi-java-1.0.0.jar examples/WebhookServer.java
*
* The endpoint must be HTTPS and must not resolve to a private address, so a bare localhost
* receiver is unreachable by design — put a tunnel (ngrok, cloudflared) in front of it while
* developing.
*/
importcom.sun.net.httpserver.HttpServer;
importio.visionapi.Webhook;
importio.visionapi.WebhookSignatureException;
importjava.io.IOException;
importjava.net.InetSocketAddress;
publicclassWebhookServer {
publicstaticvoidmain(String[] args) throwsIOException {
Stringsecret = System.getenv("VISION_WEBHOOK_SECRET");
if (secret == null || secret.isBlank()) {
System.err.println("Set VISION_WEBHOOK_SECRET (dashboard → Webhooks).");
System.exit(1);
}
HttpServerserver = HttpServer.create(newInetSocketAddress(3000), 0);
server.createContext("/hooks/vision", exchange -> {
if (!"POST".equals(exchange.getRequestMethod())) {
exchange.sendResponseHeaders(405, -1);
exchange.close();
return;
}
// Read the raw bytes. The signature covers exactly what arrived — decoding first
// and re-encoding changes key order and whitespace, and the HMAC stops matching.
byte[] body = exchange.getRequestBody().readAllBytes();
Webhook.Eventevent;
try {
event = Webhook.verify(body, exchange.getRequestHeaders().getFirst("X-Vision-Signature"), secret);
} catch (WebhookSignatureExceptione) {
System.err.println("rejected delivery: " + e.getMessage());
exchange.sendResponseHeaders(400, -1);
exchange.close();
return;
}
// Any 2xx is success. Acknowledge immediately and do the work afterwards — a slow
// handler looks like a failed delivery and earns a retry at +1 m, +5 m, +15 m, +40 m.
exchange.sendResponseHeaders(202, -1);
exchange.close();
if (event.isFailure()) {
System.err.println("task " + event.taskId() + " failed: "
+ event.error().map(e -> e.get("code")).orElse("unknown"));
} else {
System.out.println("task " + event.taskId() + " completed — "
+ event.creditsUsed() + " credits");
System.out.println(event.result().unwrap(true));
}
});
server.start();
System.out.println("listening on http://localhost:3000/hooks/vision");
}
}