diff --git a/src/main/java/org/kohsuke/github/GitHubConnectorResponseErrorHandler.java b/src/main/java/org/kohsuke/github/GitHubConnectorResponseErrorHandler.java
index dc277ac856..e71e81921d 100644
--- a/src/main/java/org/kohsuke/github/GitHubConnectorResponseErrorHandler.java
+++ b/src/main/java/org/kohsuke/github/GitHubConnectorResponseErrorHandler.java
@@ -3,12 +3,16 @@
import org.jetbrains.annotations.NotNull;
import org.kohsuke.github.connector.GitHubConnectorResponse;
+import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
import javax.annotation.Nonnull;
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
// TODO: Auto-generated Javadoc
@@ -49,6 +53,10 @@ abstract class GitHubConnectorResponseErrorHandler {
/** The status http bad request or greater. */
static GitHubConnectorResponseErrorHandler STATUS_HTTP_BAD_REQUEST_OR_GREATER = new GitHubConnectorResponseErrorHandler() {
+ private static final String CONTENT_TYPE = "Content-type";
+ private static final String TEXT_HTML = "text/html";
+ private static final String UNICORN_TITLE = "
Unicorn!";
+
@Override
public boolean isError(@NotNull GitHubConnectorResponse connectorResponse) throws IOException {
return connectorResponse.statusCode() >= HTTP_BAD_REQUEST;
@@ -58,9 +66,38 @@ public boolean isError(@NotNull GitHubConnectorResponse connectorResponse) throw
public void onError(@NotNull GitHubConnectorResponse connectorResponse) throws IOException {
if (connectorResponse.statusCode() == HTTP_NOT_FOUND) {
throw new FileNotFoundException(connectorResponse.request().url().toString());
+ } else if (isServiceDown(connectorResponse)) {
+ throw new ServiceDownException(connectorResponse);
} else {
throw new HttpException(connectorResponse);
}
}
+
+ private boolean isServiceDown(GitHubConnectorResponse connectorResponse) throws IOException {
+ if (connectorResponse.statusCode() < HTTP_INTERNAL_ERROR) {
+ return false;
+ }
+
+ String contentTypeHeader = connectorResponse.header(CONTENT_TYPE);
+ if (contentTypeHeader != null && contentTypeHeader.contains(TEXT_HTML)) {
+ try (BufferedReader bufReader = new BufferedReader(
+ new InputStreamReader(connectorResponse.bodyStream(), StandardCharsets.UTF_8))) {
+ String line;
+ int hardLineCap = 25;
+ // node is expected in the beginning anyway.
+ // This way we do not load the raw long images' Strings, which are later in the HTML code
+ // Regex or .contains would result in iterating the whole HTML document, if it didn't match
+ // UNICORN_TITLE
+ while (hardLineCap > 0 && (line = bufReader.readLine()) != null) {
+ if (line.trim().startsWith(UNICORN_TITLE)) {
+ return true;
+ }
+ hardLineCap--;
+ }
+ }
+ }
+
+ return false;
+ }
};
}
diff --git a/src/main/java/org/kohsuke/github/ServiceDownException.java b/src/main/java/org/kohsuke/github/ServiceDownException.java
new file mode 100644
index 0000000000..81f35b33b0
--- /dev/null
+++ b/src/main/java/org/kohsuke/github/ServiceDownException.java
@@ -0,0 +1,26 @@
+package org.kohsuke.github;
+
+import org.kohsuke.github.connector.GitHubConnectorResponse;
+
+import java.io.IOException;
+
+/**
+ * Special {@link IOException} case for http exceptions, when {@link HttpException} is thrown due to GitHub service
+ * being down.
+ *
+ * Inherits from {@link HttpException} to maintain compatibility with existing clients.
+ *
+ * @author Rastislav Budinsky
+ */
+public class ServiceDownException extends HttpException {
+
+ /**
+ * Instantiates a new service down exception.
+ *
+ * @param connectorResponse
+ * the connector response to base this on
+ */
+ public ServiceDownException(GitHubConnectorResponse connectorResponse) {
+ super(connectorResponse);
+ }
+}
diff --git a/src/test/java/org/kohsuke/github/GitHubTest.java b/src/test/java/org/kohsuke/github/GitHubTest.java
index 4b4a848545..fc2c76c21f 100644
--- a/src/test/java/org/kohsuke/github/GitHubTest.java
+++ b/src/test/java/org/kohsuke/github/GitHubTest.java
@@ -397,4 +397,20 @@ public void testHeaderFieldName() throws Exception {
org.getResponseHeaderFields().keySet().contains("CacHe-ControL"));
assertThat(org.getResponseHeaderFields().get("cachE-cOntrol").get(0), is("private, max-age=60, s-maxage=60"));
}
+
+ /**
+ * Test expect GitHub {@link ServiceDownException}
+ *
+ */
+ @Test
+ public void testCatchServiceDownException() {
+ snapshotNotAllowed();
+ try {
+ GHRepository repo = gitHub.getRepository("hub4j-test-org/github-api");
+ repo.getFileContent("ghcontent-ro/service-down");
+ fail("Exception was expected");
+ } catch (IOException e) {
+ assertThat(e.getClass().getName(), equalToIgnoringCase(ServiceDownException.class.getName()));
+ }
+ }
}
diff --git a/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/__files/1-user.json b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/__files/1-user.json
new file mode 100644
index 0000000000..232c9a329c
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/__files/1-user.json
@@ -0,0 +1,34 @@
+{
+ "login": "The-Huginn",
+ "id": 78657734,
+ "node_id": "MDQ6VXNlcjc4NjU3NzM0",
+ "avatar_url": "https://avatars.githubusercontent.com/u/78657734?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/The-Huginn",
+ "html_url": "https://github.com/The-Huginn",
+ "followers_url": "https://api.github.com/users/The-Huginn/followers",
+ "following_url": "https://api.github.com/users/The-Huginn/following{/other_user}",
+ "gists_url": "https://api.github.com/users/The-Huginn/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/The-Huginn/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/The-Huginn/subscriptions",
+ "organizations_url": "https://api.github.com/users/The-Huginn/orgs",
+ "repos_url": "https://api.github.com/users/The-Huginn/repos",
+ "events_url": "https://api.github.com/users/The-Huginn/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/The-Huginn/received_events",
+ "type": "User",
+ "site_admin": false,
+ "name": "Rastislav Budinsky",
+ "company": "Red Hat",
+ "blog": "thehuginn.com",
+ "location": null,
+ "email": null,
+ "hireable": null,
+ "bio": null,
+ "twitter_username": "The_Hug1nn",
+ "public_repos": 47,
+ "public_gists": 0,
+ "followers": 3,
+ "following": 2,
+ "created_at": "2021-02-06T17:33:36Z",
+ "updated_at": "2024-03-11T08:56:45Z"
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/__files/2-r_h_github-api.json b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/__files/2-r_h_github-api.json
new file mode 100644
index 0000000000..3c0b7ec286
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/__files/2-r_h_github-api.json
@@ -0,0 +1,364 @@
+{
+ "id": 206888201,
+ "node_id": "MDEwOlJlcG9zaXRvcnkyMDY4ODgyMDE=",
+ "name": "github-api",
+ "full_name": "hub4j-test-org/github-api",
+ "private": false,
+ "owner": {
+ "login": "hub4j-test-org",
+ "id": 7544739,
+ "node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7544739?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/hub4j-test-org",
+ "html_url": "https://github.com/hub4j-test-org",
+ "followers_url": "https://api.github.com/users/hub4j-test-org/followers",
+ "following_url": "https://api.github.com/users/hub4j-test-org/following{/other_user}",
+ "gists_url": "https://api.github.com/users/hub4j-test-org/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/hub4j-test-org/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/hub4j-test-org/subscriptions",
+ "organizations_url": "https://api.github.com/users/hub4j-test-org/orgs",
+ "repos_url": "https://api.github.com/users/hub4j-test-org/repos",
+ "events_url": "https://api.github.com/users/hub4j-test-org/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/hub4j-test-org/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "html_url": "https://github.com/hub4j-test-org/github-api",
+ "description": "Tricky",
+ "fork": true,
+ "url": "https://api.github.com/repos/hub4j-test-org/github-api",
+ "forks_url": "https://api.github.com/repos/hub4j-test-org/github-api/forks",
+ "keys_url": "https://api.github.com/repos/hub4j-test-org/github-api/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/hub4j-test-org/github-api/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/hub4j-test-org/github-api/teams",
+ "hooks_url": "https://api.github.com/repos/hub4j-test-org/github-api/hooks",
+ "issue_events_url": "https://api.github.com/repos/hub4j-test-org/github-api/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/hub4j-test-org/github-api/events",
+ "assignees_url": "https://api.github.com/repos/hub4j-test-org/github-api/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/hub4j-test-org/github-api/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/hub4j-test-org/github-api/tags",
+ "blobs_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/hub4j-test-org/github-api/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/hub4j-test-org/github-api/languages",
+ "stargazers_url": "https://api.github.com/repos/hub4j-test-org/github-api/stargazers",
+ "contributors_url": "https://api.github.com/repos/hub4j-test-org/github-api/contributors",
+ "subscribers_url": "https://api.github.com/repos/hub4j-test-org/github-api/subscribers",
+ "subscription_url": "https://api.github.com/repos/hub4j-test-org/github-api/subscription",
+ "commits_url": "https://api.github.com/repos/hub4j-test-org/github-api/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/hub4j-test-org/github-api/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/hub4j-test-org/github-api/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/hub4j-test-org/github-api/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/hub4j-test-org/github-api/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/hub4j-test-org/github-api/merges",
+ "archive_url": "https://api.github.com/repos/hub4j-test-org/github-api/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/hub4j-test-org/github-api/downloads",
+ "issues_url": "https://api.github.com/repos/hub4j-test-org/github-api/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/hub4j-test-org/github-api/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/hub4j-test-org/github-api/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/hub4j-test-org/github-api/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/hub4j-test-org/github-api/labels{/name}",
+ "releases_url": "https://api.github.com/repos/hub4j-test-org/github-api/releases{/id}",
+ "deployments_url": "https://api.github.com/repos/hub4j-test-org/github-api/deployments",
+ "created_at": "2019-09-06T23:26:04Z",
+ "updated_at": "2023-01-31T10:03:44Z",
+ "pushed_at": "2024-03-09T14:27:07Z",
+ "git_url": "git://github.com/hub4j-test-org/github-api.git",
+ "ssh_url": "git@github.com:hub4j-test-org/github-api.git",
+ "clone_url": "https://github.com/hub4j-test-org/github-api.git",
+ "svn_url": "https://github.com/hub4j-test-org/github-api",
+ "homepage": "http://github-api.kohsuke.org/",
+ "size": 18977,
+ "stargazers_count": 1,
+ "watchers_count": 1,
+ "language": "Java",
+ "has_issues": true,
+ "has_projects": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": false,
+ "has_discussions": false,
+ "forks_count": 0,
+ "mirror_url": null,
+ "archived": false,
+ "disabled": false,
+ "open_issues_count": 7,
+ "license": {
+ "key": "mit",
+ "name": "MIT License",
+ "spdx_id": "MIT",
+ "url": "https://api.github.com/licenses/mit",
+ "node_id": "MDc6TGljZW5zZTEz"
+ },
+ "allow_forking": true,
+ "is_template": false,
+ "web_commit_signoff_required": false,
+ "topics": [],
+ "visibility": "public",
+ "forks": 0,
+ "open_issues": 7,
+ "watchers": 1,
+ "default_branch": "main",
+ "permissions": {
+ "admin": false,
+ "maintain": false,
+ "push": false,
+ "triage": false,
+ "pull": true
+ },
+ "custom_properties": {},
+ "organization": {
+ "login": "hub4j-test-org",
+ "id": 7544739,
+ "node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7544739?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/hub4j-test-org",
+ "html_url": "https://github.com/hub4j-test-org",
+ "followers_url": "https://api.github.com/users/hub4j-test-org/followers",
+ "following_url": "https://api.github.com/users/hub4j-test-org/following{/other_user}",
+ "gists_url": "https://api.github.com/users/hub4j-test-org/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/hub4j-test-org/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/hub4j-test-org/subscriptions",
+ "organizations_url": "https://api.github.com/users/hub4j-test-org/orgs",
+ "repos_url": "https://api.github.com/users/hub4j-test-org/repos",
+ "events_url": "https://api.github.com/users/hub4j-test-org/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/hub4j-test-org/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "parent": {
+ "id": 617210,
+ "node_id": "MDEwOlJlcG9zaXRvcnk2MTcyMTA=",
+ "name": "github-api",
+ "full_name": "hub4j/github-api",
+ "private": false,
+ "owner": {
+ "login": "hub4j",
+ "id": 54909825,
+ "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0OTA5ODI1",
+ "avatar_url": "https://avatars.githubusercontent.com/u/54909825?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/hub4j",
+ "html_url": "https://github.com/hub4j",
+ "followers_url": "https://api.github.com/users/hub4j/followers",
+ "following_url": "https://api.github.com/users/hub4j/following{/other_user}",
+ "gists_url": "https://api.github.com/users/hub4j/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/hub4j/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/hub4j/subscriptions",
+ "organizations_url": "https://api.github.com/users/hub4j/orgs",
+ "repos_url": "https://api.github.com/users/hub4j/repos",
+ "events_url": "https://api.github.com/users/hub4j/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/hub4j/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "html_url": "https://github.com/hub4j/github-api",
+ "description": "Java API for GitHub",
+ "fork": false,
+ "url": "https://api.github.com/repos/hub4j/github-api",
+ "forks_url": "https://api.github.com/repos/hub4j/github-api/forks",
+ "keys_url": "https://api.github.com/repos/hub4j/github-api/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/hub4j/github-api/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/hub4j/github-api/teams",
+ "hooks_url": "https://api.github.com/repos/hub4j/github-api/hooks",
+ "issue_events_url": "https://api.github.com/repos/hub4j/github-api/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/hub4j/github-api/events",
+ "assignees_url": "https://api.github.com/repos/hub4j/github-api/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/hub4j/github-api/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/hub4j/github-api/tags",
+ "blobs_url": "https://api.github.com/repos/hub4j/github-api/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/hub4j/github-api/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/hub4j/github-api/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/hub4j/github-api/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/hub4j/github-api/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/hub4j/github-api/languages",
+ "stargazers_url": "https://api.github.com/repos/hub4j/github-api/stargazers",
+ "contributors_url": "https://api.github.com/repos/hub4j/github-api/contributors",
+ "subscribers_url": "https://api.github.com/repos/hub4j/github-api/subscribers",
+ "subscription_url": "https://api.github.com/repos/hub4j/github-api/subscription",
+ "commits_url": "https://api.github.com/repos/hub4j/github-api/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/hub4j/github-api/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/hub4j/github-api/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/hub4j/github-api/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/hub4j/github-api/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/hub4j/github-api/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/hub4j/github-api/merges",
+ "archive_url": "https://api.github.com/repos/hub4j/github-api/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/hub4j/github-api/downloads",
+ "issues_url": "https://api.github.com/repos/hub4j/github-api/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/hub4j/github-api/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/hub4j/github-api/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/hub4j/github-api/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/hub4j/github-api/labels{/name}",
+ "releases_url": "https://api.github.com/repos/hub4j/github-api/releases{/id}",
+ "deployments_url": "https://api.github.com/repos/hub4j/github-api/deployments",
+ "created_at": "2010-04-19T04:13:03Z",
+ "updated_at": "2024-03-12T00:08:34Z",
+ "pushed_at": "2024-03-12T23:45:28Z",
+ "git_url": "git://github.com/hub4j/github-api.git",
+ "ssh_url": "git@github.com:hub4j/github-api.git",
+ "clone_url": "https://github.com/hub4j/github-api.git",
+ "svn_url": "https://github.com/hub4j/github-api",
+ "homepage": "https://github-api.kohsuke.org/",
+ "size": 47209,
+ "stargazers_count": 1090,
+ "watchers_count": 1090,
+ "language": "Java",
+ "has_issues": true,
+ "has_projects": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": true,
+ "has_discussions": true,
+ "forks_count": 704,
+ "mirror_url": null,
+ "archived": false,
+ "disabled": false,
+ "open_issues_count": 154,
+ "license": {
+ "key": "mit",
+ "name": "MIT License",
+ "spdx_id": "MIT",
+ "url": "https://api.github.com/licenses/mit",
+ "node_id": "MDc6TGljZW5zZTEz"
+ },
+ "allow_forking": true,
+ "is_template": false,
+ "web_commit_signoff_required": false,
+ "topics": [
+ "api",
+ "client-library",
+ "github",
+ "github-api",
+ "github-api-v3",
+ "java",
+ "java-api"
+ ],
+ "visibility": "public",
+ "forks": 704,
+ "open_issues": 154,
+ "watchers": 1090,
+ "default_branch": "main"
+ },
+ "source": {
+ "id": 617210,
+ "node_id": "MDEwOlJlcG9zaXRvcnk2MTcyMTA=",
+ "name": "github-api",
+ "full_name": "hub4j/github-api",
+ "private": false,
+ "owner": {
+ "login": "hub4j",
+ "id": 54909825,
+ "node_id": "MDEyOk9yZ2FuaXphdGlvbjU0OTA5ODI1",
+ "avatar_url": "https://avatars.githubusercontent.com/u/54909825?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/hub4j",
+ "html_url": "https://github.com/hub4j",
+ "followers_url": "https://api.github.com/users/hub4j/followers",
+ "following_url": "https://api.github.com/users/hub4j/following{/other_user}",
+ "gists_url": "https://api.github.com/users/hub4j/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/hub4j/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/hub4j/subscriptions",
+ "organizations_url": "https://api.github.com/users/hub4j/orgs",
+ "repos_url": "https://api.github.com/users/hub4j/repos",
+ "events_url": "https://api.github.com/users/hub4j/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/hub4j/received_events",
+ "type": "Organization",
+ "site_admin": false
+ },
+ "html_url": "https://github.com/hub4j/github-api",
+ "description": "Java API for GitHub",
+ "fork": false,
+ "url": "https://api.github.com/repos/hub4j/github-api",
+ "forks_url": "https://api.github.com/repos/hub4j/github-api/forks",
+ "keys_url": "https://api.github.com/repos/hub4j/github-api/keys{/key_id}",
+ "collaborators_url": "https://api.github.com/repos/hub4j/github-api/collaborators{/collaborator}",
+ "teams_url": "https://api.github.com/repos/hub4j/github-api/teams",
+ "hooks_url": "https://api.github.com/repos/hub4j/github-api/hooks",
+ "issue_events_url": "https://api.github.com/repos/hub4j/github-api/issues/events{/number}",
+ "events_url": "https://api.github.com/repos/hub4j/github-api/events",
+ "assignees_url": "https://api.github.com/repos/hub4j/github-api/assignees{/user}",
+ "branches_url": "https://api.github.com/repos/hub4j/github-api/branches{/branch}",
+ "tags_url": "https://api.github.com/repos/hub4j/github-api/tags",
+ "blobs_url": "https://api.github.com/repos/hub4j/github-api/git/blobs{/sha}",
+ "git_tags_url": "https://api.github.com/repos/hub4j/github-api/git/tags{/sha}",
+ "git_refs_url": "https://api.github.com/repos/hub4j/github-api/git/refs{/sha}",
+ "trees_url": "https://api.github.com/repos/hub4j/github-api/git/trees{/sha}",
+ "statuses_url": "https://api.github.com/repos/hub4j/github-api/statuses/{sha}",
+ "languages_url": "https://api.github.com/repos/hub4j/github-api/languages",
+ "stargazers_url": "https://api.github.com/repos/hub4j/github-api/stargazers",
+ "contributors_url": "https://api.github.com/repos/hub4j/github-api/contributors",
+ "subscribers_url": "https://api.github.com/repos/hub4j/github-api/subscribers",
+ "subscription_url": "https://api.github.com/repos/hub4j/github-api/subscription",
+ "commits_url": "https://api.github.com/repos/hub4j/github-api/commits{/sha}",
+ "git_commits_url": "https://api.github.com/repos/hub4j/github-api/git/commits{/sha}",
+ "comments_url": "https://api.github.com/repos/hub4j/github-api/comments{/number}",
+ "issue_comment_url": "https://api.github.com/repos/hub4j/github-api/issues/comments{/number}",
+ "contents_url": "https://api.github.com/repos/hub4j/github-api/contents/{+path}",
+ "compare_url": "https://api.github.com/repos/hub4j/github-api/compare/{base}...{head}",
+ "merges_url": "https://api.github.com/repos/hub4j/github-api/merges",
+ "archive_url": "https://api.github.com/repos/hub4j/github-api/{archive_format}{/ref}",
+ "downloads_url": "https://api.github.com/repos/hub4j/github-api/downloads",
+ "issues_url": "https://api.github.com/repos/hub4j/github-api/issues{/number}",
+ "pulls_url": "https://api.github.com/repos/hub4j/github-api/pulls{/number}",
+ "milestones_url": "https://api.github.com/repos/hub4j/github-api/milestones{/number}",
+ "notifications_url": "https://api.github.com/repos/hub4j/github-api/notifications{?since,all,participating}",
+ "labels_url": "https://api.github.com/repos/hub4j/github-api/labels{/name}",
+ "releases_url": "https://api.github.com/repos/hub4j/github-api/releases{/id}",
+ "deployments_url": "https://api.github.com/repos/hub4j/github-api/deployments",
+ "created_at": "2010-04-19T04:13:03Z",
+ "updated_at": "2024-03-12T00:08:34Z",
+ "pushed_at": "2024-03-12T23:45:28Z",
+ "git_url": "git://github.com/hub4j/github-api.git",
+ "ssh_url": "git@github.com:hub4j/github-api.git",
+ "clone_url": "https://github.com/hub4j/github-api.git",
+ "svn_url": "https://github.com/hub4j/github-api",
+ "homepage": "https://github-api.kohsuke.org/",
+ "size": 47209,
+ "stargazers_count": 1090,
+ "watchers_count": 1090,
+ "language": "Java",
+ "has_issues": true,
+ "has_projects": true,
+ "has_downloads": true,
+ "has_wiki": true,
+ "has_pages": true,
+ "has_discussions": true,
+ "forks_count": 704,
+ "mirror_url": null,
+ "archived": false,
+ "disabled": false,
+ "open_issues_count": 154,
+ "license": {
+ "key": "mit",
+ "name": "MIT License",
+ "spdx_id": "MIT",
+ "url": "https://api.github.com/licenses/mit",
+ "node_id": "MDc6TGljZW5zZTEz"
+ },
+ "allow_forking": true,
+ "is_template": false,
+ "web_commit_signoff_required": false,
+ "topics": [
+ "api",
+ "client-library",
+ "github",
+ "github-api",
+ "github-api-v3",
+ "java",
+ "java-api"
+ ],
+ "visibility": "public",
+ "forks": 704,
+ "open_issues": 154,
+ "watchers": 1090,
+ "default_branch": "main"
+ },
+ "network_count": 704,
+ "subscribers_count": 1
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/1-user.json
new file mode 100644
index 0000000000..f27f181144
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/1-user.json
@@ -0,0 +1,49 @@
+{
+ "id": "e23625f3-8aa2-48ae-9aeb-e5dc645f55f3",
+ "name": "user",
+ "request": {
+ "url": "/user",
+ "method": "GET",
+ "headers": {
+ "Accept": {
+ "equalTo": "application/vnd.github.v3+json"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "1-user.json",
+ "headers": {
+ "Server": "GitHub.com",
+ "Date": "Wed, 13 Mar 2024 08:54:05 GMT",
+ "Content-Type": "application/json; charset=utf-8",
+ "Cache-Control": "private, max-age=60, s-maxage=60",
+ "Vary": [
+ "Accept, Authorization, Cookie, X-GitHub-OTP",
+ "Accept-Encoding, Accept, X-Requested-With"
+ ],
+ "ETag": "W/\"03b6fc1dc9210c8476fb4d8d5a0dbb36e2e7e6bc12839d7bf0a86f9105a65948\"",
+ "Last-Modified": "Mon, 11 Mar 2024 08:56:45 GMT",
+ "github-authentication-token-expiration": "2024-04-12 09:29:42 +0200",
+ "X-GitHub-Media-Type": "github.v3; format=json",
+ "x-github-api-version-selected": "2022-11-28",
+ "X-RateLimit-Limit": "5000",
+ "X-RateLimit-Remaining": "4971",
+ "X-RateLimit-Reset": "1710322407",
+ "X-RateLimit-Used": "29",
+ "X-RateLimit-Resource": "core",
+ "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset",
+ "Access-Control-Allow-Origin": "*",
+ "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+ "X-Frame-Options": "deny",
+ "X-Content-Type-Options": "nosniff",
+ "X-XSS-Protection": "0",
+ "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
+ "Content-Security-Policy": "default-src 'none'",
+ "X-GitHub-Request-Id": "4D88:3116FB:184AC74:1879A5D:65F169AD"
+ }
+ },
+ "uuid": "e23625f3-8aa2-48ae-9aeb-e5dc645f55f3",
+ "persistent": true,
+ "insertionIndex": 1
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/2-r_h_github-api.json b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/2-r_h_github-api.json
new file mode 100644
index 0000000000..37b288933c
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/2-r_h_github-api.json
@@ -0,0 +1,50 @@
+{
+ "id": "1e027d1b-af17-456f-88af-de973a74a34a",
+ "name": "repos_hub4j-test-org_github-api",
+ "request": {
+ "url": "/repos/hub4j-test-org/github-api",
+ "method": "GET",
+ "headers": {
+ "Accept": {
+ "equalTo": "application/vnd.github.v3+json"
+ }
+ }
+ },
+ "response": {
+ "status": 200,
+ "bodyFileName": "2-r_h_github-api.json",
+ "headers": {
+ "Server": "GitHub.com",
+ "Date": "Wed, 13 Mar 2024 08:54:05 GMT",
+ "Content-Type": "application/json; charset=utf-8",
+ "Cache-Control": "private, max-age=60, s-maxage=60",
+ "Vary": [
+ "Accept, Authorization, Cookie, X-GitHub-OTP",
+ "Accept-Encoding, Accept, X-Requested-With"
+ ],
+ "ETag": "W/\"15ec8f099b28d234345233ef57e40f4fea394e5e6e27b57cd1853b4f944840f4\"",
+ "Last-Modified": "Tue, 31 Jan 2023 10:03:44 GMT",
+ "github-authentication-token-expiration": "2024-04-12 09:29:42 +0200",
+ "X-GitHub-Media-Type": "github.v3; format=json",
+ "x-accepted-github-permissions": "metadata=read",
+ "x-github-api-version-selected": "2022-11-28",
+ "X-RateLimit-Limit": "5000",
+ "X-RateLimit-Remaining": "4969",
+ "X-RateLimit-Reset": "1710322407",
+ "X-RateLimit-Used": "31",
+ "X-RateLimit-Resource": "core",
+ "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset",
+ "Access-Control-Allow-Origin": "*",
+ "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+ "X-Frame-Options": "deny",
+ "X-Content-Type-Options": "nosniff",
+ "X-XSS-Protection": "0",
+ "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
+ "Content-Security-Policy": "default-src 'none'",
+ "X-GitHub-Request-Id": "299E:1F85E5:C1DE580:C320F81:65F169AD"
+ }
+ },
+ "uuid": "1e027d1b-af17-456f-88af-de973a74a34a",
+ "persistent": true,
+ "insertionIndex": 2
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/3-r_h_g_contents_ghcontent-ro_service-down.json b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/3-r_h_g_contents_ghcontent-ro_service-down.json
new file mode 100644
index 0000000000..2d5d37180b
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GitHubTest/wiremock/testCatchServiceDownException/mappings/3-r_h_g_contents_ghcontent-ro_service-down.json
@@ -0,0 +1,44 @@
+{
+ "id": "16923b89-9f2f-4f90-9c8a-169e90581e7a",
+ "name": "repos_hub4j-test-org_github-api_contents_ghcontent-ro_service-down",
+ "request": {
+ "url": "/repos/hub4j-test-org/github-api/contents/ghcontent-ro/service-down",
+ "method": "GET",
+ "headers": {
+ "Accept": {
+ "equalTo": "application/vnd.github.v3+json"
+ }
+ }
+ },
+ "response": {
+ "status": 503,
+ "body": "\n\n\n \n Unicorn! · GitHub\n \n \n \n\n \n
\n \n
\n\n
We had issues producing the response to your request.
\n
Sorry about that. Please try refreshing and contact us if the problem persists.
\n
\n\n
\n \n \n\n
\n \n \n
\n \n\n",
+ "headers": {
+ "Server": "GitHub.com",
+ "Date": "Wed, 13 Mar 2024 08:54:06 GMT",
+ "Content-Type": "text/html; charset=utf-8",
+ "github-authentication-token-expiration": "2024-04-12 09:29:42 +0200",
+ "X-GitHub-Media-Type": "github.v3; format=json",
+ "x-accepted-github-permissions": "contents=read",
+ "x-github-api-version-selected": "2022-11-28",
+ "X-RateLimit-Limit": "5000",
+ "X-RateLimit-Remaining": "4968",
+ "X-RateLimit-Reset": "1710322407",
+ "X-RateLimit-Used": "32",
+ "X-RateLimit-Resource": "core",
+ "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset",
+ "Access-Control-Allow-Origin": "*",
+ "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+ "X-Frame-Options": "deny",
+ "X-Content-Type-Options": "nosniff",
+ "X-XSS-Protection": "0",
+ "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
+ "Content-Security-Policy": "default-src 'none'",
+ "Vary": "Accept-Encoding, Accept, X-Requested-With",
+ "X-GitHub-Request-Id": "2EA8:339F21:17997EC:17C8638:65F169AE"
+ }
+ },
+ "uuid": "16923b89-9f2f-4f90-9c8a-169e90581e7a",
+ "persistent": true,
+ "insertionIndex": 3
+}
\ No newline at end of file