From 4aafd4362caf198bdea97d2fdf2957aa5c345465 Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Tue, 27 Sep 2011 08:22:26 -0400 Subject: [PATCH] Poke test during proposal phase. --- src/com/gitblit/FederationServlet.java | 31 ++++++++- src/com/gitblit/GitBlit.java | 3 tests/com/gitblit/tests/FederationTests.java | 10 ++- src/com/gitblit/wicket/pages/SendProposalPage.java | 38 +++++++++++- src/com/gitblit/Constants.java | 14 ++++ src/com/gitblit/utils/FederationUtils.java | 48 ++++++++++++++- 6 files changed, 124 insertions(+), 20 deletions(-) diff --git a/src/com/gitblit/Constants.java b/src/com/gitblit/Constants.java index b52f055..befa5fe 100644 --- a/src/com/gitblit/Constants.java +++ b/src/com/gitblit/Constants.java @@ -115,7 +115,7 @@ * Enumeration representing the types of federation requests. */ public static enum FederationRequest { - PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_SETTINGS, STATUS; + POKE, PROPOSAL, PULL_REPOSITORIES, PULL_USERS, PULL_SETTINGS, STATUS; public static FederationRequest fromName(String name) { for (FederationRequest type : values()) { @@ -181,4 +181,16 @@ } } + /** + * Enumeration representing the possible results of federation proposal + * requests. + */ + public static enum FederationProposalResult { + ERROR, FEDERATION_DISABLED, MISSING_DATA, NO_PROPOSALS, NO_POKE, ACCEPTED; + + @Override + public String toString() { + return name(); + } + } } diff --git a/src/com/gitblit/FederationServlet.java b/src/com/gitblit/FederationServlet.java index 784ec33..7dc5d6a 100644 --- a/src/com/gitblit/FederationServlet.java +++ b/src/com/gitblit/FederationServlet.java @@ -35,6 +35,7 @@ import com.gitblit.models.FederationProposal; import com.gitblit.models.RepositoryModel; import com.gitblit.models.UserModel; +import com.gitblit.utils.FederationUtils; import com.gitblit.utils.HttpUtils; import com.gitblit.utils.StringUtils; import com.gitblit.utils.TimeUtils; @@ -110,6 +111,16 @@ private void processRequest(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, java.io.IOException { + FederationRequest reqType = FederationRequest.fromName(request.getParameter("req")); + logger.info(MessageFormat.format("Federation {0} request from {1}", reqType, + request.getRemoteAddr())); + + if (FederationRequest.POKE.equals(reqType)) { + // Gitblit always responds to POKE requests to verify a connection + logger.info("Received federation POKE from " + request.getRemoteAddr()); + return; + } + if (!GitBlit.getBoolean(Keys.git.enableGitServlet, true)) { logger.warn(Keys.git.enableGitServlet + " must be set TRUE for federation requests."); response.sendError(HttpServletResponse.SC_FORBIDDEN); @@ -123,11 +134,6 @@ response.sendError(HttpServletResponse.SC_FORBIDDEN); return; } - - String token = request.getParameter("token"); - FederationRequest reqType = FederationRequest.fromName(request.getParameter("req")); - logger.info(MessageFormat.format("Federation {0} request from {1}", reqType, - request.getRemoteAddr())); if (FederationRequest.PROPOSAL.equals(reqType)) { // Receive a gitblit federation proposal @@ -156,6 +162,20 @@ logger.error(MessageFormat.format("Rejected {0} federation proposal from {1}", proposal.tokenType.name(), proposal.url)); response.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + return; + } + + // poke the origin Gitblit instance that is proposing federation + boolean poked = false; + try { + poked = FederationUtils.poke(proposal.url); + } catch (Exception e) { + logger.error("Failed to poke origin", e); + } + if (!poked) { + logger.error(MessageFormat.format("Failed to send federation poke to {0}", + proposal.url)); + response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); return; } @@ -207,6 +227,7 @@ } // Determine the federation tokens for this gitblit instance + String token = request.getParameter("token"); List<String> tokens = GitBlit.self().getFederationTokens(); if (!tokens.contains(token)) { logger.warn(MessageFormat.format( diff --git a/src/com/gitblit/GitBlit.java b/src/com/gitblit/GitBlit.java index 62b93e7..f7c43a8 100644 --- a/src/com/gitblit/GitBlit.java +++ b/src/com/gitblit/GitBlit.java @@ -971,7 +971,8 @@ * @param proposal * the proposal * @param gitblitUrl - * the url of your gitblit instance + * the url of your gitblit instance to send an email to + * administrators * @return true if the proposal was submitted */ public boolean submitFederationProposal(FederationProposal proposal, String gitblitUrl) { diff --git a/src/com/gitblit/utils/FederationUtils.java b/src/com/gitblit/utils/FederationUtils.java index 9fd8817..fde9557 100644 --- a/src/com/gitblit/utils/FederationUtils.java +++ b/src/com/gitblit/utils/FederationUtils.java @@ -45,6 +45,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.gitblit.Constants.FederationProposalResult; import com.gitblit.Constants.FederationRequest; import com.gitblit.FederationServlet; import com.gitblit.IStoredSettings; @@ -82,7 +83,7 @@ private static final SSLContext SSL_CONTEXT; private static final DummyHostnameVerifier HOSTNAME_VERIFIER; - + private static final Logger LOGGER = LoggerFactory.getLogger(FederationUtils.class); static { @@ -182,21 +183,60 @@ } /** + * Sends a federation poke to the Gitblit instance at remoteUrl. Pokes are + * sent by an pulling Gitblit instance to an origin Gitblit instance as part + * of the proposal process. This is to ensure that the pulling Gitblit + * instance has an IP route to the origin instance. + * + * @param remoteUrl + * the remote Gitblit instance to send a federation proposal to + * @param proposal + * a complete federation proposal + * @return true if there is a route to the remoteUrl + */ + public static boolean poke(String remoteUrl) throws Exception { + String url = FederationServlet.asFederationLink(remoteUrl, null, FederationRequest.POKE); + Gson gson = new Gson(); + String json = gson.toJson("POKE"); + int status = writeJson(url, json); + return status == HttpServletResponse.SC_OK; + } + + /** * Sends a federation proposal to the Gitblit instance at remoteUrl * * @param remoteUrl * the remote Gitblit instance to send a federation proposal to * @param proposal * a complete federation proposal - * @return true if the proposal was received + * @return the federation proposal result code */ - public static boolean propose(String remoteUrl, FederationProposal proposal) throws Exception { + public static FederationProposalResult propose(String remoteUrl, FederationProposal proposal) + throws Exception { String url = FederationServlet .asFederationLink(remoteUrl, null, FederationRequest.PROPOSAL); Gson gson = new GsonBuilder().setPrettyPrinting().create(); String json = gson.toJson(proposal); int status = writeJson(url, json); - return status == HttpServletResponse.SC_OK; + switch (status) { + case HttpServletResponse.SC_FORBIDDEN: + // remote Gitblit Federation disabled + return FederationProposalResult.FEDERATION_DISABLED; + case HttpServletResponse.SC_BAD_REQUEST: + // remote Gitblit did not receive any JSON data + return FederationProposalResult.MISSING_DATA; + case HttpServletResponse.SC_METHOD_NOT_ALLOWED: + // remote Gitblit not accepting proposals + return FederationProposalResult.NO_PROPOSALS; + case HttpServletResponse.SC_NOT_ACCEPTABLE: + // remote Gitblit failed to poke this Gitblit instance + return FederationProposalResult.NO_POKE; + case HttpServletResponse.SC_OK: + // received + return FederationProposalResult.ACCEPTED; + default: + return FederationProposalResult.ERROR; + } } /** diff --git a/src/com/gitblit/wicket/pages/SendProposalPage.java b/src/com/gitblit/wicket/pages/SendProposalPage.java index ea91f1b..635b432 100644 --- a/src/com/gitblit/wicket/pages/SendProposalPage.java +++ b/src/com/gitblit/wicket/pages/SendProposalPage.java @@ -26,6 +26,7 @@ import org.apache.wicket.markup.html.form.TextField; import org.apache.wicket.model.CompoundPropertyModel; +import com.gitblit.Constants.FederationProposalResult; import com.gitblit.GitBlit; import com.gitblit.models.FederationProposal; import com.gitblit.models.RepositoryModel; @@ -78,17 +79,44 @@ error("Please enter a destination url for your proposal!"); return; } - + // build new proposal FederationProposal proposal = GitBlit.self().createFederationProposal(myUrl, token); proposal.url = myUrl; proposal.message = message; try { - if (FederationUtils.propose(destinationUrl, proposal)) { - info(MessageFormat.format("Proposal successfully received by {0}.", destinationUrl)); + FederationProposalResult res = FederationUtils + .propose(destinationUrl, proposal); + switch (res) { + case ACCEPTED: + info(MessageFormat.format("Proposal successfully received by {0}.", + destinationUrl)); setResponsePage(RepositoriesPage.class); - } else { - error(MessageFormat.format("Sorry, {0} rejected your proposal.", destinationUrl)); + break; + case NO_POKE: + error(MessageFormat.format( + "Sorry, {0} could not find a Gitblit instance at {1}.", + destinationUrl, myUrl)); + break; + case NO_PROPOSALS: + error(MessageFormat.format( + "Sorry, {0} is not accepting proposals at this time.", + destinationUrl)); + break; + case FEDERATION_DISABLED: + error(MessageFormat + .format("Sorry, {0} is not configured to federate with any Gitblit instances.", + destinationUrl)); + break; + case MISSING_DATA: + error(MessageFormat.format("Sorry, {0} did not receive any proposal data!", + destinationUrl)); + break; + case ERROR: + error(MessageFormat.format( + "Sorry, {0} reports that an unexpected error occurred!", + destinationUrl)); + break; } } catch (Exception e) { if (!StringUtils.isEmpty(e.getMessage())) { diff --git a/tests/com/gitblit/tests/FederationTests.java b/tests/com/gitblit/tests/FederationTests.java index 28b089f..c5f7e8d 100644 --- a/tests/com/gitblit/tests/FederationTests.java +++ b/tests/com/gitblit/tests/FederationTests.java @@ -24,6 +24,7 @@ import junit.framework.TestCase; import com.gitblit.Constants.AccessRestrictionType; +import com.gitblit.Constants.FederationProposalResult; import com.gitblit.Constants.FederationRequest; import com.gitblit.Constants.FederationToken; import com.gitblit.FederationServlet; @@ -91,14 +92,15 @@ "testtoken", repositories); // propose federation - assertTrue("proposal refused", - FederationUtils.propose("http://localhost:" + port, proposal)); + assertEquals("proposal refused", + FederationUtils.propose("http://localhost:" + port, proposal), + FederationProposalResult.NO_PROPOSALS); } public void testPullRepositories() throws Exception { try { - String url = FederationServlet.asFederationLink("http://localhost:" + port, "testtoken", - FederationRequest.PULL_REPOSITORIES); + String url = FederationServlet.asFederationLink("http://localhost:" + port, + "testtoken", FederationRequest.PULL_REPOSITORIES); String json = FederationUtils.readJson(url); } catch (IOException e) { if (!e.getMessage().contains("403")) { -- Gitblit v1.9.1