Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit bdcee7c

Browse filesBrowse files
committed
Merge pull request hub4j#160 with some modifications
Strategy pattern is better for API rate limit handling as the current behavior is quite valid for batch applications. I'm not taking OkHttp version change so as not to introduce any unneeded version requirement.
2 parents 7ed234c + 4093e53 commit bdcee7c
Copy full SHA for bdcee7c

File tree

Expand file treeCollapse file tree

6 files changed

+95
-14
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+95
-14
lines changed

‎src/main/java/org/kohsuke/github/GHRateLimit.java

Copy file name to clipboardExpand all lines: src/main/java/org/kohsuke/github/GHRateLimit.java
+20-2Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.kohsuke.github;
22

3+
import java.util.Date;
4+
35
/**
46
* Rate limit.
57
* @author Kohsuke Kawaguchi
@@ -10,12 +12,28 @@ public class GHRateLimit {
1012
*/
1113
public int remaining;
1214
/**
13-
* Alotted API call per hour.
15+
* Allotted API call per hour.
1416
*/
1517
public int limit;
1618

19+
/**
20+
* The time at which the current rate limit window resets in UTC epoch seconds.
21+
*/
22+
public Date reset;
23+
24+
/**
25+
* Non-epoch date
26+
*/
27+
public Date getResetDate() {
28+
return new Date(reset.getTime() * 1000);
29+
}
30+
1731
@Override
1832
public String toString() {
19-
return remaining+"/"+limit;
33+
return "GHRateLimit{" +
34+
"remaining=" + remaining +
35+
", limit=" + limit +
36+
", resetDate=" + getResetDate() +
37+
'}';
2038
}
2139
}

‎src/main/java/org/kohsuke/github/GitHub.java

