Skip to content

Commit 54dbf07

Browse files
authored
Handle rate limiting of UAA server (#1332)
* Feature: handle rate limiting of UAA server. Fixes #1307 Signed-off-by: Daniel Garnier-Moiroux <[email protected]>
1 parent 87344b7 commit 54dbf07

File tree

17 files changed

+755
-17
lines changed

17 files changed

+755
-17
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ Name | Description
297297
`TEST_PROXY_PORT` | _(Optional)_ The port of a proxy to route all requests through. Defaults to `8080`.
298298
`TEST_PROXY_USERNAME` | _(Optional)_ The username for a proxy to route all requests through
299299
`TEST_SKIPSSLVALIDATION` | _(Optional)_ Whether to skip SSL validation when connecting to the Cloud Foundry instance. Defaults to `false`.
300+
`UAA_API_REQUEST_LIMIT` | _(Optional)_ If your UAA server does rate limiting and returns 429 errors, set this variable to the smallest limit configured there. Whether your server limits UAA calls is shown in the log, together with the location of the configuration file on the server. Defaults to `0` (no limit).
300301

301302
If you do not have access to a CloudFoundry instance with admin access, you can run one locally using [bosh-deployment](https://github.com/cloudfoundry/bosh-deployment) & [cf-deployment](https://github.com/cloudfoundry/cf-deployment/) and Virtualbox.
302303

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2013-2025 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+
* http://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.cloudfoundry.reactor.uaa;
18+
19+
import java.util.Map;
20+
import org.cloudfoundry.reactor.ConnectionContext;
21+
import org.cloudfoundry.reactor.TokenProvider;
22+
import org.cloudfoundry.uaa.ratelimit.Ratelimit;
23+
import org.cloudfoundry.uaa.ratelimit.RatelimitRequest;
24+
import org.cloudfoundry.uaa.ratelimit.RatelimitResponse;
25+
import reactor.core.publisher.Mono;
26+
27+
public final class ReactorRatelimit extends AbstractUaaOperations implements Ratelimit {
28+
29+
/**
30+
* Creates an instance
31+
*
32+
* @param connectionContext the {@link ConnectionContext} to use when communicating with the server
33+
* @param root the root URI of the server. Typically something like {@code https://uaa.run.pivotal.io}.
34+
* @param tokenProvider the {@link TokenProvider} to use when communicating with the server
35+
* @param requestTags map with custom http headers which will be added to web request
36+
*/
37+
public ReactorRatelimit(
38+
ConnectionContext connectionContext,
39+
Mono<String> root,
40+
TokenProvider tokenProvider,
41+
Map<String, String> requestTags) {
42+
super(connectionContext, root, tokenProvider, requestTags);
43+
}
44+
45+
@Override
46+
public Mono<RatelimitResponse> getRatelimit(RatelimitRequest request) {
47+
return get(
48+
request,
49+
RatelimitResponse.class,
50+
builder -> builder.pathSegment("RateLimitingStatus"))
51+
.checkpoint();
52+
}
53+
}

cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/uaa/_ReactorUaaClient.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.cloudfoundry.uaa.groups.Groups;
3434
import org.cloudfoundry.uaa.identityproviders.IdentityProviders;
3535
import org.cloudfoundry.uaa.identityzones.IdentityZones;
36+
import org.cloudfoundry.uaa.ratelimit.Ratelimit;
3637
import org.cloudfoundry.uaa.serverinformation.ServerInformation;
3738
import org.cloudfoundry.uaa.tokens.Tokens;
3839
import org.cloudfoundry.uaa.users.Users;
@@ -104,6 +105,12 @@ public Users users() {
104105
return new ReactorUsers(getConnectionContext(), getRoot(), getTokenProvider(), getRequestTags());
105106
}
106107

108+
@Override
109+
@Value.Derived
110+
public Ratelimit rateLimit() {
111+
return new ReactorRatelimit(getConnectionContext(), getRoot(), getTokenProvider(), getRequestTags());
112+
}
113+
107114
/**
108115
* The connection context
109116
*/

cloudfoundry-client-reactor/src/main/java/org/cloudfoundry/reactor/util/RequestLogger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public class RequestLogger {
3434
private long requestSentTime;
3535

3636
public void request(HttpClientRequest request) {
37-
request(String.format("%-6s {}", request.method()), request.uri());
37+
request(String.format("%-6s {}", request.method()), request.resourceUrl());
3838
}
3939

4040
public void response(HttpClientResponse response) {

cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/serviceInstances/ReactorServiceInstancesV3Test.java renamed to cloudfoundry-client-reactor/src/test/java/org/cloudfoundry/reactor/client/v3/serviceinstances/ReactorServiceInstancesV3Test.java

File renamed without changes.

cloudfoundry-client/src/main/java/org/cloudfoundry/uaa/UaaClient.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.cloudfoundry.uaa.groups.Groups;
2222
import org.cloudfoundry.uaa.identityproviders.IdentityProviders;
2323
import org.cloudfoundry.uaa.identityzones.IdentityZones;
24+
import org.cloudfoundry.uaa.ratelimit.Ratelimit;
2425
import org.cloudfoundry.uaa.serverinformation.ServerInformation;
2526
import org.cloudfoundry.uaa.tokens.Tokens;
2627
import org.cloudfoundry.uaa.users.Users;
@@ -80,4 +81,9 @@ public interface UaaClient {
8081
* Main entry point to the UAA User Client API
8182
*/
8283
Users users();
84+
85+
/**
86+
* Main entry point to the UAA Ratelimit API
87+
*/
88+
Ratelimit rateLimit();
8389
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2013-2025 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+
* http://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.cloudfoundry.uaa.ratelimit;
18+
19+
import reactor.core.publisher.Mono;
20+
21+
/**
22+
* Main entry point to the UAA Ratelimit Client API
23+
*/
24+
public interface Ratelimit {
25+
26+
Mono<RatelimitResponse> getRatelimit(RatelimitRequest request);
27+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2013-2025 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+
* http://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.cloudfoundry.uaa.ratelimit;
18+
19+
20+
import com.fasterxml.jackson.annotation.JsonProperty;
21+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
22+
23+
import java.util.Date;
24+
25+
import org.immutables.value.Value;
26+
27+
/**
28+
* The payload for the uaa ratelimiting
29+
*/
30+
@JsonDeserialize
31+
@Value.Immutable
32+
abstract class _Current {
33+
34+
/**
35+
* The number of configured limiter mappings
36+
*/
37+
@JsonProperty("limiterMappings")
38+
abstract Integer getLimiterMappings();
39+
40+
/**
41+
* Is ratelimit "ACTIVE" or not? Possible values are DISABLED, PENDING, ACTIVE
42+
*/
43+
@JsonProperty("status")
44+
abstract String getStatus();
45+
46+
/**
47+
* Timestamp, when this Current was created.
48+
*/
49+
@JsonProperty("asOf")
50+
abstract Date getTimeOfCurrent();
51+
52+
/**
53+
* The credentialIdExtractor
54+
*/
55+
@JsonProperty("credentialIdExtractor")
56+
abstract String getCredentialIdExtractor();
57+
58+
/**
59+
* The loggingLevel. Valid values include: "OnlyLimited", "AllCalls" and "AllCallsWithDetails"
60+
*/
61+
@JsonProperty("loggingLevel")
62+
abstract String getLoggingLevel();
63+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2013-2025 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+
* http://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.cloudfoundry.uaa.ratelimit;
18+
19+
import org.immutables.value.Value;
20+
21+
@Value.Immutable
22+
abstract class _RatelimitRequest {
23+
24+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2013-2025 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+
* http://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.cloudfoundry.uaa.ratelimit;
18+
19+
import com.fasterxml.jackson.annotation.JsonProperty;
20+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
21+
import org.cloudfoundry.Nullable;
22+
import org.immutables.value.Value;
23+
24+
@JsonDeserialize
25+
@Value.Immutable
26+
abstract class _RatelimitResponse {
27+
28+
@JsonProperty("current")
29+
@Nullable
30+
abstract Current getCurrentData();
31+
32+
@JsonProperty("fromSource")
33+
@Nullable
34+
abstract String getFromSource();
35+
36+
}

0 commit comments

Comments
 (0)