Skip to content

Commit e3c45af

Browse files
committed
add smee-client example
1 parent 5c9abfd commit e3c45af

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed

examples/smee.java

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
//usr/bin/env jbang "$0" "$@" ; exit $?
2+
//DEPS info.picocli:picocli:4.2.0
3+
//DEPS org.jboss.resteasy:resteasy-client:4.4.1.Final
4+
//DEPS com.fasterxml.jackson.core:jackson-databind:2.2.3
5+
6+
import com.fasterxml.jackson.databind.JsonNode;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import com.fasterxml.jackson.databind.node.ObjectNode;
9+
import org.apache.http.HttpRequest;
10+
import org.apache.http.client.methods.CloseableHttpResponse;
11+
import org.apache.http.client.methods.HttpPost;
12+
import org.apache.http.entity.StringEntity;
13+
import org.apache.http.impl.client.CloseableHttpClient;
14+
import org.apache.http.impl.client.HttpClientBuilder;
15+
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
16+
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine;
17+
import org.jboss.resteasy.spi.ResteasyConfiguration;
18+
import picocli.CommandLine;
19+
import picocli.CommandLine.Command;
20+
import picocli.CommandLine.Parameters;
21+
22+
import javax.ws.rs.HttpMethod;
23+
import javax.ws.rs.client.*;
24+
import javax.ws.rs.core.Configuration;
25+
import javax.ws.rs.core.Request;
26+
import javax.ws.rs.core.UriBuilder;
27+
import javax.ws.rs.sse.InboundSseEvent;
28+
import javax.ws.rs.sse.SseEventSource;
29+
import java.io.IOException;
30+
import java.net.*;
31+
import java.util.concurrent.Callable;
32+
import java.util.concurrent.TimeUnit;
33+
import java.util.logging.Logger;
34+
35+
import static picocli.CommandLine.*;
36+
37+
/**
38+
* A pure java implementation of smee-client.
39+
*/
40+
@Command(name = "smee", mixinStandardHelpOptions = true, version = "smee 0.1",
41+
description = "smee made with jbang", showDefaultValues = true)
42+
class smee implements Callable<Integer> {
43+
44+
private static final Logger log;
45+
46+
static {
47+
System.setProperty("java.util.logging.SimpleFormatter.format",
48+
"%5$s %n");
49+
log = Logger.getLogger(smee.class.getName());
50+
}
51+
@Option(names = {"-u","--url"}, description = "URL of the webhook proxy service\n Default: Fetch new from https://smee.io/new")
52+
private String url;
53+
54+
@Option(names={"-t", "--target"},
55+
description = "Full URL (including protocol and path) of the target service the events will forwarded to\n Default: http://127.0.0.1:PORT/PATH")
56+
private String target;
57+
58+
@Option(names={"-p","--port"}, defaultValue = "${PORT:-3000}", description = "Local HTTP server port")
59+
int port;
60+
61+
@Option(names={"-P", "--path"}, defaultValue = "/", description = "URL path to post proxied requests to")
62+
String path;
63+
64+
public static void main(String... args) {
65+
int exitCode = new CommandLine(new smee()).execute(args);
66+
System.exit(exitCode);
67+
}
68+
69+
@Override
70+
public Integer call() throws Exception { // your business logic goes here...
71+
72+
if(target==null) {
73+
target = String.format("http://127.0.0.1:%s%s", port, path);
74+
}
75+
76+
if(url==null) {
77+
url = createChannel();
78+
}
79+
80+
URI uri = new URI(url);
81+
82+
Client client = ClientBuilder.newBuilder()
83+
.build();
84+
//.register(HTTPLoggingFilter.class);
85+
86+
final var events = SseEventSource.target(client.target(uri))
87+
// Reconnect immediately
88+
.reconnectingEvery(0, TimeUnit.MILLISECONDS).build();
89+
90+
events.register(this::onMessage, this::onError);
91+
92+
log.info("Forwarding " + url + " to " + target);
93+
events.open();
94+
95+
if(events.isOpen()) {
96+
log.info("Connected " + url);
97+
}
98+
99+
while(true) {
100+
Thread.sleep(50000);
101+
}
102+
103+
//return 0;
104+
}
105+
106+
private void onError(Throwable error) {
107+
log.severe(error.getMessage());
108+
}
109+
110+
private void onMessage(InboundSseEvent event) {
111+
if("ping".equals(event.getName()) || "ready".equals(event.getName())) {
112+
return;
113+
}
114+
ObjectMapper mapper = new ObjectMapper();
115+
try {
116+
ObjectNode data = (ObjectNode) mapper.readTree(event.readData());
117+
118+
var urib = UriBuilder.fromUri(this.target);
119+
120+
if(data.has("query")) {
121+
urib.replaceQuery(URLEncoder.encode(data.get("query").asText(), "UTF-8"));
122+
data.remove("query");
123+
}
124+
125+
try(CloseableHttpClient client = HttpClientBuilder.create().disableCookieManagement().build()) {
126+
127+
var request = new HttpPost(urib.build());
128+
129+
if(data.has("body")) {
130+
request.setEntity(new StringEntity(data.get("body").asText()));
131+
data.remove("body");
132+
}
133+
134+
data.fieldNames().forEachRemaining(s ->
135+
{
136+
if(!s.equalsIgnoreCase("content-length")) {
137+
request.setHeader(s, data.get(s).asText());
138+
}});
139+
140+
141+
CloseableHttpResponse response = client.execute(request);
142+
143+
if(response.getStatusLine().getStatusCode()!=200) {
144+
log.severe(response.getStatusLine().toString());
145+
} else {
146+
log.info(request.getMethod() + " " + request.getURI() + " - " + response.getStatusLine().getStatusCode());
147+
}
148+
}
149+
150+
} catch (IOException e) {
151+
log.warning("Could not parse event data: " + e.getMessage());
152+
e.printStackTrace();
153+
} catch (RuntimeException re) {
154+
log.warning("Could not parse event data: " + re.getMessage());
155+
re.printStackTrace();
156+
}
157+
158+
}
159+
160+
private String createChannel() throws IOException {
161+
HttpURLConnection con = (HttpURLConnection)(new URL( "https://smee.io/new" ).openConnection());
162+
con.setInstanceFollowRedirects( false );
163+
con.connect();
164+
return con.getHeaderField( "Location" );
165+
}
166+
167+
static public class HTTPLoggingFilter implements ClientRequestFilter, ClientResponseFilter {
168+
169+
private static final Logger logger = Logger.getLogger(HTTPLoggingFilter.class.getName());
170+
171+
@Override
172+
public void filter(ClientRequestContext requestContext) throws IOException {
173+
logger.info("request:" + requestContext.getUri().toString());
174+
logger.info("request:" + requestContext.getHeaders().toString());
175+
176+
}
177+
178+
@Override
179+
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
180+
//
181+
// logger.info(responseContext.getUri().toString());
182+
logger.info("response: " + responseContext.getHeaders().toString());
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)