routeIds = this.routingTable.findRouteIds(tagsMetadata);
99 | if (routeIds.contains(route.getId())) {
100 | return Mono.just(route);
101 | }
102 | return Mono.empty();
103 | }
104 |
105 | }
106 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/filter/AbstractFilterChain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.filter;
18 |
19 | import java.util.Collections;
20 | import java.util.List;
21 | import java.util.ListIterator;
22 |
23 | import org.apache.commons.logging.Log;
24 | import org.apache.commons.logging.LogFactory;
25 | import reactor.core.publisher.Mono;
26 |
27 | import org.springframework.cloud.gateway.rsocket.filter.RSocketFilter.Success;
28 | import org.springframework.lang.Nullable;
29 |
30 | /**
31 | * Default implementation of {@link FilterChain}.
32 | *
33 | *
34 | * Each instance of this class represents one link in the chain. The public constructor
35 | * {@link #AbstractFilterChain(List)} initializes the full chain and represents its first
36 | * link.
37 | *
38 | *
39 | * This class is immutable and thread-safe. It can be created once and re-used to handle
40 | * request concurrently.
41 | *
42 | * Copied from org.springframework.web.server.handler.AbstractFilterChain
43 | *
44 | * @since 5.0
45 | */
46 | public abstract class AbstractFilterChain
47 | implements FilterChain {
48 |
49 | private final Log log = LogFactory.getLog(getClass());
50 |
51 | protected final List allFilters;
52 |
53 | @Nullable
54 | protected final F currentFilter;
55 |
56 | @Nullable
57 | protected final FC next;
58 |
59 | /**
60 | * Public constructor with the list of filters and the target handler to use.
61 | * @param filters the filters ahead of the handler
62 | */
63 | @SuppressWarnings("unchecked")
64 | protected AbstractFilterChain(List filters) {
65 | this.allFilters = Collections.unmodifiableList(filters);
66 | FC chain = initChain(filters);
67 | this.currentFilter = (F) chain.currentFilter;
68 | this.next = (FC) chain.next;
69 | }
70 |
71 | private FC initChain(List filters) {
72 | FC chain = create(filters, null, null);
73 | ListIterator extends F> iterator = filters.listIterator(filters.size());
74 | while (iterator.hasPrevious()) {
75 | chain = create(filters, iterator.previous(), chain);
76 | }
77 | return chain;
78 | }
79 |
80 | /**
81 | * Private constructor to represent one link in the chain.
82 | */
83 | protected AbstractFilterChain(List allFilters, @Nullable F currentFilter,
84 | @Nullable FC next) {
85 |
86 | this.allFilters = allFilters;
87 | this.currentFilter = currentFilter;
88 | this.next = next;
89 | }
90 |
91 | /**
92 | * Private constructor to represent one link in the chain.
93 | */
94 | protected abstract FC create(List allFilters, @Nullable F currentFilter,
95 | @Nullable FC next);
96 |
97 | public List getFilters() {
98 | return this.allFilters;
99 | }
100 |
101 | @Override
102 | @SuppressWarnings("unchecked")
103 | public Mono filter(E exchange) {
104 | return Mono.defer(() -> this.currentFilter != null && this.next != null
105 | ? this.currentFilter.filter(exchange, this.next) : getMonoSuccess());
106 | }
107 |
108 | private Mono getMonoSuccess() {
109 | if (log.isDebugEnabled()) {
110 | log.debug("filter chain completed with success");
111 | }
112 | return MONO_SUCCESS;
113 | }
114 |
115 | private static final Mono MONO_SUCCESS = Mono.just(Success.INSTANCE);
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/filter/AbstractRSocketExchange.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.filter;
18 |
19 | import java.util.Map;
20 | import java.util.concurrent.ConcurrentHashMap;
21 |
22 | public abstract class AbstractRSocketExchange implements RSocketExchange {
23 |
24 | private final Map attributes = new ConcurrentHashMap<>();
25 |
26 | @Override
27 | public Map getAttributes() {
28 | return this.attributes;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/filter/FilterChain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.filter;
18 |
19 | import reactor.core.publisher.Mono;
20 |
21 | import org.springframework.cloud.gateway.rsocket.filter.RSocketFilter.Success;
22 |
23 | /**
24 | * Contract to allow a {@link RSocketFilter} to delegate to the next in the chain.
25 | *
26 | * @author Spencer Gibb
27 | */
28 | public interface FilterChain {
29 |
30 | /**
31 | * Delegate to the next {@code WebFilter} in the chain.
32 | * @param exchange the current server exchange
33 | * @return {@code Mono} to indicate when request handling is complete
34 | */
35 | Mono filter(E exchange);
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/filter/RSocketExchange.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.filter;
18 |
19 | import java.util.Map;
20 |
21 | import org.springframework.lang.Nullable;
22 | import org.springframework.util.Assert;
23 |
24 | public interface RSocketExchange {
25 |
26 | /**
27 | * Return a mutable map of request attributes for the current exchange.
28 | * @return current attributes.
29 | */
30 | Map getAttributes();
31 |
32 | /**
33 | * Return the request attribute value if present.
34 | * @param name the attribute name
35 | * @param the attribute type
36 | * @return the attribute value
37 | */
38 | @SuppressWarnings("unchecked")
39 | @Nullable
40 | default T getAttribute(String name) {
41 | return (T) getAttributes().get(name);
42 | }
43 |
44 | /**
45 | * Return the request attribute value or if not present raise an
46 | * {@link IllegalArgumentException}.
47 | * @param name the attribute name
48 | * @param the attribute type
49 | * @return the attribute value
50 | */
51 | @SuppressWarnings("unchecked")
52 | default T getRequiredAttribute(String name) {
53 | T value = getAttribute(name);
54 | Assert.notNull(value, () -> "Required attribute '" + name + "' is missing");
55 | return value;
56 | }
57 |
58 | /**
59 | * Return the request attribute value, or a default, fallback value.
60 | * @param name the attribute name
61 | * @param defaultValue a default value to return instead
62 | * @param the attribute type
63 | * @return the attribute value
64 | */
65 | @SuppressWarnings("unchecked")
66 | default T getAttributeOrDefault(String name, T defaultValue) {
67 | return (T) getAttributes().getOrDefault(name, defaultValue);
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/filter/RSocketFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.filter;
18 |
19 | import reactor.core.publisher.Mono;
20 |
21 | /**
22 | * Contract for interception-style, chained processing of Web requests that may be used to
23 | * implement cross-cutting, application-agnostic requirements such as security, timeouts,
24 | * and others.
25 | *
26 | * Copied from WebFilter
27 | *
28 | * @author Spencer Gibb
29 | */
30 | public interface RSocketFilter> {
31 |
32 | /**
33 | * Enum to signal successful end of chain reached without the end being empty, i.e.
34 | * Mono<Void> via Mono.empty(). This is because at the end of the chain an
35 | * actual value needs to be returned. We can map success, but not empty.
36 | */
37 | enum Success {
38 |
39 | INSTANCE
40 |
41 | } // should never have more than one value
42 |
43 | /**
44 | * Process the Web request and (optionally) delegate to the next {@code RSocketFilter}
45 | * through the given {@link FilterChain}.
46 | * @param exchange the current RSocket exchange
47 | * @param chain provides a way to delegate to the next filter
48 | * @return {@code Mono} to indicate when request processing is complete.
49 | */
50 | Mono filter(E exchange, FC chain);
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/metrics/MicrometerResponderRSocketInterceptor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.metrics;
18 |
19 | import java.util.Objects;
20 |
21 | import io.micrometer.core.instrument.Meter;
22 | import io.micrometer.core.instrument.MeterRegistry;
23 | import io.micrometer.core.instrument.Tag;
24 | import io.rsocket.RSocket;
25 | import io.rsocket.plugins.RSocketInterceptor;
26 |
27 | public class MicrometerResponderRSocketInterceptor implements RSocketInterceptor {
28 |
29 | private final MeterRegistry meterRegistry;
30 |
31 | private final Tag[] tags;
32 |
33 | /**
34 | * Creates a new {@link RSocketInterceptor}.
35 | * @param meterRegistry the {@link MeterRegistry} to use to create {@link Meter}s.
36 | * @param tags the additional tags to attach to each {@link Meter}
37 | * @throws NullPointerException if {@code meterRegistry} is {@code null}
38 | */
39 | public MicrometerResponderRSocketInterceptor(MeterRegistry meterRegistry,
40 | Tag... tags) {
41 | this.meterRegistry = Objects.requireNonNull(meterRegistry,
42 | "meterRegistry must not be null");
43 | this.tags = tags;
44 | }
45 |
46 | @Override
47 | public MicrometerResponderRSocket apply(RSocket delegate) {
48 | Objects.requireNonNull(delegate, "delegate must not be null");
49 |
50 | return new MicrometerResponderRSocket(delegate, meterRegistry, tags);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/route/Route.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.route;
18 |
19 | import java.util.List;
20 |
21 | import org.springframework.cloud.gateway.rsocket.core.GatewayExchange;
22 | import org.springframework.cloud.gateway.rsocket.core.GatewayFilter;
23 | import org.springframework.cloud.gateway.rsocket.support.AsyncPredicate;
24 | import org.springframework.core.Ordered;
25 |
26 | /**
27 | * @author Spencer Gibb
28 | */
29 | public interface Route extends Ordered {
30 |
31 | String getId();
32 |
33 | default int getOrder() {
34 | return 0;
35 | }
36 |
37 | AsyncPredicate getPredicate();
38 |
39 | List getFilters();
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/route/Routes.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.route;
18 |
19 | import org.apache.commons.logging.Log;
20 | import org.apache.commons.logging.LogFactory;
21 | import reactor.core.publisher.Flux;
22 | import reactor.core.publisher.Mono;
23 |
24 | import org.springframework.cloud.gateway.rsocket.core.GatewayExchange;
25 |
26 | /**
27 | * @author Spencer Gibb
28 | */
29 | public interface Routes {
30 |
31 | /** log. */
32 | Log log = LogFactory.getLog(Routes.class);
33 |
34 | Flux getRoutes();
35 |
36 | default Mono findRoute(GatewayExchange exchange) {
37 | return getRoutes()
38 | // individually filter routes so that filterWhen error delaying is not a
39 | // problem
40 | .concatMap(route -> Mono.just(route).filterWhen(r -> {
41 | // add the current route we are testing
42 | // TODO: exchange attributes
43 | // exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR,
44 | // r.getId());
45 | return r.getPredicate().apply(exchange);
46 | })
47 | // instead of immediately stopping main flux due to error, log and
48 | // swallow it
49 | .doOnError(e -> log.error(
50 | "Error applying predicate for route: " + route.getId(),
51 | e))
52 | .onErrorResume(e -> Mono.empty()))
53 | .next().map(route -> {
54 | if (log.isDebugEnabled()) {
55 | log.debug("Route matched: " + route.getId());
56 | }
57 | return route;
58 | });
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/routing/LoadBalancerFactory.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.routing;
18 |
19 | import java.util.List;
20 | import java.util.Random;
21 | import java.util.concurrent.atomic.AtomicInteger;
22 | import java.util.function.Function;
23 |
24 | import io.rsocket.RSocket;
25 | import org.apache.commons.logging.Log;
26 | import org.apache.commons.logging.LogFactory;
27 | import reactor.core.publisher.Mono;
28 | import reactor.util.function.Tuple2;
29 |
30 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata;
31 |
32 | public class LoadBalancerFactory {
33 |
34 | private static final Log log = LogFactory.getLog(LoadBalancerFactory.class);
35 |
36 | private final RoutingTable routingTable;
37 |
38 | public LoadBalancerFactory(RoutingTable routingTable) {
39 | this.routingTable = routingTable;
40 | }
41 |
42 | public List> find(TagsMetadata tagsMetadata) {
43 | List> rSockets = this.routingTable
44 | .findRSockets(tagsMetadata);
45 | return rSockets;
46 | }
47 |
48 | // TODO: potentially GatewayExchange or return a new Result Object?
49 | public Mono> choose(TagsMetadata tagsMetadata) {
50 | List> rSockets = this.routingTable
51 | .findRSockets(tagsMetadata);
52 | // TODO: change loadbalancer impl based on tags
53 | // TODO: cache loadbalancers based on tags
54 | return new RoundRobinLoadBalancer(tagsMetadata).apply(rSockets);
55 | }
56 |
57 | // TODO: Flux as input?
58 | // TODO: reuse commons load balancer?
59 | public interface LoadBalancer extends
60 | Function>, Mono>> {
61 |
62 | }
63 |
64 | public static class RoundRobinLoadBalancer implements LoadBalancer {
65 |
66 | private final TagsMetadata tagsMetadata;
67 |
68 | private final AtomicInteger position;
69 |
70 | public RoundRobinLoadBalancer(TagsMetadata tagsMetadata) {
71 | this(tagsMetadata, new Random().nextInt(1000));
72 | }
73 |
74 | public RoundRobinLoadBalancer(TagsMetadata tagsMetadata, int seedPosition) {
75 | this.tagsMetadata = tagsMetadata;
76 | this.position = new AtomicInteger(seedPosition);
77 | }
78 |
79 | @Override
80 | public Mono> apply(
81 | List> rSockets) {
82 | if (rSockets.isEmpty()) {
83 | if (log.isWarnEnabled()) {
84 | log.warn("No servers available for: " + this.tagsMetadata);
85 | }
86 | return Mono.empty();
87 | }
88 | // TODO: enforce order?
89 | int pos = Math.abs(this.position.incrementAndGet());
90 |
91 | Tuple2 tuple = rSockets.get(pos % rSockets.size());
92 | return Mono.just(tuple);
93 | }
94 |
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/routing/RoutingTableRoutes.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.routing;
18 |
19 | import java.util.Collection;
20 | import java.util.Collections;
21 | import java.util.List;
22 | import java.util.Map;
23 | import java.util.Objects;
24 | import java.util.Set;
25 | import java.util.concurrent.ConcurrentHashMap;
26 | import java.util.function.Consumer;
27 |
28 | import org.apache.commons.logging.Log;
29 | import org.apache.commons.logging.LogFactory;
30 | import org.reactivestreams.Publisher;
31 | import reactor.core.publisher.Flux;
32 | import reactor.core.publisher.Mono;
33 |
34 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata;
35 | import org.springframework.cloud.gateway.rsocket.core.GatewayExchange;
36 | import org.springframework.cloud.gateway.rsocket.core.GatewayFilter;
37 | import org.springframework.cloud.gateway.rsocket.route.Route;
38 | import org.springframework.cloud.gateway.rsocket.route.Routes;
39 | import org.springframework.cloud.gateway.rsocket.support.AsyncPredicate;
40 | import org.springframework.core.style.ToStringCreator;
41 |
42 | /**
43 | * View of RoutingTable as Route objects.
44 | */
45 | public class RoutingTableRoutes
46 | implements Routes, Consumer {
47 |
48 | private static final Log log = LogFactory.getLog(RoutingTableRoutes.class);
49 |
50 | private Map routes = new ConcurrentHashMap<>();
51 |
52 | private final RoutingTable routingTable;
53 |
54 | public RoutingTableRoutes(RoutingTable routingTable) {
55 | this.routingTable = routingTable;
56 | this.routingTable.addListener(this);
57 | }
58 |
59 | @Override
60 | public Flux getRoutes() {
61 | // TODO: sorting?
62 | // TODO: caching
63 |
64 | Collection routeCollection = routes.values();
65 | if (log.isDebugEnabled()) {
66 | log.debug("Found routes: " + routeCollection);
67 | }
68 | return Flux.fromIterable(routeCollection);
69 | }
70 |
71 | @Override
72 | public void accept(RoutingTable.RegisteredEvent registeredEvent) {
73 | TagsMetadata routingMetadata = registeredEvent.getRoutingMetadata();
74 | String routeId = routingMetadata.getRouteId();
75 |
76 | routes.computeIfAbsent(routeId, key -> createRoute(routeId));
77 | }
78 |
79 | private Route createRoute(String routeId) {
80 | AsyncPredicate predicate = new RoutIdPredicate(routingTable,
81 | routeId);
82 |
83 | RegistryRoute route = new RegistryRoute(routeId, predicate);
84 |
85 | if (log.isDebugEnabled()) {
86 | log.debug("Created Route for registered service " + route);
87 | }
88 |
89 | return route;
90 | }
91 |
92 | static class RoutIdPredicate implements AsyncPredicate {
93 |
94 | private final RoutingTable routingTable;
95 |
96 | private final String routeId;
97 |
98 | RoutIdPredicate(RoutingTable routingTable, String routeId) {
99 | this.routingTable = routingTable;
100 | this.routeId = routeId;
101 | }
102 |
103 | @Override
104 | public Publisher apply(GatewayExchange exchange) {
105 | // TODO: standard predicates
106 | // TODO: allow customized predicates
107 | Set routeIds = routingTable
108 | .findRouteIds(exchange.getRoutingMetadata());
109 | return Mono.just(routeIds.contains(routeId));
110 | }
111 |
112 | @Override
113 | public String toString() {
114 | return String.format("[RoutIdPredicate %s]", routeId);
115 | }
116 |
117 | }
118 |
119 | static class RegistryRoute implements Route {
120 |
121 | final String id;
122 |
123 | final AsyncPredicate predicate;
124 |
125 | RegistryRoute(String id, AsyncPredicate predicate) {
126 | this.id = id;
127 | this.predicate = predicate;
128 | }
129 |
130 | @Override
131 | public String getId() {
132 | return this.id;
133 | }
134 |
135 | @Override
136 | public AsyncPredicate getPredicate() {
137 | return this.predicate;
138 | }
139 |
140 | @Override
141 | public List getFilters() {
142 | return Collections.emptyList();
143 | }
144 |
145 | @Override
146 | public boolean equals(Object o) {
147 | if (this == o) {
148 | return true;
149 | }
150 | if (o == null || getClass() != o.getClass()) {
151 | return false;
152 | }
153 | RegistryRoute that = (RegistryRoute) o;
154 | return Objects.equals(this.id, that.id)
155 | && Objects.equals(this.predicate, that.predicate);
156 | }
157 |
158 | @Override
159 | public int hashCode() {
160 | return Objects.hash(this.id, this.predicate);
161 | }
162 |
163 | @Override
164 | public String toString() {
165 | return new ToStringCreator(this).append("id", id)
166 | .append("predicate", predicate).toString();
167 |
168 | }
169 |
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/routing/RoutingTableSocketAcceptorFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.routing;
18 |
19 | import reactor.core.publisher.Mono;
20 |
21 | import org.springframework.cloud.gateway.rsocket.socketacceptor.SocketAcceptorExchange;
22 | import org.springframework.cloud.gateway.rsocket.socketacceptor.SocketAcceptorFilter;
23 | import org.springframework.cloud.gateway.rsocket.socketacceptor.SocketAcceptorFilterChain;
24 | import org.springframework.core.Ordered;
25 |
26 | /**
27 | * Filter that registers the SendingSocket.
28 | */
29 | public class RoutingTableSocketAcceptorFilter implements SocketAcceptorFilter, Ordered {
30 |
31 | private final RoutingTable routingTable;
32 |
33 | public RoutingTableSocketAcceptorFilter(RoutingTable routingTable) {
34 | this.routingTable = routingTable;
35 | }
36 |
37 | @Override
38 | public Mono filter(SocketAcceptorExchange exchange,
39 | SocketAcceptorFilterChain chain) {
40 | if (exchange.getMetadata() != null) {
41 | // TODO: needed? &&
42 | // StringUtils.hasLength(exchange.getMetadata().getServiceName())) {
43 | this.routingTable.register(exchange.getMetadata().getEnrichedTagsMetadata(),
44 | exchange.getSendingSocket());
45 | }
46 |
47 | return chain.filter(exchange);
48 | }
49 |
50 | @Override
51 | public int getOrder() {
52 | return HIGHEST_PRECEDENCE + 1000;
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/socketacceptor/GatewaySocketAcceptor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.socketacceptor;
18 |
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.logging.Level;
22 | import java.util.stream.Collectors;
23 |
24 | import io.micrometer.core.instrument.MeterRegistry;
25 | import io.micrometer.core.instrument.Tag;
26 | import io.micrometer.core.instrument.Tags;
27 | import io.rsocket.ConnectionSetupPayload;
28 | import io.rsocket.RSocket;
29 | import io.rsocket.SocketAcceptor;
30 | import org.apache.commons.logging.Log;
31 | import org.apache.commons.logging.LogFactory;
32 | import reactor.core.publisher.Mono;
33 |
34 | import org.springframework.cloud.gateway.rsocket.autoconfigure.BrokerProperties;
35 | import org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetup;
36 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata;
37 | import org.springframework.cloud.gateway.rsocket.core.GatewayRSocketFactory;
38 | import org.springframework.cloud.gateway.rsocket.metrics.MicrometerResponderRSocket;
39 | import org.springframework.messaging.rsocket.MetadataExtractor;
40 | import org.springframework.util.MimeType;
41 |
42 | public class GatewaySocketAcceptor implements SocketAcceptor {
43 |
44 | private static final Log log = LogFactory.getLog(GatewaySocketAcceptor.class);
45 |
46 | private final SocketAcceptorFilterChain filterChain;
47 |
48 | private final GatewayRSocketFactory rSocketFactory;
49 |
50 | private final MeterRegistry meterRegistry;
51 |
52 | private final BrokerProperties properties;
53 |
54 | private final MetadataExtractor metadataExtractor;
55 |
56 | public GatewaySocketAcceptor(GatewayRSocketFactory rSocketFactory,
57 | List filters, MeterRegistry meterRegistry,
58 | BrokerProperties properties, MetadataExtractor metadataExtractor) {
59 | this.rSocketFactory = rSocketFactory;
60 | this.filterChain = new SocketAcceptorFilterChain(filters);
61 | this.meterRegistry = meterRegistry;
62 | this.properties = properties;
63 | this.metadataExtractor = metadataExtractor;
64 | }
65 |
66 | @Override
67 | @SuppressWarnings("Duplicates")
68 | public Mono accept(ConnectionSetupPayload setup, RSocket sendingSocket) {
69 | if (log.isTraceEnabled()) {
70 | log.trace("accept()");
71 | }
72 | // decorate GatewayRSocket with metrics
73 | // current gateway id, type requester, service name (from metadata), service id
74 |
75 | Tags requesterTags = Tags.of("gateway.id", properties.getId(), "type",
76 | "requester");
77 |
78 | Tags metadataTags;
79 | SocketAcceptorExchange exchange;
80 |
81 | Map metadataMap = null;
82 | try {
83 | metadataMap = this.metadataExtractor.extract(setup,
84 | MimeType.valueOf(setup.metadataMimeType()));
85 | }
86 | catch (Exception e) {
87 | if (log.isDebugEnabled()) {
88 | log.debug("Error extracting metadata", e);
89 | }
90 | return Mono.error(e);
91 | }
92 | if (metadataMap.containsKey("routesetup")) {
93 | RouteSetup metadata = (RouteSetup) metadataMap.get("routesetup");
94 | metadataTags = Tags.of("service.name", metadata.getServiceName())
95 | .and("service.id", metadata.getId().toString());
96 | // enrich exchange to have metadata
97 | exchange = new SocketAcceptorExchange(setup,
98 | decorate(sendingSocket, requesterTags.and(metadataTags)), metadata);
99 | }
100 | else {
101 | metadataTags = Tags.of("service.name", "UNKNOWN").and("service.id",
102 | "UNKNOWN");
103 | exchange = new SocketAcceptorExchange(setup,
104 | decorate(sendingSocket, requesterTags));
105 | }
106 |
107 | Tags responderTags = Tags
108 | .of("gateway.id", properties.getId(), "type", "responder")
109 | .and(metadataTags);
110 |
111 | // decorate with metrics gateway id, type responder, service name, service id
112 | // (instance id)
113 | return this.filterChain.filter(exchange).log(
114 | GatewaySocketAcceptor.class.getName() + ".socket acceptor filter chain",
115 | Level.FINEST).map(success -> {
116 | TagsMetadata tags = exchange.getMetadata().getEnrichedTagsMetadata();
117 | return decorate(this.rSocketFactory.create(tags), responderTags);
118 | });
119 | }
120 |
121 | private RSocket decorate(RSocket rSocket, Tags tags) {
122 | Tag[] tagArray = tags.stream().collect(Collectors.toList()).toArray(new Tag[] {});
123 | return new MicrometerResponderRSocket(rSocket, meterRegistry, tagArray);
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/socketacceptor/SocketAcceptorExchange.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.socketacceptor;
18 |
19 | import java.math.BigInteger;
20 |
21 | import io.rsocket.ConnectionSetupPayload;
22 | import io.rsocket.RSocket;
23 |
24 | import org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetup;
25 | import org.springframework.cloud.gateway.rsocket.filter.AbstractRSocketExchange;
26 |
27 | public class SocketAcceptorExchange extends AbstractRSocketExchange {
28 |
29 | private final ConnectionSetupPayload setup;
30 |
31 | private final RSocket sendingSocket;
32 |
33 | private final RouteSetup metadata;
34 |
35 | public SocketAcceptorExchange(ConnectionSetupPayload setup, RSocket sendingSocket) {
36 | this(setup, sendingSocket, RouteSetup.of((BigInteger) null, null).build());
37 | }
38 |
39 | public SocketAcceptorExchange(ConnectionSetupPayload setup, RSocket sendingSocket,
40 | RouteSetup metadata) {
41 | this.setup = setup;
42 | this.sendingSocket = sendingSocket;
43 | this.metadata = metadata;
44 | }
45 |
46 | public ConnectionSetupPayload getSetup() {
47 | return setup;
48 | }
49 |
50 | public RSocket getSendingSocket() {
51 | return sendingSocket;
52 | }
53 |
54 | public RouteSetup getMetadata() {
55 | return metadata;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/socketacceptor/SocketAcceptorFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.socketacceptor;
18 |
19 | import org.springframework.cloud.gateway.rsocket.filter.RSocketFilter;
20 |
21 | public interface SocketAcceptorFilter
22 | extends RSocketFilter {
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/socketacceptor/SocketAcceptorFilterChain.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.socketacceptor;
18 |
19 | import java.util.List;
20 |
21 | import org.springframework.cloud.gateway.rsocket.filter.AbstractFilterChain;
22 |
23 | public class SocketAcceptorFilterChain extends
24 | AbstractFilterChain {
25 |
26 | /**
27 | * Public constructor with the list of filters and the target handler to use.
28 | * @param filters the filters ahead of the handler
29 | */
30 | public SocketAcceptorFilterChain(List filters) {
31 | super(filters);
32 | }
33 |
34 | public SocketAcceptorFilterChain(List allFilters,
35 | SocketAcceptorFilter currentFilter, SocketAcceptorFilterChain next) {
36 | super(allFilters, currentFilter, next);
37 | }
38 |
39 | @Override
40 | protected SocketAcceptorFilterChain create(List allFilters,
41 | SocketAcceptorFilter currentFilter, SocketAcceptorFilterChain next) {
42 | return new SocketAcceptorFilterChain(allFilters, currentFilter, next);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/socketacceptor/SocketAcceptorPredicate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.socketacceptor;
18 |
19 | import org.springframework.cloud.gateway.rsocket.support.AsyncPredicate;
20 |
21 | public interface SocketAcceptorPredicate extends AsyncPredicate {
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/socketacceptor/SocketAcceptorPredicateFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.socketacceptor;
18 |
19 | import java.util.List;
20 |
21 | import reactor.core.publisher.Mono;
22 |
23 | import org.springframework.cloud.gateway.rsocket.support.AsyncPredicate;
24 | import org.springframework.core.Ordered;
25 | import org.springframework.util.Assert;
26 |
27 | public class SocketAcceptorPredicateFilter implements SocketAcceptorFilter, Ordered {
28 |
29 | private final AsyncPredicate predicate;
30 |
31 | // TODO: change from List to Flux?
32 | public SocketAcceptorPredicateFilter(List predicates) {
33 | Assert.notNull(predicates, "predicates may not be null");
34 | if (predicates.isEmpty()) {
35 | predicate = exchange -> Mono.just(true);
36 | }
37 | else {
38 | AsyncPredicate combined = predicates.get(0);
39 | for (SocketAcceptorPredicate p : predicates.subList(1, predicates.size())) {
40 | combined = combined.and(p);
41 | }
42 | predicate = combined;
43 | }
44 | }
45 |
46 | @Override
47 | public int getOrder() {
48 | return HIGHEST_PRECEDENCE + 10000;
49 | }
50 |
51 | @Override
52 | public Mono filter(SocketAcceptorExchange exchange,
53 | SocketAcceptorFilterChain chain) {
54 | return Mono.from(predicate.apply(exchange)).flatMap(test -> {
55 | if (test) {
56 | return chain.filter(exchange);
57 | }
58 | return Mono.empty();
59 | });
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/java/org/springframework/cloud/gateway/rsocket/support/AsyncPredicate.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2018 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.support;
18 |
19 | import java.util.function.Function;
20 |
21 | import org.reactivestreams.Publisher;
22 | import reactor.core.publisher.Flux;
23 | import reactor.core.publisher.Mono;
24 |
25 | import org.springframework.util.Assert;
26 |
27 | /**
28 | * @author Ben Hale
29 | */
30 | public interface AsyncPredicate extends Function> {
31 |
32 | default AsyncPredicate and(AsyncPredicate super T> other) {
33 | Assert.notNull(other, "other must not be null");
34 |
35 | return t -> Flux.zip(apply(t), other.apply(t))
36 | .map(tuple -> tuple.getT1() && tuple.getT2());
37 | }
38 |
39 | default AsyncPredicate negate() {
40 | return t -> Mono.from(apply(t)).map(b -> !b);
41 | }
42 |
43 | default AsyncPredicate or(AsyncPredicate super T> other) {
44 | Assert.notNull(other, "other must not be null");
45 |
46 | return t -> Flux.zip(apply(t), other.apply(t))
47 | .map(tuple -> tuple.getT1() || tuple.getT2());
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | # Auto Configure
2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
3 | org.springframework.cloud.gateway.rsocket.autoconfigure.GatewayRSocketAutoConfiguration
4 |
5 | # Environment Post Processors
6 | org.springframework.boot.env.EnvironmentPostProcessor=\
7 | org.springframework.cloud.gateway.rsocket.autoconfigure.GatewayRSocketEnvironmentPostProcessor
8 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/java/org/springframework/cloud/gateway/rsocket/actuate/BrokerActuatorRegistrarTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.actuate;
18 |
19 | import org.junit.Test;
20 | import org.junit.runner.RunWith;
21 |
22 | import org.springframework.beans.factory.annotation.Autowired;
23 | import org.springframework.boot.SpringBootConfiguration;
24 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
25 | import org.springframework.boot.test.context.SpringBootTest;
26 | import org.springframework.test.context.junit4.SpringRunner;
27 |
28 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
29 |
30 | @RunWith(SpringRunner.class)
31 | @SpringBootTest(webEnvironment = RANDOM_PORT,
32 | properties = { "spring.rsocket.server.port=0",
33 | "spring.cloud.gateway.rsocket.route-id=55",
34 | "spring.cloud.gateway.rsocket.service-name=gateway" })
35 | public class BrokerActuatorRegistrarTests {
36 |
37 | @Autowired
38 | private BrokerActuatorHandlerRegistration registrar;
39 |
40 | @Test
41 | public void test() {
42 | }
43 |
44 | @SpringBootConfiguration
45 | @EnableAutoConfiguration
46 | static class Config {
47 |
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/java/org/springframework/cloud/gateway/rsocket/autoconfigure/GatewayRSocketAutoConfigurationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.autoconfigure;
18 |
19 | import io.rsocket.SocketAcceptor;
20 | import org.junit.Test;
21 |
22 | import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
23 | import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
24 | import org.springframework.boot.autoconfigure.AutoConfigurations;
25 | import org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration;
26 | import org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration;
27 | import org.springframework.boot.rsocket.context.RSocketServerBootstrap;
28 | import org.springframework.boot.rsocket.server.RSocketServer;
29 | import org.springframework.boot.rsocket.server.RSocketServerFactory;
30 | import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
31 | import org.springframework.cloud.gateway.rsocket.common.autoconfigure.GatewayRSocketCommonAutoConfiguration;
32 | import org.springframework.cloud.gateway.rsocket.core.GatewayServerRSocketFactoryProcessor;
33 | import org.springframework.cloud.gateway.rsocket.routing.RoutingTable;
34 | import org.springframework.cloud.gateway.rsocket.routing.RoutingTableRoutes;
35 | import org.springframework.cloud.gateway.rsocket.routing.RoutingTableSocketAcceptorFilter;
36 | import org.springframework.cloud.gateway.rsocket.socketacceptor.GatewaySocketAcceptor;
37 | import org.springframework.cloud.gateway.rsocket.socketacceptor.SocketAcceptorPredicate;
38 | import org.springframework.cloud.gateway.rsocket.socketacceptor.SocketAcceptorPredicateFilter;
39 | import org.springframework.context.annotation.Bean;
40 | import org.springframework.context.annotation.Configuration;
41 |
42 | import static org.assertj.core.api.Assertions.assertThat;
43 | import static org.mockito.ArgumentMatchers.any;
44 | import static org.mockito.Mockito.mock;
45 | import static org.mockito.Mockito.when;
46 |
47 | public class GatewayRSocketAutoConfigurationTests {
48 |
49 | @Test
50 | public void gatewayRSocketConfigured() {
51 | new ReactiveWebApplicationContextRunner().withUserConfiguration(MyConfig.class)
52 | .withSystemProperties("spring.cloud.gateway.rsocket.route-id=11")
53 | .withConfiguration(
54 | AutoConfigurations.of(RSocketStrategiesAutoConfiguration.class,
55 | RSocketMessagingAutoConfiguration.class,
56 | GatewayRSocketCommonAutoConfiguration.class,
57 | GatewayRSocketAutoConfiguration.class,
58 | CompositeMeterRegistryAutoConfiguration.class,
59 | MetricsAutoConfiguration.class))
60 | .run(context -> assertThat(context).hasSingleBean(RoutingTable.class)
61 | .hasSingleBean(RoutingTableRoutes.class)
62 | .hasSingleBean(RoutingTableSocketAcceptorFilter.class)
63 | .hasSingleBean(GatewayServerRSocketFactoryProcessor.class)
64 | .hasSingleBean(BrokerProperties.class)
65 | .hasSingleBean(GatewaySocketAcceptor.class)
66 | .hasSingleBean(SocketAcceptorPredicateFilter.class)
67 | .hasSingleBean(RSocketServerBootstrap.class)
68 | .doesNotHaveBean(SocketAcceptorPredicate.class));
69 | }
70 |
71 | @Configuration
72 | protected static class MyConfig {
73 |
74 | @Bean
75 | RSocketServerFactory rSocketServerFactory() {
76 | RSocketServerFactory serverFactory = mock(RSocketServerFactory.class);
77 | when(serverFactory.create(any(SocketAcceptor.class)))
78 | .thenReturn(mock(RSocketServer.class));
79 | return serverFactory;
80 | }
81 |
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/java/org/springframework/cloud/gateway/rsocket/cluster/ClusterServiceTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.cluster;
18 |
19 | import org.junit.Test;
20 |
21 | import org.springframework.cloud.gateway.rsocket.actuate.BrokerInfo;
22 |
23 | import static org.assertj.core.api.Assertions.assertThat;
24 |
25 | public class ClusterServiceTests {
26 |
27 | @Test
28 | public void registerIncomingWorks() {
29 | ClusterService routingTable = new ClusterService();
30 |
31 | BrokerInfo brokerInfo = BrokerInfo.of(1L).timestamp(100L).build();
32 | boolean result = routingTable.registerIncoming(brokerInfo);
33 |
34 | String brokerId = brokerInfo.getBrokerId().toString();
35 | assertThat(result).isTrue();
36 | assertThat(routingTable.incomingBrokers).containsKey(brokerId);
37 |
38 | brokerInfo = BrokerInfo.of(1L).timestamp(10L).build();
39 | result = routingTable.registerIncoming(brokerInfo);
40 | assertThat(result).isFalse();
41 | assertThat(routingTable.incomingBrokers.get(brokerId)).isNotNull()
42 | .extracting(ClusterService.BrokerEntry::getTimestamp).isEqualTo(100L);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/java/org/springframework/cloud/gateway/rsocket/core/GatewayRSocketIntegrationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.core;
18 |
19 | import java.time.Duration;
20 |
21 | import org.junit.AfterClass;
22 | import org.junit.BeforeClass;
23 | import org.junit.Test;
24 | import org.junit.runner.RunWith;
25 | import reactor.core.publisher.Hooks;
26 | import reactor.test.StepVerifier;
27 |
28 | import org.springframework.beans.factory.annotation.Autowired;
29 | import org.springframework.boot.autoconfigure.rsocket.RSocketProperties;
30 | import org.springframework.boot.rsocket.context.RSocketServerBootstrap;
31 | import org.springframework.boot.test.context.SpringBootTest;
32 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
33 | import org.springframework.cloud.gateway.rsocket.test.PingPongApp;
34 | import org.springframework.test.context.junit4.SpringRunner;
35 | import org.springframework.test.util.ReflectionTestUtils;
36 | import org.springframework.util.SocketUtils;
37 |
38 | import static org.assertj.core.api.Assertions.assertThat;
39 |
40 | @RunWith(SpringRunner.class)
41 | @SpringBootTest(classes = PingPongApp.class,
42 | properties = { "ping.take=10", "ping.subscribe=false" },
43 | webEnvironment = WebEnvironment.RANDOM_PORT)
44 | public class GatewayRSocketIntegrationTests {
45 |
46 | private static int port;
47 |
48 | @Autowired
49 | private PingPongApp.Ping ping;
50 |
51 | @Autowired
52 | private PingPongApp.Pong pong;
53 |
54 | @Autowired
55 | private RSocketProperties properties;
56 |
57 | @Autowired
58 | private PingPongApp.MySocketAcceptorFilter mySocketAcceptorFilter;
59 |
60 | @Autowired
61 | private RSocketServerBootstrap server;
62 |
63 | @BeforeClass
64 | public static void init() {
65 | Hooks.onOperatorDebug();
66 | port = SocketUtils.findAvailableTcpPort();
67 | System.setProperty("spring.rsocket.server.port", String.valueOf(port));
68 | }
69 |
70 | @AfterClass
71 | public static void after() {
72 | System.clearProperty("spring.rsocket.server.port");
73 | }
74 |
75 | @Test
76 | public void contextLoads() {
77 | // @formatter:off
78 | StepVerifier.create(ping.getPongFlux())
79 | .expectSubscription()
80 | .then(() -> server.stop())
81 | .thenConsumeWhile(s -> true)
82 | .expectComplete()
83 | .verify(Duration.ofSeconds(20));
84 | // @formatter:on
85 |
86 | assertThat(ping.getPongsReceived()).isGreaterThan(0);
87 | assertThat(pong.getPingsReceived()).isGreaterThan(0);
88 | Object server = properties.getServer();
89 | Object port = ReflectionTestUtils.invokeGetterMethod(server, "port");
90 | assertThat(port).isNotEqualTo(7002);
91 | assertThat(mySocketAcceptorFilter.invoked()).isTrue();
92 | assertThat(this.server.isRunning()).isFalse();
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/java/org/springframework/cloud/gateway/rsocket/routing/RoutingTableRoutesTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.routing;
18 |
19 | import java.util.HashSet;
20 |
21 | import io.rsocket.RSocket;
22 | import org.junit.Test;
23 | import reactor.core.publisher.Mono;
24 | import reactor.test.StepVerifier;
25 |
26 | import org.springframework.cloud.gateway.rsocket.common.metadata.Forwarding;
27 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata;
28 | import org.springframework.cloud.gateway.rsocket.core.GatewayExchange;
29 | import org.springframework.cloud.gateway.rsocket.route.Route;
30 | import org.springframework.cloud.gateway.rsocket.routing.RoutingTable.RegisteredEvent;
31 | import org.springframework.cloud.gateway.rsocket.routing.RoutingTable.RouteEntry;
32 |
33 | import static org.assertj.core.api.Assertions.assertThat;
34 | import static org.mockito.ArgumentMatchers.any;
35 | import static org.mockito.Mockito.mock;
36 | import static org.mockito.Mockito.when;
37 | import static org.springframework.cloud.gateway.rsocket.core.GatewayExchange.Type.REQUEST_RESPONSE;
38 |
39 | public class RoutingTableRoutesTests {
40 |
41 | @Test
42 | public void routesAreBuilt() {
43 | RoutingTable routingTable = mock(RoutingTable.class);
44 | RoutingTableRoutes routes = new RoutingTableRoutes(routingTable);
45 |
46 | HashSet routeIds = new HashSet<>();
47 | routeIds.add("2");
48 | when(routingTable.findRouteIds(any(TagsMetadata.class))).thenReturn(routeIds);
49 | addRoute(routes, "1");
50 | addRoute(routes, "2");
51 | addRoute(routes, "3");
52 |
53 | Forwarding forwarding = Forwarding.of(1L).routeId("2").build();
54 | Mono routeMono = routes
55 | .findRoute(new GatewayExchange(REQUEST_RESPONSE, forwarding));
56 |
57 | StepVerifier.create(routeMono).consumeNextWith(route -> {
58 | assertThat(route).isNotNull().extracting(Route::getId).isEqualTo("2");
59 | }).verifyComplete();
60 | }
61 |
62 | void addRoute(RoutingTableRoutes routes, String routeId) {
63 | TagsMetadata tagsMetadata = TagsMetadata.builder().routeId(routeId).build();
64 |
65 | RSocket rsocket = mock(RSocket.class);
66 | routes.accept(new RegisteredEvent(new RouteEntry(rsocket, tagsMetadata)));
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/java/org/springframework/cloud/gateway/rsocket/socketacceptor/SocketAcceptorPredicateFilterTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.socketacceptor;
18 |
19 | import java.util.Arrays;
20 | import java.util.Collections;
21 | import java.util.List;
22 |
23 | import io.rsocket.ConnectionSetupPayload;
24 | import io.rsocket.RSocket;
25 | import org.junit.Test;
26 | import org.reactivestreams.Publisher;
27 | import reactor.core.publisher.Mono;
28 | import reactor.test.StepVerifier;
29 |
30 | import org.springframework.cloud.gateway.rsocket.filter.RSocketFilter.Success;
31 |
32 | import static org.assertj.core.api.Assertions.assertThat;
33 | import static org.mockito.Mockito.mock;
34 |
35 | public class SocketAcceptorPredicateFilterTests {
36 |
37 | @Test
38 | public void noPredicateWorks() {
39 | Mono result = runFilter(Collections.emptyList());
40 | StepVerifier.create(result).expectNext(Success.INSTANCE).verifyComplete();
41 | }
42 |
43 | @Test
44 | public void singleTruePredicateWorks() {
45 | TestPredicate predicate = new TestPredicate(true);
46 | Mono result = runFilter(predicate);
47 | StepVerifier.create(result).expectNext(Success.INSTANCE).verifyComplete();
48 | assertThat(predicate.invoked()).isTrue();
49 | }
50 |
51 | @Test
52 | public void singleFalsePredicateWorks() {
53 | TestPredicate predicate = new TestPredicate(false);
54 | Mono result = runFilter(predicate);
55 | StepVerifier.create(result).verifyComplete();
56 |
57 | assertThat(predicate.invoked()).isTrue();
58 | }
59 |
60 | @Test
61 | public void multipleFalsePredicateWorks() {
62 | TestPredicate predicate = new TestPredicate(false);
63 | TestPredicate predicate2 = new TestPredicate(false);
64 | Mono result = runFilter(predicate, predicate2);
65 | StepVerifier.create(result).verifyComplete();
66 |
67 | assertThat(predicate.invoked()).isTrue();
68 | assertThat(predicate2.invoked()).isTrue(); // Async predicates don't short circuit
69 | }
70 |
71 | @Test
72 | public void multiplePredicatesNoSuccessWorks() {
73 | TestPredicate truePredicate = new TestPredicate(true);
74 | TestPredicate falsePredicate = new TestPredicate(false);
75 | Mono result = runFilter(truePredicate, falsePredicate);
76 | StepVerifier.create(result).verifyComplete();
77 | assertThat(truePredicate.invoked()).isTrue();
78 | assertThat(falsePredicate.invoked()).isTrue();
79 | }
80 |
81 | @Test
82 | public void multiplePredicatesSuccessWorks() {
83 | TestPredicate truePredicate = new TestPredicate(true);
84 | TestPredicate truePredicate2 = new TestPredicate(true);
85 | Mono result = runFilter(truePredicate, truePredicate2);
86 | StepVerifier.create(result).expectNext(Success.INSTANCE).verifyComplete();
87 | assertThat(truePredicate.invoked()).isTrue();
88 | assertThat(truePredicate2.invoked()).isTrue();
89 | }
90 |
91 | private Mono runFilter(SocketAcceptorPredicate predicate) {
92 | return runFilter(Collections.singletonList(predicate));
93 | }
94 |
95 | private Mono runFilter(SocketAcceptorPredicate... predicates) {
96 | return runFilter(Arrays.asList(predicates));
97 | }
98 |
99 | private Mono runFilter(List predicates) {
100 | SocketAcceptorPredicateFilter filter = new SocketAcceptorPredicateFilter(
101 | predicates);
102 | SocketAcceptorExchange exchange = new SocketAcceptorExchange(
103 | mock(ConnectionSetupPayload.class), mock(RSocket.class));
104 | SocketAcceptorFilterChain filterChain = new SocketAcceptorFilterChain(
105 | Collections.singletonList(filter));
106 | return filter.filter(exchange, filterChain);
107 | }
108 |
109 | private class TestPredicate implements SocketAcceptorPredicate {
110 |
111 | private boolean invoked = false;
112 |
113 | private final Mono test;
114 |
115 | TestPredicate(boolean value) {
116 | test = Mono.just(value);
117 | }
118 |
119 | @Override
120 | public Publisher apply(SocketAcceptorExchange exchange) {
121 | invoked = true;
122 | return test;
123 | }
124 |
125 | public boolean invoked() {
126 | return invoked;
127 | }
128 |
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/java/org/springframework/cloud/gateway/rsocket/test/SocketAcceptorFilterOrderTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.test;
18 |
19 | import java.util.Arrays;
20 | import java.util.Collections;
21 | import java.util.List;
22 |
23 | import org.junit.Test;
24 |
25 | import org.springframework.cloud.gateway.rsocket.routing.RoutingTable;
26 | import org.springframework.cloud.gateway.rsocket.routing.RoutingTableSocketAcceptorFilter;
27 | import org.springframework.cloud.gateway.rsocket.socketacceptor.SocketAcceptorFilter;
28 | import org.springframework.cloud.gateway.rsocket.socketacceptor.SocketAcceptorPredicateFilter;
29 | import org.springframework.core.OrderComparator;
30 |
31 | import static org.assertj.core.api.Assertions.assertThat;
32 | import static org.mockito.Mockito.mock;
33 |
34 | public class SocketAcceptorFilterOrderTests {
35 |
36 | @Test
37 | public void predicateFilterAfterRegistryFilter() {
38 | SocketAcceptorFilter predicateFilter = new SocketAcceptorPredicateFilter(
39 | Collections.emptyList());
40 | SocketAcceptorFilter registryFilter = new RoutingTableSocketAcceptorFilter(
41 | mock(RoutingTable.class));
42 | List filters = Arrays.asList(predicateFilter,
43 | registryFilter);
44 | OrderComparator.sort(filters);
45 |
46 | assertThat(filters).containsExactly(registryFilter, predicateFilter);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-broker/src/test/resources/application.yml:
--------------------------------------------------------------------------------
1 |
2 | logging:
3 | level:
4 | # org.springframework.cloud.gateway.rsocket: DEBUG
5 | org.springframework.cloud.gateway.rsocket: TRACE
6 | org.springframework.messaging.handler.invocation.reactive: TRACE
7 |
8 | management:
9 | endpoints:
10 | web:
11 | exposure:
12 | include: '*'
13 | spring:
14 | cloud:
15 | gateway:
16 | rsocket:
17 | route-id: 1234
18 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
21 | 4.0.0
22 |
23 | org.springframework.cloud
24 | spring-cloud-rsocket
25 | 0.2.0.BUILD-SNAPSHOT
26 | ..
27 |
28 | org.springframework.cloud
29 | spring-cloud-rsocket-client
30 | Spring Cloud RSocket Client
31 | Spring Cloud RSocket Client
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-validation
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-rsocket
41 |
42 |
43 | org.springframework.cloud
44 | spring-cloud-rsocket-common
45 |
46 |
47 | org.springframework.boot
48 | spring-boot-configuration-processor
49 | true
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-starter-actuator
54 | test
55 |
56 |
57 | org.projectlombok
58 | lombok
59 | test
60 |
61 |
62 | org.springframework.boot
63 | spring-boot-starter-test
64 | test
65 |
66 |
67 | io.projectreactor
68 | reactor-test
69 | test
70 |
71 |
72 |
73 |
74 |
75 | org.apache.maven.plugins
76 | maven-compiler-plugin
77 |
78 |
79 | -parameters
80 |
81 |
82 |
83 |
84 |
85 | default-compile
86 | none
87 |
88 |
89 |
90 | default-testCompile
91 | none
92 |
93 |
94 | java-compile
95 | compile
96 |
97 | compile
98 |
99 |
100 |
101 | java-test-compile
102 | test-compile
103 |
104 | testCompile
105 |
106 |
107 |
108 |
109 |
110 | org.apache.maven.plugins
111 | maven-jar-plugin
112 | 3.1.0
113 |
114 |
115 |
116 | test-jar
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 | java8plus
126 |
127 | [1.8,2.0)
128 |
129 |
130 |
131 |
132 | org.apache.maven.plugins
133 | maven-compiler-plugin
134 |
135 |
136 | -parameters
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/main/java/org/springframework/cloud/gateway/rsocket/client/BrokerClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.client;
18 |
19 | import java.util.function.Consumer;
20 |
21 | import reactor.core.publisher.Mono;
22 |
23 | import org.springframework.cloud.gateway.rsocket.common.autoconfigure.Broker;
24 | import org.springframework.cloud.gateway.rsocket.common.metadata.Forwarding;
25 | import org.springframework.messaging.rsocket.RSocketRequester;
26 |
27 | public class BrokerClient {
28 |
29 | private final ClientProperties properties;
30 |
31 | private final RSocketRequester.Builder builder;
32 |
33 | public BrokerClient(ClientProperties properties, RSocketRequester.Builder builder) {
34 | this.properties = properties;
35 | this.builder = builder;
36 | }
37 |
38 | public ClientProperties getProperties() {
39 | return this.properties;
40 | }
41 |
42 | public RSocketRequester.Builder getRSocketRequesterBuilder() {
43 | return this.builder;
44 | }
45 |
46 | public Mono connect() {
47 | return connect(builder);
48 | }
49 |
50 | public Mono connect(RSocketRequester.Builder requesterBuilder) {
51 | Broker broker = properties.getBroker();
52 | switch (broker.getConnectionType()) {
53 | case WEBSOCKET:
54 | return requesterBuilder.connectWebSocket(broker.getWsUri());
55 | }
56 | return requesterBuilder.connectTcp(broker.getHost(), broker.getPort());
57 | }
58 |
59 | public Consumer> forwarding(String destServiceName) {
60 | return spec -> {
61 | Forwarding forwarding = Forwarding.of(properties.getRouteId())
62 | .serviceName(destServiceName).build();
63 | spec.metadata(forwarding, Forwarding.FORWARDING_MIME_TYPE);
64 | };
65 | }
66 |
67 | public Consumer> forwarding(
68 | Consumer builderConsumer) {
69 | return spec -> {
70 | Forwarding.Builder builder = Forwarding.of(properties.getRouteId());
71 | builderConsumer.accept(builder);
72 | spec.metadata(builder.build(), Forwarding.FORWARDING_MIME_TYPE);
73 | };
74 | }
75 |
76 | }
77 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/main/java/org/springframework/cloud/gateway/rsocket/client/BrokerClientConnectionListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.client;
18 |
19 | import java.util.function.Consumer;
20 |
21 | import org.springframework.boot.context.event.ApplicationReadyEvent;
22 | import org.springframework.context.ApplicationEventPublisher;
23 | import org.springframework.context.ApplicationListener;
24 | import org.springframework.context.PayloadApplicationEvent;
25 | import org.springframework.core.Ordered;
26 | import org.springframework.core.ResolvableType;
27 | import org.springframework.messaging.rsocket.RSocketRequester;
28 |
29 | /**
30 | * Automatically subscribes to {@link BrokerClient}. On subscribe it publishes a
31 | * {@link PayloadApplicationEvent} with a generic type of {@link RSocketRequester}.
32 | */
33 | public class BrokerClientConnectionListener
34 | implements ApplicationListener, Ordered {
35 |
36 | private final BrokerClient brokerClient;
37 |
38 | private final ApplicationEventPublisher publisher;
39 |
40 | public BrokerClientConnectionListener(BrokerClient brokerClient,
41 | ApplicationEventPublisher publisher) {
42 | this.brokerClient = brokerClient;
43 | this.publisher = publisher;
44 | }
45 |
46 | @Override
47 | public void onApplicationEvent(ApplicationReadyEvent event) {
48 | // TODO: is there a better event the just RSocketRequester?
49 | // TODO: save Disposable?
50 | this.brokerClient.connect().subscribe(publishEvent());
51 | }
52 |
53 | private Consumer publishEvent() {
54 | return requester -> publisher.publishEvent(new RSocketRequesterEvent<>(
55 | BrokerClientConnectionListener.this, requester));
56 | }
57 |
58 | @Override
59 | public int getOrder() {
60 | return Ordered.HIGHEST_PRECEDENCE; // TODO: configurable
61 | }
62 |
63 | private static final class RSocketRequesterEvent
64 | extends PayloadApplicationEvent {
65 |
66 | private RSocketRequesterEvent(Object source, T payload) {
67 | super(source, payload);
68 | }
69 |
70 | @Override
71 | public ResolvableType getResolvableType() {
72 | return ResolvableType.forClassWithGenerics(getClass(),
73 | ResolvableType.forClass(RSocketRequester.class));
74 | }
75 |
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/main/java/org/springframework/cloud/gateway/rsocket/client/ClientProperties.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.client;
18 |
19 | import java.math.BigInteger;
20 | import java.util.LinkedHashMap;
21 | import java.util.Map;
22 | import java.util.Objects;
23 | import java.util.StringJoiner;
24 |
25 | import javax.validation.Valid;
26 | import javax.validation.constraints.NotEmpty;
27 | import javax.validation.constraints.NotNull;
28 |
29 | import org.springframework.boot.context.properties.ConfigurationProperties;
30 | import org.springframework.boot.context.properties.NestedConfigurationProperty;
31 | import org.springframework.cloud.gateway.rsocket.common.autoconfigure.Broker;
32 | import org.springframework.cloud.gateway.rsocket.common.metadata.WellKnownKey;
33 | import org.springframework.core.style.ToStringCreator;
34 | import org.springframework.util.StringUtils;
35 | import org.springframework.validation.annotation.Validated;
36 |
37 | @ConfigurationProperties("spring.cloud.gateway.rsocket.client")
38 | @Validated
39 | public class ClientProperties {
40 |
41 | @NotNull
42 | private BigInteger routeId;
43 |
44 | @NotEmpty
45 | private String serviceName;
46 |
47 | private Map tags = new LinkedHashMap<>();
48 |
49 | @Valid
50 | @NestedConfigurationProperty
51 | private Broker broker = new Broker();
52 |
53 | private Map> forwarding = new LinkedHashMap<>();
54 |
55 | public BigInteger getRouteId() {
56 | return this.routeId;
57 | }
58 |
59 | public void setRouteId(BigInteger routeId) {
60 | this.routeId = routeId;
61 | }
62 |
63 | public String getServiceName() {
64 | return this.serviceName;
65 | }
66 |
67 | public void setServiceName(String serviceName) {
68 | this.serviceName = serviceName;
69 | }
70 |
71 | public Map getTags() {
72 | return tags;
73 | }
74 |
75 | public Broker getBroker() {
76 | return this.broker;
77 | }
78 |
79 | public void setBroker(Broker broker) {
80 | this.broker = broker;
81 | }
82 |
83 | public Map> getForwarding() {
84 | return forwarding;
85 | }
86 |
87 | @Override
88 | public String toString() {
89 | // @formatter:off
90 | return new ToStringCreator(this)
91 | .append("routeId", routeId)
92 | .append("serviceName", serviceName)
93 | .append("tags", tags)
94 | .append("broker", broker)
95 | .append("forwarding", forwarding)
96 | .toString();
97 | // @formatter:on
98 | }
99 |
100 | public static class TagKey {
101 |
102 | private WellKnownKey wellKnownKey;
103 |
104 | private String customKey;
105 |
106 | public TagKey() {
107 | System.out.println("here");
108 | }
109 |
110 | public TagKey(String text) {
111 | if (!StringUtils.isEmpty(text)) {
112 | try {
113 | wellKnownKey = WellKnownKey.valueOf(text.toUpperCase());
114 | }
115 | catch (IllegalArgumentException e) {
116 | // NOT a valid well know key
117 | customKey = text;
118 | }
119 | }
120 | }
121 |
122 | public static TagKey of(WellKnownKey key) {
123 | TagKey tagKey = new TagKey();
124 | tagKey.setWellKnownKey(key);
125 | return tagKey;
126 | }
127 |
128 | public static TagKey of(String key) {
129 | return new TagKey(key);
130 | }
131 |
132 | public WellKnownKey getWellKnownKey() {
133 | return wellKnownKey;
134 | }
135 |
136 | public void setWellKnownKey(WellKnownKey wellKnownKey) {
137 | this.wellKnownKey = wellKnownKey;
138 | }
139 |
140 | public String getCustomKey() {
141 | return customKey;
142 | }
143 |
144 | public void setCustomKey(String customKey) {
145 | this.customKey = customKey;
146 | }
147 |
148 | @Override
149 | public boolean equals(Object o) {
150 | if (this == o) {
151 | return true;
152 | }
153 | if (o == null || getClass() != o.getClass()) {
154 | return false;
155 | }
156 | TagKey tag = (TagKey) o;
157 | return wellKnownKey == tag.wellKnownKey
158 | && Objects.equals(customKey, tag.customKey);
159 | }
160 |
161 | @Override
162 | public int hashCode() {
163 | return Objects.hash(wellKnownKey, customKey);
164 | }
165 |
166 | @Override
167 | public String toString() {
168 | StringJoiner joiner = new StringJoiner(", ", "[", "]");
169 | if (wellKnownKey != null) {
170 | joiner.add(wellKnownKey.name());
171 | }
172 | if (customKey != null) {
173 | joiner.add("'" + customKey + "'");
174 | }
175 | return joiner.toString();
176 | }
177 |
178 | }
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/main/java/org/springframework/cloud/gateway/rsocket/client/ClientRSocketRequesterBuilder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.client;
18 |
19 | import java.net.URI;
20 | import java.util.function.Consumer;
21 |
22 | import io.rsocket.transport.ClientTransport;
23 | import io.rsocket.transport.netty.client.TcpClientTransport;
24 | import io.rsocket.transport.netty.client.WebsocketClientTransport;
25 | import reactor.core.publisher.Mono;
26 |
27 | import org.springframework.messaging.rsocket.ClientRSocketFactoryConfigurer;
28 | import org.springframework.messaging.rsocket.RSocketRequester;
29 | import org.springframework.messaging.rsocket.RSocketStrategies;
30 | import org.springframework.util.MimeType;
31 | import org.springframework.util.RouteMatcher;
32 |
33 | final class ClientRSocketRequesterBuilder implements RSocketRequester.Builder {
34 |
35 | private final RSocketRequester.Builder delegate;
36 |
37 | private final ClientProperties properties;
38 |
39 | private final RouteMatcher routeMatcher;
40 |
41 | ClientRSocketRequesterBuilder(RSocketRequester.Builder delegate,
42 | ClientProperties properties, RouteMatcher routeMatcher) {
43 | this.delegate = delegate;
44 | this.properties = properties;
45 | this.routeMatcher = routeMatcher;
46 | }
47 |
48 | @Override
49 | public RSocketRequester.Builder dataMimeType(MimeType mimeType) {
50 | return delegate.dataMimeType(mimeType);
51 | }
52 |
53 | @Override
54 | public RSocketRequester.Builder metadataMimeType(MimeType mimeType) {
55 | return delegate.metadataMimeType(mimeType);
56 | }
57 |
58 | @Override
59 | public RSocketRequester.Builder setupData(Object data) {
60 | return delegate.setupData(data);
61 | }
62 |
63 | @Override
64 | public RSocketRequester.Builder setupRoute(String route, Object... routeVars) {
65 | return delegate.setupRoute(route, routeVars);
66 | }
67 |
68 | @Override
69 | public RSocketRequester.Builder setupMetadata(Object value, MimeType mimeType) {
70 | return delegate.setupMetadata(value, mimeType);
71 | }
72 |
73 | @Override
74 | public RSocketRequester.Builder rsocketStrategies(RSocketStrategies strategies) {
75 | return delegate.rsocketStrategies(strategies);
76 | }
77 |
78 | @Override
79 | public RSocketRequester.Builder rsocketStrategies(
80 | Consumer configurer) {
81 | return delegate.rsocketStrategies(configurer);
82 | }
83 |
84 | @Override
85 | public RSocketRequester.Builder rsocketFactory(
86 | ClientRSocketFactoryConfigurer configurer) {
87 | return delegate.rsocketFactory(configurer);
88 | }
89 |
90 | @Override
91 | public RSocketRequester.Builder apply(Consumer configurer) {
92 | return delegate.apply(configurer);
93 | }
94 |
95 | @Override
96 | public Mono connectTcp(String host, int port) {
97 | return connect(TcpClientTransport.create(host, port));
98 | }
99 |
100 | @Override
101 | public Mono connectWebSocket(URI uri) {
102 | return connect(WebsocketClientTransport.create(uri));
103 | }
104 |
105 | @Override
106 | public Mono connect(ClientTransport transport) {
107 | return delegate.connect(transport)
108 | .map(requester -> new ClientRSocketRequester(requester, properties,
109 | routeMatcher));
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/main/java/org/springframework/cloud/gateway/rsocket/client/GatewayRSocketClientAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.client;
18 |
19 | import java.math.BigInteger;
20 | import java.util.function.Supplier;
21 |
22 | import io.micrometer.core.instrument.MeterRegistry;
23 | import io.micrometer.core.instrument.Tag;
24 | import io.rsocket.RSocket;
25 | import io.rsocket.micrometer.MicrometerRSocketInterceptor;
26 | import io.rsocket.plugins.RSocketInterceptor;
27 |
28 | import org.springframework.beans.factory.annotation.Qualifier;
29 | import org.springframework.boot.autoconfigure.AutoConfigureAfter;
30 | import org.springframework.boot.autoconfigure.AutoConfigureBefore;
31 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
32 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
33 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
34 | import org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration;
35 | import org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration;
36 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
37 | import org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetup;
38 | import org.springframework.context.ApplicationEventPublisher;
39 | import org.springframework.context.annotation.Bean;
40 | import org.springframework.context.annotation.Configuration;
41 | import org.springframework.context.annotation.Scope;
42 | import org.springframework.messaging.rsocket.ClientRSocketFactoryConfigurer;
43 | import org.springframework.messaging.rsocket.RSocketRequester;
44 | import org.springframework.messaging.rsocket.RSocketStrategies;
45 | import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
46 |
47 | import static org.springframework.cloud.gateway.rsocket.common.autoconfigure.GatewayRSocketCommonAutoConfiguration.ID_GENERATOR_BEAN_NAME;
48 |
49 | /**
50 | * @author Spencer Gibb
51 | */
52 | @Configuration
53 | // TODO: add this property to config metadata
54 | @ConditionalOnProperty(name = "spring.cloud.gateway.rsocket.enabled",
55 | matchIfMissing = true)
56 | @EnableConfigurationProperties
57 | @ConditionalOnClass({ RSocket.class, RSocketRequester.class })
58 | @AutoConfigureAfter(RSocketStrategiesAutoConfiguration.class)
59 | @AutoConfigureBefore(RSocketRequesterAutoConfiguration.class)
60 | public class GatewayRSocketClientAutoConfiguration {
61 |
62 | private final RSocketMessageHandler messageHandler;
63 |
64 | public GatewayRSocketClientAutoConfiguration(RSocketMessageHandler handler) {
65 | messageHandler = handler;
66 | }
67 |
68 | @Bean
69 | @Scope("prototype") // TODO: I don't think prototype works here
70 | @ConditionalOnMissingBean
71 | public RSocketRequester.Builder gatewayRSocketRequesterBuilder(
72 | RSocketStrategies strategies, ClientProperties properties,
73 | MeterRegistry meterRegistry) {
74 | RouteSetup.Builder routeSetup = RouteSetup.of(properties.getRouteId(),
75 | properties.getServiceName());
76 | properties.getTags().forEach((key, value) -> {
77 | if (key.getWellKnownKey() != null) {
78 | routeSetup.with(key.getWellKnownKey(), value);
79 | }
80 | else if (key.getCustomKey() != null) {
81 | routeSetup.with(key.getCustomKey(), value);
82 | }
83 | });
84 |
85 | MicrometerRSocketInterceptor interceptor = new MicrometerRSocketInterceptor(
86 | meterRegistry, Tag.of("servicename", properties.getServiceName()));
87 |
88 | RSocketRequester.Builder builder = RSocketRequester.builder()
89 | .setupMetadata(routeSetup.build(), RouteSetup.ROUTE_SETUP_MIME_TYPE)
90 | .rsocketStrategies(strategies).rsocketFactory(configurer(interceptor));
91 |
92 | return new ClientRSocketRequesterBuilder(builder, properties,
93 | strategies.routeMatcher());
94 | }
95 |
96 | private ClientRSocketFactoryConfigurer configurer(RSocketInterceptor interceptor) {
97 | return rsocketFactory -> rsocketFactory.addRequesterPlugin(interceptor)
98 | .acceptor(messageHandler.responder());
99 | }
100 |
101 | @Bean
102 | public BrokerClient brokerClient(RSocketRequester.Builder builder,
103 | ClientProperties properties) {
104 | return new BrokerClient(properties, builder);
105 | }
106 |
107 | @Bean
108 | @ConditionalOnProperty(name = "spring.cloud.gateway.rsocket.client.auto-connect",
109 | matchIfMissing = true)
110 | public BrokerClientConnectionListener brokerClientConnectionListener(
111 | BrokerClient client, ApplicationEventPublisher publisher) {
112 | return new BrokerClientConnectionListener(client, publisher);
113 | }
114 |
115 | @Bean
116 | public ClientProperties clientProperties(
117 | @Qualifier(ID_GENERATOR_BEAN_NAME) Supplier idGenerator) {
118 | ClientProperties clientProperties = new ClientProperties();
119 | clientProperties.setRouteId(idGenerator.get());
120 | return clientProperties;
121 | }
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | # Auto Configure
2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
3 | org.springframework.cloud.gateway.rsocket.client.GatewayRSocketClientAutoConfiguration
4 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/test/java/org/springframework/cloud/gateway/rsocket/client/ClientPropertiesTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.client;
18 |
19 | import java.util.Map;
20 |
21 | import org.junit.Test;
22 | import org.junit.runner.RunWith;
23 |
24 | import org.springframework.beans.factory.annotation.Autowired;
25 | import org.springframework.boot.SpringBootConfiguration;
26 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
27 | import org.springframework.boot.test.context.SpringBootTest;
28 | import org.springframework.cloud.gateway.rsocket.client.ClientProperties.TagKey;
29 | import org.springframework.test.context.junit4.SpringRunner;
30 |
31 | import static org.assertj.core.api.Assertions.assertThat;
32 | import static org.assertj.core.api.Assertions.entry;
33 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
34 |
35 | @RunWith(SpringRunner.class)
36 | @SpringBootTest(webEnvironment = RANDOM_PORT,
37 | properties = "spring.cloud.gateway.rsocket.client.auto-connect=false")
38 | public class ClientPropertiesTests {
39 |
40 | @Autowired
41 | ClientProperties properties;
42 |
43 | @Test
44 | public void clientProperties() {
45 | assertThat(properties).isNotNull();
46 | assertThat(properties.getRouteId()).isEqualTo(11L);
47 | assertThat(properties.getServiceName()).isEqualTo("test_requester");
48 | assertThat(properties.getTags()).containsEntry(TagKey.of("INSTANCE_NAME"),
49 | "test_requester1");
50 | assertThat(properties.getForwarding()).containsKeys("test_responder-rc",
51 | "key.with.dots", "key.with.{replacement}");
52 | Map map = properties.getForwarding().get("test_responder-rc");
53 | assertThat(map).contains(entry(TagKey.of("SERVICE_NAME"), "test_responder"),
54 | entry(TagKey.of("custom-tag"), "custom-value"));
55 | assertThat(properties.getBroker()).isNotNull().extracting("host", "port")
56 | .containsExactly("localhost", 7002);
57 | }
58 |
59 | @SpringBootConfiguration
60 | @EnableAutoConfiguration
61 | static class Config {
62 |
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/test/java/org/springframework/cloud/gateway/rsocket/client/ClientRSocketRequesterTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.client;
18 |
19 | import java.math.BigInteger;
20 | import java.util.Collections;
21 | import java.util.HashMap;
22 | import java.util.LinkedHashMap;
23 | import java.util.Map;
24 |
25 | import org.junit.jupiter.api.Test;
26 |
27 | import org.springframework.cloud.gateway.rsocket.client.ClientProperties.TagKey;
28 | import org.springframework.cloud.gateway.rsocket.common.metadata.Forwarding;
29 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata.Key;
30 | import org.springframework.util.AntPathMatcher;
31 | import org.springframework.util.RouteMatcher;
32 | import org.springframework.util.SimpleRouteMatcher;
33 |
34 | import static org.assertj.core.api.Assertions.assertThat;
35 | import static org.springframework.cloud.gateway.rsocket.client.ClientRSocketRequester.expand;
36 | import static org.springframework.cloud.gateway.rsocket.client.ClientRSocketRequester.forwarding;
37 | import static org.springframework.cloud.gateway.rsocket.common.metadata.WellKnownKey.ROUTE_ID;
38 | import static org.springframework.cloud.gateway.rsocket.common.metadata.WellKnownKey.SERVICE_NAME;
39 |
40 | public class ClientRSocketRequesterTests {
41 |
42 | @Test
43 | public void forwardingWorks() {
44 | RouteMatcher routeMatcher = new SimpleRouteMatcher(new AntPathMatcher("."));
45 | RouteMatcher.Route route = routeMatcher.parseRoute("myroute.foo1.bar1");
46 | LinkedHashMap tags = new LinkedHashMap<>();
47 | tags.put(TagKey.of(SERVICE_NAME), "{foo}");
48 | tags.put(TagKey.of(ROUTE_ID), "22");
49 | tags.put(TagKey.of("mycustomkey"), "{foo}-{bar}");
50 | Forwarding fwd = (Forwarding) forwarding(routeMatcher, route,
51 | new BigInteger("11"), "myroute.{foo}.{bar}", tags).build();
52 |
53 | assertThat(fwd).isNotNull();
54 | assertThat(fwd.getEnrichedTagsMetadata().getTags()).isNotEmpty()
55 | .containsEntry(new Key(SERVICE_NAME), "foo1")
56 | .containsEntry(new Key(ROUTE_ID), "22")
57 | .containsEntry(new Key("mycustomkey"), "foo1-bar1");
58 | }
59 |
60 | @Test
61 | public void expandArrayVars() {
62 | String result = expand("myroute.{foo}.{bar}", "foo1", "bar1");
63 | assertThat(result).isEqualTo("myroute.foo1.bar1");
64 | }
65 |
66 | @Test
67 | public void expandMapVars() {
68 | HashMap map = new HashMap<>();
69 | map.put("value", "a+b");
70 | map.put("city", "Z\u00fcrich");
71 | String result = expand("/hotel list/{city} specials/{value}", map);
72 |
73 | assertThat(result).isEqualTo("/hotel list/Z\u00fcrich specials/a+b");
74 | }
75 |
76 | @Test
77 | public void expandPartially() {
78 | HashMap map = new HashMap<>();
79 | map.put("city", "Z\u00fcrich");
80 | String result = expand("/hotel list/{city} specials/{value}", map);
81 |
82 | assertThat(result).isEqualTo("/hotel list/Zürich specials/");
83 | }
84 |
85 | @Test
86 | public void expandSimple() {
87 | HashMap map = new HashMap<>();
88 | map.put("foo", "1 2");
89 | map.put("bar", "3 4");
90 | String result = expand("/{foo} {bar}", map);
91 | assertThat(result).isEqualTo("/1 2 3 4");
92 | }
93 |
94 | @Test // SPR-13311
95 | public void expandWithRegexVar() {
96 | String template = "/myurl/{name:[a-z]{1,5}}/show";
97 | Map map = Collections.singletonMap("name", "test");
98 | String result = expand(template, map);
99 | assertThat(result).isEqualTo("/myurl/test/show");
100 | }
101 |
102 | @Test // SPR-17630
103 | public void expandWithMismatchedCurlyBraces() {
104 | String result = expand("/myurl/{{{{", Collections.emptyMap());
105 | assertThat(result).isEqualTo("/myurl/{{{{");
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-client/src/test/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring.cloud.gateway.rsocket.client:
2 | route-id: 11
3 | service-name: test_requester
4 | tags:
5 | INSTANCE_NAME: test_requester1
6 | forwarding:
7 | test_responder-rc:
8 | service_name: test_responder
9 | custom-tag: custom-value
10 | "[key.with.dots]":
11 | service_name: service_with_dots
12 | "[key.with.{replacement}]":
13 | service_name: service_with_replacement
14 | broker:
15 | host: localhost
16 | port: 7002
17 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
18 |
19 |
21 | 4.0.0
22 |
23 | org.springframework.cloud
24 | spring-cloud-rsocket
25 | 0.2.0.BUILD-SNAPSHOT
26 | ..
27 |
28 | org.springframework.cloud
29 | spring-cloud-rsocket-common
30 | Spring Cloud RSocket Common
31 | Spring Cloud RSocket Common
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-rsocket
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-validation
41 |
42 |
43 | org.springframework.boot
44 | spring-boot-configuration-processor
45 | true
46 |
47 |
48 | io.rsocket
49 | rsocket-core
50 |
51 |
52 | io.rsocket
53 | rsocket-micrometer
54 |
55 |
56 | io.rsocket
57 | rsocket-transport-netty
58 |
59 |
60 | io.micrometer
61 | micrometer-core
62 |
63 |
64 | org.roaringbitmap
65 | RoaringBitmap
66 |
67 |
68 | org.springframework.boot
69 | spring-boot-starter-actuator
70 | test
71 |
72 |
73 | org.projectlombok
74 | lombok
75 | test
76 |
77 |
78 | org.springframework.boot
79 | spring-boot-starter-test
80 | test
81 |
82 |
83 | io.projectreactor
84 | reactor-test
85 | test
86 |
87 |
88 |
89 |
90 |
91 | org.apache.maven.plugins
92 | maven-compiler-plugin
93 |
94 |
95 | -parameters
96 |
97 |
98 |
99 |
100 |
101 | default-compile
102 | none
103 |
104 |
105 |
106 | default-testCompile
107 | none
108 |
109 |
110 | java-compile
111 | compile
112 |
113 | compile
114 |
115 |
116 |
117 | java-test-compile
118 | test-compile
119 |
120 | testCompile
121 |
122 |
123 |
124 |
125 |
126 | org.apache.maven.plugins
127 | maven-jar-plugin
128 | 3.1.0
129 |
130 |
131 |
132 | test-jar
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | java8plus
142 |
143 | [1.8,2.0)
144 |
145 |
146 |
147 |
148 | org.apache.maven.plugins
149 | maven-compiler-plugin
150 |
151 |
152 | -parameters
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/main/java/org/springframework/cloud/gateway/rsocket/common/autoconfigure/Broker.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.autoconfigure;
18 |
19 | import java.net.URI;
20 |
21 | import javax.validation.constraints.NotNull;
22 |
23 | import org.springframework.core.style.ToStringCreator;
24 |
25 | public class Broker {
26 |
27 | public enum ConnectionType {
28 |
29 | /** TCP RSocket connection. */
30 | TCP,
31 | /** WEBSOCKET RSocket connection. */
32 | WEBSOCKET
33 |
34 | }
35 |
36 | // FIXME: validate based on connectionType
37 | private String host;
38 |
39 | private int port;
40 |
41 | @NotNull
42 | private ConnectionType connectionType = ConnectionType.TCP;
43 |
44 | private URI wsUri;
45 |
46 | public String getHost() {
47 | return this.host;
48 | }
49 |
50 | public void setHost(String host) {
51 | this.host = host;
52 | }
53 |
54 | public int getPort() {
55 | return this.port;
56 | }
57 |
58 | public void setPort(int port) {
59 | this.port = port;
60 | }
61 |
62 | public ConnectionType getConnectionType() {
63 | return this.connectionType;
64 | }
65 |
66 | public void setConnectionType(ConnectionType connectionType) {
67 | this.connectionType = connectionType;
68 | }
69 |
70 | public URI getWsUri() {
71 | return this.wsUri;
72 | }
73 |
74 | public void setWsUri(URI wsUri) {
75 | this.wsUri = wsUri;
76 | }
77 |
78 | @Override
79 | public String toString() {
80 | // @formatter:off
81 | return new ToStringCreator(this)
82 | .append("host", host)
83 | .append("port", port)
84 | .append("wsUri", wsUri)
85 | .append("connectionType", connectionType)
86 | .toString();
87 | // @formatter:on
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/main/java/org/springframework/cloud/gateway/rsocket/common/autoconfigure/GatewayRSocketCommonAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.autoconfigure;
18 |
19 | import java.math.BigInteger;
20 | import java.security.SecureRandom;
21 | import java.util.function.Supplier;
22 |
23 | import io.rsocket.RSocket;
24 |
25 | import org.springframework.boot.autoconfigure.AutoConfigureBefore;
26 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
27 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
28 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
29 | import org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration;
30 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
31 | import org.springframework.boot.rsocket.messaging.RSocketStrategiesCustomizer;
32 | import org.springframework.cloud.gateway.rsocket.common.metadata.Forwarding;
33 | import org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetup;
34 | import org.springframework.context.annotation.Bean;
35 | import org.springframework.context.annotation.Configuration;
36 |
37 | /**
38 | * @author Spencer Gibb
39 | */
40 | @Configuration
41 | @ConditionalOnProperty(name = "spring.cloud.gateway.rsocket.enabled",
42 | matchIfMissing = true)
43 | @EnableConfigurationProperties
44 | @ConditionalOnClass(RSocket.class)
45 | @AutoConfigureBefore(RSocketStrategiesAutoConfiguration.class)
46 | public class GatewayRSocketCommonAutoConfiguration {
47 |
48 | /**
49 | * Name of id generator bean.
50 | */
51 | public static final String ID_GENERATOR_BEAN_NAME = "gatewayRSocketIdGenerator";
52 |
53 | private final SecureRandom secureRandom = new SecureRandom();
54 |
55 | @Bean
56 | public RSocketStrategiesCustomizer gatewayRSocketStrategiesCustomizer() {
57 | return strategies -> {
58 | strategies.decoder(new Forwarding.Decoder(), new RouteSetup.Decoder())
59 | .encoder(new Forwarding.Encoder(), new RouteSetup.Encoder());
60 | };
61 | }
62 |
63 | @Bean(name = ID_GENERATOR_BEAN_NAME)
64 | @ConditionalOnMissingBean(name = ID_GENERATOR_BEAN_NAME)
65 | public Supplier gatewayRSocketIdGenerator() {
66 | return () -> {
67 | byte[] bytes = new byte[16];
68 | secureRandom.nextBytes(bytes);
69 | return new BigInteger(bytes);
70 | };
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/main/java/org/springframework/cloud/gateway/rsocket/common/autoconfigure/GatewayRSocketCommonMetadataAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.autoconfigure;
18 |
19 | import io.rsocket.RSocket;
20 |
21 | import org.springframework.beans.factory.InitializingBean;
22 | import org.springframework.boot.autoconfigure.AutoConfigureAfter;
23 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
24 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
25 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
26 | import org.springframework.cloud.gateway.rsocket.common.metadata.Forwarding;
27 | import org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetup;
28 | import org.springframework.context.ApplicationContext;
29 | import org.springframework.context.annotation.Configuration;
30 | import org.springframework.messaging.rsocket.DefaultMetadataExtractor;
31 | import org.springframework.messaging.rsocket.MetadataExtractor;
32 | import org.springframework.messaging.rsocket.RSocketStrategies;
33 |
34 | import static org.springframework.cloud.gateway.rsocket.common.metadata.Forwarding.FORWARDING_MIME_TYPE;
35 | import static org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetup.ROUTE_SETUP_MIME_TYPE;
36 |
37 | /**
38 | * @author Spencer Gibb
39 | */
40 | @Configuration
41 | @ConditionalOnProperty(name = "spring.cloud.gateway.rsocket.enabled",
42 | matchIfMissing = true)
43 | @EnableConfigurationProperties
44 | @ConditionalOnClass(RSocket.class)
45 | @AutoConfigureAfter({ GatewayRSocketCommonAutoConfiguration.class })
46 | public class GatewayRSocketCommonMetadataAutoConfiguration implements InitializingBean {
47 |
48 | private final ApplicationContext context;
49 |
50 | public GatewayRSocketCommonMetadataAutoConfiguration(ApplicationContext context) {
51 | this.context = context;
52 | }
53 |
54 | @Override
55 | public void afterPropertiesSet() {
56 | RSocketStrategies rSocketStrategies = this.context
57 | .getBean(RSocketStrategies.class);
58 | MetadataExtractor metadataExtractor = rSocketStrategies.metadataExtractor();
59 | // TODO: see if possible to make easier in framework.
60 | if (metadataExtractor instanceof DefaultMetadataExtractor) {
61 | DefaultMetadataExtractor extractor = (DefaultMetadataExtractor) metadataExtractor;
62 | extractor.metadataToExtract(FORWARDING_MIME_TYPE, Forwarding.class,
63 | Forwarding.METADATA_KEY);
64 | extractor.metadataToExtract(ROUTE_SETUP_MIME_TYPE, RouteSetup.class,
65 | RouteSetup.METADATA_KEY);
66 | }
67 | }
68 |
69 | }
70 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/main/java/org/springframework/cloud/gateway/rsocket/common/metadata/Metadata.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.metadata;
18 |
19 | import io.rsocket.metadata.WellKnownMimeType;
20 |
21 | import org.springframework.util.MimeType;
22 |
23 | public abstract class Metadata {
24 |
25 | /**
26 | * Composite Metadata MimeType.
27 | */
28 | public static final MimeType COMPOSITE_MIME_TYPE = MimeType
29 | .valueOf(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.toString());
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/main/java/org/springframework/cloud/gateway/rsocket/common/metadata/WellKnownKey.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.metadata;
18 |
19 | import java.util.Arrays;
20 | import java.util.HashMap;
21 | import java.util.Map;
22 |
23 | public enum WellKnownKey {
24 |
25 | // CHECKSTYLE:OFF
26 | // @formatter:off
27 | UNPARSEABLE_KEY("UNPARSEABLE_KEY_DO_NOT_USE", (byte) -2),
28 | UNKNOWN_RESERVED_KEY("UNKNOWN_YET_RESERVED_DO_NOT_USE", (byte) -1),
29 |
30 | NO_TAG("NO_TAG_DO_NOT_USE", (byte) 0x00),
31 | SERVICE_NAME("io.rsocket.routing.ServiceName", (byte) 0x01),
32 | ROUTE_ID("io.rsocket.routing.RouteId", (byte) 0x02),
33 | INSTANCE_NAME("io.rsocket.routing.InstanceName", (byte) 0x03),
34 | CLUSTER_NAME("io.rsocket.routing.ClusterName", (byte) 0x04),
35 | PROVIDER("io.rsocket.routing.Provider", (byte) 0x05),
36 | REGION("io.rsocket.routing.Region", (byte) 0x06),
37 | ZONE("io.rsocket.routing.Zone", (byte) 0x07),
38 | DEVICE("io.rsocket.routing.Device", (byte) 0x08),
39 | OS("io.rsocket.routing.OS", (byte) 0x09),
40 | USER_NAME("io.rsocket.routing.UserName", (byte) 0x0A),
41 | USER_ID("io.rsocket.routing.UserId", (byte) 0x0B),
42 | MAJOR_VERSION("io.rsocket.routing.MajorVersion", (byte) 0x0C),
43 | MINOR_VERSION("io.rsocket.routing.MinorVersion", (byte) 0x0D),
44 | PATCH_VERSION("io.rsocket.routing.PatchVersion", (byte) 0x0E),
45 | VERSION("io.rsocket.routing.Version", (byte) 0x0F),
46 | ENVIRONMENT("io.rsocket.routing.Environment", (byte) 0x10),
47 | TESTC_ELL("io.rsocket.routing.TestCell", (byte) 0x11),
48 | DNS("io.rsocket.routing.DNS", (byte) 0x12),
49 | IPV4("io.rsocket.routing.IPv4", (byte) 0x13),
50 | IPV6("io.rsocket.routing.IPv6", (byte) 0x14),
51 | COUNTRY("io.rsocket.routing.Country", (byte) 0x15),
52 | TIME_ZONE("io.rsocket.routing.TimeZone", (byte) 0x1A),
53 | SHARD_KEY("io.rsocket.routing.ShardKey", (byte) 0x1B),
54 | SHARD_METHOD("io.rsocket.routing.ShardMethod", (byte) 0x1C),
55 | STICKY_ROUTE_KEY("io.rsocket.routing.StickyRouteKey", (byte) 0x1D),
56 | LB_METHOD("io.rsocket.routing.LBMethod", (byte) 0x1E),
57 | BROKER_EXTENSION("Broker Implementation Extension Key", (byte) 0x1E),
58 | WELL_KNOWN_EXTENSION("Well Known Extension Key", (byte) 0x1E);
59 | // @formatter:on
60 | // CHECKSTYLE:ON
61 |
62 | static final WellKnownKey[] TYPES_BY_ID;
63 | static final Map TYPES_BY_STRING;
64 |
65 | static {
66 | // precompute an array of all valid mime ids,
67 | // filling the blanks with the RESERVED enum
68 | TYPES_BY_ID = new WellKnownKey[128]; // 0-127 inclusive
69 | Arrays.fill(TYPES_BY_ID, UNKNOWN_RESERVED_KEY);
70 | // also prepare a Map of the types by key string
71 | TYPES_BY_STRING = new HashMap<>(128);
72 |
73 | for (WellKnownKey value : values()) {
74 | if (value.getIdentifier() >= 0) {
75 | TYPES_BY_ID[value.getIdentifier()] = value;
76 | TYPES_BY_STRING.put(value.getString(), value);
77 | }
78 | }
79 | }
80 |
81 | private final byte identifier;
82 |
83 | private final String str;
84 |
85 | WellKnownKey(String str, byte identifier) {
86 | this.str = str;
87 | this.identifier = identifier;
88 | }
89 |
90 | public static WellKnownKey fromIdentifier(int id) {
91 | if (id < 0x00 || id > 0x7F) {
92 | return UNPARSEABLE_KEY;
93 | }
94 | return TYPES_BY_ID[id];
95 | }
96 |
97 | public static WellKnownKey fromMimeType(String mimeType) {
98 | if (mimeType == null) {
99 | throw new IllegalArgumentException("type must be non-null");
100 | }
101 |
102 | // force UNPARSEABLE if by chance UNKNOWN_RESERVED_MIME_TYPE's text has been used
103 | if (mimeType.equals(UNKNOWN_RESERVED_KEY.str)) {
104 | return UNPARSEABLE_KEY;
105 | }
106 |
107 | return TYPES_BY_STRING.getOrDefault(mimeType, UNPARSEABLE_KEY);
108 | }
109 |
110 | /**
111 | * @return the byte identifier of the mime type, guaranteed to be positive or zero.
112 | */
113 | public byte getIdentifier() {
114 | return identifier;
115 | }
116 |
117 | /**
118 | * @return the mime type represented as a {@link String}, which is made of US_ASCII
119 | * compatible characters only
120 | */
121 | public String getString() {
122 | return str;
123 | }
124 |
125 | /** @see #getString() */
126 | @Override
127 | public String toString() {
128 | return str;
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/main/resources/META-INF/spring.factories:
--------------------------------------------------------------------------------
1 | # Auto Configure
2 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
3 | org.springframework.cloud.gateway.rsocket.common.autoconfigure.GatewayRSocketCommonAutoConfiguration,\
4 | org.springframework.cloud.gateway.rsocket.common.autoconfigure.GatewayRSocketCommonMetadataAutoConfiguration
5 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/test/java/org/springframework/cloud/gateway/rsocket/common/metadata/ForwardingIntegrationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.metadata;
18 |
19 | import java.util.Map;
20 |
21 | import io.netty.buffer.ByteBuf;
22 | import io.netty.buffer.Unpooled;
23 | import io.rsocket.Payload;
24 | import io.rsocket.util.DefaultPayload;
25 | import org.junit.runner.RunWith;
26 |
27 | import org.springframework.beans.factory.annotation.Autowired;
28 | import org.springframework.boot.SpringBootConfiguration;
29 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
30 | import org.springframework.boot.test.context.SpringBootTest;
31 | import org.springframework.cloud.gateway.rsocket.common.test.MetadataEncoder;
32 | import org.springframework.core.io.buffer.DataBuffer;
33 | import org.springframework.messaging.rsocket.MetadataExtractor;
34 | import org.springframework.messaging.rsocket.RSocketStrategies;
35 | import org.springframework.test.context.junit4.SpringRunner;
36 |
37 | import static org.assertj.core.api.Assertions.assertThat;
38 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
39 |
40 | @RunWith(SpringRunner.class)
41 | @SpringBootTest(properties = "spring.rsocket.server.port=0", webEnvironment = RANDOM_PORT)
42 | public class ForwardingIntegrationTests extends ForwardingTests {
43 |
44 | @Autowired
45 | private RSocketStrategies strategies;
46 |
47 | @Override
48 | protected ByteBuf encode(Forwarding forwarding) {
49 | DataBuffer dataBuffer = new MetadataEncoder(Metadata.COMPOSITE_MIME_TYPE,
50 | strategies).metadata(forwarding, Forwarding.FORWARDING_MIME_TYPE)
51 | .encode();
52 | return TagsMetadata.asByteBuf(dataBuffer);
53 | }
54 |
55 | @Override
56 | protected Forwarding decode(ByteBuf byteBuf) {
57 | MetadataExtractor metadataExtractor = strategies.metadataExtractor();
58 | Payload payload = DefaultPayload.create(Unpooled.EMPTY_BUFFER, byteBuf);
59 | Map metadata = metadataExtractor.extract(payload,
60 | Metadata.COMPOSITE_MIME_TYPE);
61 | assertThat(metadata).containsKey(Forwarding.METADATA_KEY);
62 |
63 | return (Forwarding) metadata.get(Forwarding.METADATA_KEY);
64 | }
65 |
66 | @SpringBootConfiguration
67 | @EnableAutoConfiguration
68 | static class Config {
69 |
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/test/java/org/springframework/cloud/gateway/rsocket/common/metadata/ForwardingTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.metadata;
18 |
19 | import java.math.BigInteger;
20 | import java.util.LinkedHashMap;
21 |
22 | import io.netty.buffer.ByteBuf;
23 | import org.junit.Test;
24 |
25 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata.Key;
26 |
27 | import static org.assertj.core.api.Assertions.assertThat;
28 | import static org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetupTests.MAX_BIGINT;
29 | import static org.springframework.cloud.gateway.rsocket.common.metadata.RouteSetupTests.TWO_BYTE_BIGINT;
30 | import static org.springframework.cloud.gateway.rsocket.common.metadata.WellKnownKey.REGION;
31 |
32 | public class ForwardingTests {
33 |
34 | @Test
35 | public void encodeAndDecodeWorksMaxBigint() {
36 | ByteBuf byteBuf = createForwarding(MAX_BIGINT);
37 | assertForwarding(byteBuf, MAX_BIGINT);
38 | }
39 |
40 | @Test
41 | public void encodeAndDecodeWorksMinBigint() {
42 | ByteBuf byteBuf = createForwarding(BigInteger.ONE);
43 | assertForwarding(byteBuf, BigInteger.ONE);
44 | }
45 |
46 | @Test
47 | public void encodeAndDecodeWorksTwoBytes() {
48 | ByteBuf byteBuf = createForwarding(TWO_BYTE_BIGINT);
49 | assertForwarding(byteBuf, TWO_BYTE_BIGINT);
50 | }
51 |
52 | protected ByteBuf createForwarding(BigInteger originRouteId) {
53 | LinkedHashMap tags = new LinkedHashMap<>();
54 | Forwarding forwarding = Forwarding.of(originRouteId).with(REGION, "us-east-1")
55 | .build();
56 | return encode(forwarding);
57 | }
58 |
59 | protected ByteBuf encode(Forwarding forwarding) {
60 | return forwarding.encode();
61 | }
62 |
63 | protected void assertForwarding(ByteBuf byteBuf, BigInteger originRouteId) {
64 | Forwarding forwarding = decode(byteBuf);
65 | assertThat(forwarding).isNotNull();
66 | assertThat(forwarding.getOriginRouteId()).isEqualTo(originRouteId);
67 | assertThat(forwarding.getTags()).hasSize(1).containsOnlyKeys(new Key(REGION))
68 | .containsValues("us-east-1");
69 | }
70 |
71 | protected Forwarding decode(ByteBuf byteBuf) {
72 | return Forwarding.decodeForwarding(byteBuf);
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/test/java/org/springframework/cloud/gateway/rsocket/common/metadata/RouteSetupIntegrationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.metadata;
18 |
19 | import java.util.Map;
20 |
21 | import io.netty.buffer.ByteBuf;
22 | import io.netty.buffer.Unpooled;
23 | import io.rsocket.Payload;
24 | import io.rsocket.util.DefaultPayload;
25 | import org.junit.runner.RunWith;
26 |
27 | import org.springframework.beans.factory.annotation.Autowired;
28 | import org.springframework.boot.SpringBootConfiguration;
29 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
30 | import org.springframework.boot.test.context.SpringBootTest;
31 | import org.springframework.cloud.gateway.rsocket.common.test.MetadataEncoder;
32 | import org.springframework.core.io.buffer.DataBuffer;
33 | import org.springframework.messaging.rsocket.MetadataExtractor;
34 | import org.springframework.messaging.rsocket.RSocketStrategies;
35 | import org.springframework.test.context.junit4.SpringRunner;
36 |
37 | import static org.assertj.core.api.Assertions.assertThat;
38 | import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
39 |
40 | @RunWith(SpringRunner.class)
41 | @SpringBootTest(properties = "spring.rsocket.server.port=0", webEnvironment = RANDOM_PORT)
42 | public class RouteSetupIntegrationTests extends RouteSetupTests {
43 |
44 | @Autowired
45 | private RSocketStrategies strategies;
46 |
47 | @Override
48 | protected ByteBuf encode(RouteSetup routeSetup) {
49 | DataBuffer dataBuffer = new MetadataEncoder(Metadata.COMPOSITE_MIME_TYPE,
50 | strategies).metadata(routeSetup, RouteSetup.ROUTE_SETUP_MIME_TYPE)
51 | .encode();
52 | return TagsMetadata.asByteBuf(dataBuffer);
53 | }
54 |
55 | @Override
56 | protected RouteSetup decode(ByteBuf byteBuf) {
57 | MetadataExtractor metadataExtractor = strategies.metadataExtractor();
58 | Payload payload = DefaultPayload.create(Unpooled.EMPTY_BUFFER, byteBuf);
59 | Map metadata = metadataExtractor.extract(payload,
60 | Metadata.COMPOSITE_MIME_TYPE);
61 | assertThat(metadata).containsKey(RouteSetup.METADATA_KEY);
62 |
63 | return (RouteSetup) metadata.get(RouteSetup.METADATA_KEY);
64 | }
65 |
66 | @SpringBootConfiguration
67 | @EnableAutoConfiguration
68 | static class Config {
69 |
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/test/java/org/springframework/cloud/gateway/rsocket/common/metadata/RouteSetupTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.metadata;
18 |
19 | import java.math.BigInteger;
20 |
21 | import io.netty.buffer.ByteBuf;
22 | import org.junit.Test;
23 |
24 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata.Key;
25 |
26 | import static org.assertj.core.api.Assertions.assertThat;
27 | import static org.springframework.cloud.gateway.rsocket.common.metadata.WellKnownKey.REGION;
28 |
29 | public class RouteSetupTests {
30 |
31 | static final BigInteger MAX_BIGINT = new BigInteger(
32 | "170141183460469231731687303715884105727");
33 | static final BigInteger TWO_BYTE_BIGINT = new BigInteger("128");
34 |
35 | @Test
36 | public void bigIntegerTest() {
37 | byte[] bytes = MAX_BIGINT.toByteArray();
38 | System.out.println("max bytes: " + bytes.length);
39 |
40 | bytes = BigInteger.ONE.toByteArray();
41 | System.out.println("min bytes: " + bytes.length);
42 |
43 | BigInteger bigInteger = TWO_BYTE_BIGINT;
44 | bytes = bigInteger.toByteArray();
45 | System.out.println("16 bytes: " + bytes.length);
46 | }
47 |
48 | @Test
49 | public void encodeAndDecodeWorksMaxBigint() {
50 | ByteBuf byteBuf = createRouteSetup(MAX_BIGINT);
51 | assertRouteSetup(byteBuf, MAX_BIGINT);
52 | }
53 |
54 | @Test
55 | public void encodeAndDecodeWorksMinBigint() {
56 | ByteBuf byteBuf = createRouteSetup(BigInteger.ONE);
57 | assertRouteSetup(byteBuf, BigInteger.ONE);
58 | }
59 |
60 | @Test
61 | public void encodeAndDecodeWorksTwoBytes() {
62 | ByteBuf byteBuf = createRouteSetup(TWO_BYTE_BIGINT);
63 | assertRouteSetup(byteBuf, TWO_BYTE_BIGINT);
64 | }
65 |
66 | @Test
67 | public void encodeAndDecodeWorksEmptyTags() {
68 | ByteBuf byteBuf = createRouteSetup(TWO_BYTE_BIGINT, false);
69 | assertRouteSetup(byteBuf, TWO_BYTE_BIGINT, false);
70 | }
71 |
72 | protected ByteBuf createRouteSetup(BigInteger id) {
73 | return createRouteSetup(id, true);
74 | }
75 |
76 | protected ByteBuf createRouteSetup(BigInteger id, boolean addTags) {
77 | RouteSetup.Builder routeSetup = RouteSetup.of(id, "myservice11111111");
78 | if (addTags) {
79 | routeSetup.with(REGION, "us-east-1");
80 | }
81 | return encode(routeSetup.build());
82 | }
83 |
84 | protected ByteBuf encode(RouteSetup routeSetup) {
85 | return routeSetup.encode();
86 | }
87 |
88 | protected void assertRouteSetup(ByteBuf byteBuf, BigInteger routeId) {
89 | assertRouteSetup(byteBuf, routeId, true);
90 | }
91 |
92 | protected void assertRouteSetup(ByteBuf byteBuf, BigInteger routeId,
93 | boolean addTags) {
94 | RouteSetup routeSetup = decode(byteBuf);
95 | assertThat(routeSetup).isNotNull();
96 | assertThat(routeSetup.getId()).isEqualTo(routeId);
97 | assertThat(routeSetup.getServiceName()).isEqualTo("myservice11111111");
98 | if (addTags) {
99 | assertThat(routeSetup.getTags()).hasSize(1).containsOnlyKeys(new Key(REGION))
100 | .containsValues("us-east-1");
101 | }
102 | else {
103 | assertThat(routeSetup.getTags()).isEmpty();
104 | }
105 | }
106 |
107 | protected RouteSetup decode(ByteBuf byteBuf) {
108 | return RouteSetup.decodeRouteSetup(byteBuf);
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-common/src/test/java/org/springframework/cloud/gateway/rsocket/common/metadata/TagsMetadataTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013-2019 the original author or authors.
3 | *
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 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
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 org.springframework.cloud.gateway.rsocket.common.metadata;
18 |
19 | import io.netty.buffer.ByteBuf;
20 | import org.junit.Test;
21 |
22 | import org.springframework.cloud.gateway.rsocket.common.metadata.TagsMetadata.Key;
23 |
24 | import static org.assertj.core.api.Assertions.assertThat;
25 | import static org.springframework.cloud.gateway.rsocket.common.metadata.WellKnownKey.ROUTE_ID;
26 | import static org.springframework.cloud.gateway.rsocket.common.metadata.WellKnownKey.SERVICE_NAME;
27 |
28 | public class TagsMetadataTests {
29 |
30 | @Test
31 | public void encodeAndDecodeWorksAllWellKnowKeys() {
32 | ByteBuf byteBuf = TagsMetadata.builder().with(ROUTE_ID, "routeId1111111")
33 | .with(SERVICE_NAME, "serviceName2222222").encode();
34 | TagsMetadata metadata = TagsMetadata.decode(byteBuf);
35 | assertThat(metadata).isNotNull();
36 | assertThat(metadata.getTags()).hasSize(2)
37 | .containsOnlyKeys(new Key(ROUTE_ID), new Key(SERVICE_NAME))
38 | .containsValues("routeId1111111", "serviceName2222222");
39 | }
40 |
41 | @Test
42 | public void encodeAndDecodeWorksAllStringKeys() {
43 | ByteBuf byteBuf = TagsMetadata.builder().with("mykey111111111", "myval1111111")
44 | .with("mykey2222222222", "myval2222222").encode();
45 | TagsMetadata metadata = TagsMetadata.decode(byteBuf);
46 | assertThat(metadata).isNotNull();
47 | assertThat(metadata.getTags()).hasSize(2)
48 | .containsOnlyKeys(new Key("mykey111111111"), new Key("mykey2222222222"))
49 | .containsValues("myval1111111", "myval2222222");
50 | }
51 |
52 | @Test
53 | public void encodeAndDecodeWorksMixedKeys() {
54 | ByteBuf byteBuf = TagsMetadata.builder().with(ROUTE_ID, "routeId1111111")
55 | .with("mykey2222222222", "myval2222222").encode();
56 | TagsMetadata metadata = TagsMetadata.decode(byteBuf);
57 | assertThat(metadata).isNotNull();
58 | assertThat(metadata.getTags()).hasSize(2)
59 | .containsOnlyKeys(new Key(ROUTE_ID), new Key("mykey2222222222"))
60 | .containsValues("routeId1111111", "myval2222222");
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/spring-cloud-rsocket-dependencies/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | spring-cloud-dependencies-parent
8 | org.springframework.cloud
9 | 2.2.0.BUILD-SNAPSHOT
10 |
11 |
12 |
13 | spring-cloud-rsocket-dependencies
14 | 0.2.0.BUILD-SNAPSHOT
15 | pom
16 |
17 | spring-cloud-rsocket-dependencies
18 | Spring Cloud RSocket Dependencies
19 |
20 |
21 | 0.8.9
22 |
23 |
24 |
25 |
26 |
27 | org.springframework.cloud
28 | spring-cloud-rsocket-common
29 | ${project.version}
30 |
31 |
32 | org.springframework.cloud
33 | spring-cloud-rsocket-client
34 | ${project.version}
35 |
36 |
37 | org.springframework.cloud
38 | spring-cloud-rsocket-broker
39 | ${project.version}
40 |
41 |
42 | org.roaringbitmap
43 | RoaringBitmap
44 | ${roaringbitmap.version}
45 |
46 |
47 |
48 |
49 |
50 |
51 | spring
52 |
53 |
54 | spring-snapshots
55 | Spring Snapshots
56 | https://repo.spring.io/libs-snapshot-local
57 |
58 | true
59 |
60 |
61 | false
62 |
63 |
64 |
65 | spring-milestones
66 | Spring Milestones
67 | https://repo.spring.io/libs-milestone-local
68 |
69 | false
70 |
71 |
72 |
73 | spring-releases
74 | Spring Releases
75 | https://repo.spring.io/release
76 |
77 | false
78 |
79 |
80 |
81 |
82 |
83 | spring-snapshots
84 | Spring Snapshots
85 | https://repo.spring.io/libs-snapshot-local
86 |
87 | true
88 |
89 |
90 | false
91 |
92 |
93 |
94 | spring-milestones
95 | Spring Milestones
96 | https://repo.spring.io/libs-milestone-local
97 |
98 | false
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/checkstyle/checkstyle-suppressions.xml:
--------------------------------------------------------------------------------
1 |
2 |
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 |
--------------------------------------------------------------------------------