Copy file name to clipboardExpand all lines: src/main/java/org/kohsuke/github/GitHub.java
+4-1Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public class GitHub {
7575

7676
private final String apiUrl;
7777

78+
/*package*/ final RateLimitHandler rateLimitHandler;
79+
7880
private HttpConnector connector = HttpConnector.DEFAULT;
7981

8082
/**
@@ -113,7 +115,7 @@ public class GitHub {
113115
* @param connector
114116
* HttpConnector to use. Pass null to use default connector.
115117
*/
116-
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector) throws IOException {
118+
/* package */ GitHub(String apiUrl, String login, String oauthAccessToken, String password, HttpConnector connector, RateLimitHandler rateLimitHandler) throws IOException {
117119
if (apiUrl.endsWith("/")) apiUrl = apiUrl.substring(0, apiUrl.length()-1); // normalize
118120
this.apiUrl = apiUrl;
119121
if (null != connector) this.connector = connector;
@@ -132,6 +134,7 @@ public class GitHub {
132134
if (login==null && encodedAuthorization!=null)
133135
login = getMyself().getLogin();
134136
this.login = login;
137+
this.rateLimitHandler = rateLimitHandler;
135138
}
136139

137140
/**

‎src/main/java/org/kohsuke/github/GitHubBuilder.java

Copy file name to clipboardExpand all lines: src/main/java/org/kohsuke/github/GitHubBuilder.java
+7-1Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public class GitHubBuilder {
2828

2929
private HttpConnector connector;
3030

31+
private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
32+
3133
public GitHubBuilder() {
3234
}
3335

@@ -171,6 +173,10 @@ public GitHubBuilder withConnector(HttpConnector connector) {
171173
this.connector = connector;
172174
return this;
173175
}
176+
public GitHubBuilder withRateLimitHandler(RateLimitHandler handler) {
177+
this.rateLimitHandler = handler;
178+
return this;
179+
}
174180

175181
/**
176182
* Configures {@linkplain #withConnector(HttpConnector) connector}
@@ -186,6 +192,6 @@ public HttpURLConnection connect(URL url) throws IOException {
186192
}
187193

188194
public GitHub build() throws IOException {
189-
return new GitHub(endpoint, user, oauthToken, password, connector);
195+
return new GitHub(endpoint, user, oauthToken, password, connector, rateLimitHandler);
190196
}
191197
}
+61Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package org.kohsuke.github;
2+
3+
import java.io.IOException;
4+
import java.io.InterruptedIOException;
5+
import java.net.HttpURLConnection;
6+
7+
/**
8+
* Pluggable strategy to determine what to do when the API rate limit is reached.
9+
*
10+
* @author Kohsuke Kawaguchi
11+
* @see GitHubBuilder#withRateLimitHandler(RateLimitHandler)
12+
*/
13+
public abstract class RateLimitHandler {
14+
/**
15+
* Called when the library encounters HTTP error indicating that the API rate limit is reached.
16+
*
17+
* <p>
18+
* Any exception thrown from this method will cause the request to fail, and the caller of github-api
19+
* will receive an exception. If this method returns normally, another request will be attempted.
20+
* For that to make sense, the implementation needs to wait for some time.
21+
*
22+
* @see <a href="https://developer.github.com/v3/#rate-limiting">API documentation from GitHub</a>
23+
* @param e
24+
* Exception from Java I/O layer. If you decide to fail the processing, you can throw
25+
* this exception (or wrap this exception into another exception and throw it.)
26+
* @param uc
27+
* Connection that resulted in an error. Useful for accessing other response headers.
28+
*/
29+
public abstract void onError(IOException e, HttpURLConnection uc) throws IOException;
30+
31+
/**
32+
* Block until the API rate limit is reset. Useful for long-running batch processing.
33+
*/
34+
public static final RateLimitHandler WAIT = new RateLimitHandler() {
35+
@Override
36+
public void onError(IOException e, HttpURLConnection uc) throws IOException {
37+
try {
38+
Thread.sleep(parseWaitTime(uc));
39+
} catch (InterruptedException _) {
40+
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
41+
}
42+
}
43+
44+
private long parseWaitTime(HttpURLConnection uc) {
45+
String v = uc.getHeaderField("X-RateLimit-Reset");
46+
if (v==null) return 10000; // can't tell
47+
48+
return Math.max(10000, Long.parseLong(v)*1000 - System.currentTimeMillis());
49+
}
50+
};
51+
52+
/**
53+
* Fail immediately.
54+
*/
55+
public static final RateLimitHandler FAIL = new RateLimitHandler() {
56+
@Override
57+
public void onError(IOException e, HttpURLConnection uc) throws IOException {
58+
throw (IOException)new IOException("API rate limit reached").initCause(e);
59+
}
60+
};
61+
}

‎src/main/java/org/kohsuke/github/Requester.java

Copy file name to clipboardExpand all lines: src/main/java/org/kohsuke/github/Requester.java
+2-9Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -417,18 +417,11 @@ private InputStream wrapStream(HttpURLConnection uc, InputStream in) throws IOEx
417417
}
418418

419419
/**
420-
* If the error is because of the API limit, wait 10 sec and return normally.
421-
* Otherwise throw an exception reporting an error.
420+
* Handle API error by either throwing it or by returning normally to retry.
422421
*/
423422
/*package*/ void handleApiError(IOException e, HttpURLConnection uc) throws IOException {
424423
if ("0".equals(uc.getHeaderField("X-RateLimit-Remaining"))) {
425-
// API limit reached. wait 10 secs and return normally
426-
try {
427-
Thread.sleep(10000);
428-
return;
429-
} catch (InterruptedException _) {
430-
throw (InterruptedIOException)new InterruptedIOException().initCause(e);
431-
}
424+
root.rateLimitHandler.onError(e,uc);
432425
}
433426

434427
if (e instanceof FileNotFoundException)

‎src/test/java/org/kohsuke/github/AbstractGitHubApiTestBase.java

Copy file name to clipboardExpand all lines: src/test/java/org/kohsuke/github/AbstractGitHubApiTestBase.java
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public abstract class AbstractGitHubApiTestBase extends Assert {
1818
@Before
1919
public void setUp() throws Exception {
2020
Properties props = new Properties();
21-
java.io.File f = new java.io.File(System.getProperty("user.home"), ".github.kohsuke2");
21+
java.io.File f = new java.io.File(System.getProperty("user.home"), ".github");
2222
if (f.exists()) {
2323
FileInputStream in = new FileInputStream(f);
2424
try {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.