Skip to content

Commit 6eecf29

Browse files
authored
Generic http binding (#225)
Signed-off-by: Matej Vasek <mvasek@redhat.com>
1 parent 29c9eaa commit 6eecf29

File tree

14 files changed

+1056
-0
lines changed

14 files changed

+1056
-0
lines changed

examples/basic-http/pom.xml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Copyright 2018-Present The CloudEvents Authors
4+
~ <p>
5+
~ Licensed under the Apache License, Version 2.0 (the "License");
6+
~ you may not use this file except in compliance with the License.
7+
~ You may obtain a copy of the License at
8+
~ <p>
9+
~ http://www.apache.org/licenses/LICENSE-2.0
10+
~ <p>
11+
~ Unless required by applicable law or agreed to in writing, software
12+
~ distributed under the License is distributed on an "AS IS" BASIS,
13+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
~ See the License for the specific language governing permissions and
15+
~ limitations under the License.
16+
-->
17+
18+
<project xmlns="http://maven.apache.org/POM/4.0.0"
19+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
21+
<parent>
22+
<artifactId>cloudevents-examples</artifactId>
23+
<groupId>io.cloudevents</groupId>
24+
<version>2.0.0-SNAPSHOT</version>
25+
</parent>
26+
<modelVersion>4.0.0</modelVersion>
27+
28+
<artifactId>cloudevents-basic-http-example</artifactId>
29+
30+
<dependencies>
31+
<dependency>
32+
<groupId>io.cloudevents</groupId>
33+
<artifactId>cloudevents-http-basic</artifactId>
34+
<version>${project.version}</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>io.cloudevents</groupId>
38+
<artifactId>cloudevents-json-jackson</artifactId>
39+
<version>${project.version}</version>
40+
</dependency>
41+
<dependency>
42+
<groupId>org.eclipse.jetty</groupId>
43+
<artifactId>jetty-server</artifactId>
44+
<version>9.4.28.v20200408</version>
45+
</dependency>
46+
</dependencies>
47+
</project>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2018-Present The CloudEvents Authors
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.cloudevents.examples.http.basic;
18+
19+
import com.sun.net.httpserver.Headers;
20+
import com.sun.net.httpserver.HttpExchange;
21+
import com.sun.net.httpserver.HttpServer;
22+
import io.cloudevents.CloudEvent;
23+
import io.cloudevents.core.message.MessageReader;
24+
import io.cloudevents.core.message.MessageWriter;
25+
import io.cloudevents.http.HttpMessageFactory;
26+
27+
import java.io.*;
28+
import java.net.InetSocketAddress;
29+
30+
public class BasicHttpServer {
31+
32+
public static void main(String[] args) throws IOException {
33+
HttpServer httpServer = HttpServer.create(new InetSocketAddress("localhost", 8080), 0);
34+
httpServer.createContext("/echo", BasicHttpServer::echoHandler);
35+
httpServer.start();
36+
}
37+
38+
private static void echoHandler(HttpExchange exchange) throws IOException {
39+
if (!"POST".equalsIgnoreCase(exchange.getRequestMethod())) {
40+
exchange.sendResponseHeaders(405, 0);
41+
return;
42+
}
43+
try {
44+
MessageReader messageReader = createMessageReader(exchange);
45+
CloudEvent cloudEvent = messageReader.toEvent();
46+
47+
System.out.println("Handling event: " + cloudEvent);
48+
49+
MessageWriter messageWriter = createMessageWriter(exchange);
50+
messageWriter.writeBinary(cloudEvent);
51+
} catch (Throwable t) {
52+
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
53+
try (PrintWriter pw = new PrintWriter(byteArrayOutputStream)) {
54+
t.printStackTrace(pw);
55+
}
56+
byte[] body = byteArrayOutputStream.toByteArray();
57+
exchange.sendResponseHeaders(500, body.length);
58+
try (OutputStream outputStream = exchange.getResponseBody()) {
59+
outputStream.write(body);
60+
}
61+
}
62+
}
63+
}
64+
65+
private static MessageReader createMessageReader(HttpExchange httpExchange) throws IOException {
66+
Headers headers = httpExchange.getRequestHeaders();
67+
byte[] body = IOUtils.toByteArray(httpExchange.getRequestBody());
68+
return HttpMessageFactory.createReaderFromMultimap(headers, body);
69+
}
70+
71+
private static MessageWriter createMessageWriter(HttpExchange httpExchange) {
72+
return HttpMessageFactory.createWriter(
73+
httpExchange.getResponseHeaders()::add,
74+
body -> {
75+
try {
76+
try (OutputStream os = httpExchange.getResponseBody()){
77+
if (body != null) {
78+
httpExchange.sendResponseHeaders(200, body.length);
79+
os.write(body);
80+
} else {
81+
httpExchange.sendResponseHeaders(204, -1);
82+
}
83+
}
84+
} catch (IOException t) {
85+
throw new UncheckedIOException(t);
86+
}
87+
}
88+
);
89+
}
90+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2018-Present The CloudEvents Authors
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.cloudevents.examples.http.basic;
18+
19+
import io.cloudevents.CloudEvent;
20+
import io.cloudevents.core.message.MessageReader;
21+
import io.cloudevents.core.message.MessageWriter;
22+
import io.cloudevents.core.v1.CloudEventBuilder;
23+
import io.cloudevents.http.HttpMessageFactory;
24+
25+
import java.io.IOException;
26+
import java.io.OutputStream;
27+
import java.io.UncheckedIOException;
28+
import java.net.HttpURLConnection;
29+
import java.net.URI;
30+
import java.net.URL;
31+
import java.nio.charset.StandardCharsets;
32+
import java.util.List;
33+
import java.util.Map;
34+
35+
public class HttpURLConnectionClient {
36+
37+
public static void main(String[] args) throws IOException {
38+
CloudEvent ceToSend = new CloudEventBuilder()
39+
.withId("my-id")
40+
.withSource(URI.create("/myClient"))
41+
.withType("dev.knative.cronjob.event")
42+
.withDataContentType("application/json")
43+
.withData("{ \"msg\" : \"hello\" }".getBytes(StandardCharsets.UTF_8))
44+
.build();
45+
46+
47+
URL url = new URL("http://localhost:8080/echo");
48+
HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
49+
httpUrlConnection.setRequestMethod("POST");
50+
httpUrlConnection.setDoOutput(true);
51+
httpUrlConnection.setDoInput(true);
52+
53+
MessageWriter messageWriter = createMessageWriter(httpUrlConnection);
54+
messageWriter.writeBinary(ceToSend);
55+
56+
MessageReader messageReader = createMessageReader(httpUrlConnection);
57+
CloudEvent receivedCE = messageReader.toEvent();
58+
59+
System.out.println("CloudEvent: " + receivedCE);
60+
System.out.println("Data: " + new String(receivedCE.getData(), StandardCharsets.UTF_8));
61+
}
62+
63+
private static MessageReader createMessageReader(HttpURLConnection httpUrlConnection) throws IOException {
64+
Map<String, List<String>> headers = httpUrlConnection.getHeaderFields();
65+
byte[] body = IOUtils.toByteArray(httpUrlConnection.getInputStream());
66+
return HttpMessageFactory.createReaderFromMultimap(headers, body);
67+
}
68+
69+
private static MessageWriter createMessageWriter(HttpURLConnection httpUrlConnection) {
70+
return HttpMessageFactory.createWriter(
71+
httpUrlConnection::setRequestProperty,
72+
body -> {
73+
try {
74+
if (body != null) {
75+
httpUrlConnection.setRequestProperty("content-length", String.valueOf(body.length));
76+
try (OutputStream outputStream = httpUrlConnection.getOutputStream()) {
77+
outputStream.write(body);
78+
}
79+
} else {
80+
httpUrlConnection.setRequestProperty("content-length", "0");
81+
}
82+
} catch (IOException t) {
83+
throw new UncheckedIOException(t);
84+
}
85+
});
86+
}
87+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2018-Present The CloudEvents Authors
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.cloudevents.examples.http.basic;
18+
19+
import java.io.ByteArrayOutputStream;
20+
import java.io.IOException;
21+
import java.io.InputStream;
22+
23+
// based on apache's commons-io
24+
// which is licensed under the Apache License, Version 2.0
25+
// https://commons.apache.org/proper/commons-io/
26+
// https://github.com/apache/commons-io/blob/master/src/main/java/org/apache/commons/io/IOUtils.java
27+
28+
public final class IOUtils {
29+
30+
private IOUtils() {}
31+
32+
// since Java 9+ you may call InputStream.readAllBytes() instead of this method
33+
public static byte[] toByteArray(InputStream body) throws IOException {
34+
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
35+
byte[] buff = new byte[(1<<10) * 8];
36+
int read;
37+
while ((read = body.read(buff)) != -1) {
38+
byteArrayOutputStream.write(buff, 0, read);
39+
}
40+
return byteArrayOutputStream.toByteArray();
41+
}
42+
}
43+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright 2018-Present The CloudEvents Authors
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.cloudevents.examples.http.basic;
18+
19+
import javax.servlet.ServletOutputStream;
20+
import javax.servlet.http.HttpServletRequest;
21+
import javax.servlet.http.HttpServletResponse;
22+
import javax.servlet.ServletException;
23+
import java.io.IOException;
24+
import java.io.UncheckedIOException;
25+
import java.net.InetSocketAddress;
26+
import java.util.Enumeration;
27+
import java.util.function.BiConsumer;
28+
import java.util.function.Consumer;
29+
30+
import io.cloudevents.CloudEvent;
31+
import io.cloudevents.core.message.MessageReader;
32+
import io.cloudevents.core.message.MessageWriter;
33+
import io.cloudevents.http.HttpMessageFactory;
34+
35+
import org.eclipse.jetty.http.HttpStatus;
36+
import org.eclipse.jetty.server.Server;
37+
import org.eclipse.jetty.server.Request;
38+
import org.eclipse.jetty.server.handler.AbstractHandler;
39+
40+
public class JettyServer {
41+
42+
private static class Handler extends AbstractHandler {
43+
44+
@Override
45+
public void handle(String uri,
46+
Request request,
47+
HttpServletRequest httpServletRequest,
48+
HttpServletResponse httpServletResponse) throws IOException, ServletException {
49+
if (!"/echo".equalsIgnoreCase(uri)) {
50+
httpServletResponse.setStatus(HttpStatus.NOT_FOUND_404);
51+
return;
52+
}
53+
if (!"POST".equalsIgnoreCase(request.getMethod())) {
54+
httpServletResponse.setStatus(HttpStatus.METHOD_NOT_ALLOWED_405);
55+
return;
56+
}
57+
58+
CloudEvent receivedEvent = createMessageReader(httpServletRequest).toEvent();
59+
System.out.println("Handling event: " + receivedEvent);
60+
createMessageWriter(httpServletResponse).writeBinary(receivedEvent);
61+
}
62+
}
63+
64+
private static MessageReader createMessageReader(HttpServletRequest httpServletRequest) throws IOException {
65+
Consumer<BiConsumer<String, String>> forEachHeader = processHeader -> {
66+
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
67+
while (headerNames.hasMoreElements()) {
68+
String name = headerNames.nextElement();
69+
processHeader.accept(name, httpServletRequest.getHeader(name));
70+
71+
}
72+
};
73+
byte[] body = IOUtils.toByteArray(httpServletRequest.getInputStream());
74+
return HttpMessageFactory.createReader(forEachHeader, body);
75+
}
76+
77+
private static MessageWriter createMessageWriter(HttpServletResponse httpServletResponse) throws IOException {
78+
return HttpMessageFactory.createWriter(
79+
httpServletResponse::addHeader,
80+
body -> {
81+
try {
82+
try (ServletOutputStream outputStream = httpServletResponse.getOutputStream()) {
83+
if (body != null) {
84+
httpServletResponse.setContentLength(body.length);
85+
httpServletResponse.setStatus(HttpStatus.OK_200);
86+
outputStream.write(body);
87+
} else {
88+
httpServletResponse.setStatus(HttpStatus.NO_CONTENT_204);
89+
}
90+
}
91+
} catch (IOException e) {
92+
throw new UncheckedIOException(e);
93+
}
94+
});
95+
}
96+
97+
public static void main(String[] args) throws Exception {
98+
Server server = new Server(new InetSocketAddress("localhost", 8080));
99+
server.setHandler(new Handler());
100+
server.start();
101+
server.join();
102+
}
103+
}

examples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<module>kafka</module>
1818
<module>restful-ws-quarkus</module>
1919
<module>vertx</module>
20+
<module>basic-http</module>
2021
</modules>
2122

2223

0 commit comments

Comments
 (0)