package com.getsentry.raven.connection; import com.getsentry.raven.environment.RavenEnvironment; import com.getsentry.raven.event.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.Set; /** * Abstract connection to a Sentry server. * <p> * Provide the basic tools to submit events to the server (authentication header, dsn).<br> * To avoid spamming the network if and when Sentry is down, automatically lock the connection each time a * {@link ConnectionException} is caught. */ public abstract class AbstractConnection implements Connection { /** * Current Sentry protocol version. */ public static final String SENTRY_PROTOCOL_VERSION = "6"; private static final Logger logger = LoggerFactory.getLogger(AbstractConnection.class); /** * Value of the X-Sentry-Auth header. */ private final String authHeader; /** * Set of callbacks that will be called when an exception occurs while attempting to * send events to the Sentry server. */ private Set<EventSendFailureCallback> eventSendFailureCallbacks; private LockdownManager lockdownManager; /** * Creates a connection based on the public and secret keys. * * @param publicKey public key (identifier) to the Sentry server. * @param secretKey secret key (password) to the Sentry server. */ protected AbstractConnection(String publicKey, String secretKey) { this.lockdownManager = new LockdownManager(); this.eventSendFailureCallbacks = new HashSet<>(); this.authHeader = "Sentry sentry_version=" + SENTRY_PROTOCOL_VERSION + "," + "sentry_client=" + RavenEnvironment.getRavenName() + "," + "sentry_key=" + publicKey + "," + "sentry_secret=" + secretKey; } /** * Creates an authentication header for the Sentry protocol. * * @return an authentication header as a String. */ protected String getAuthHeader() { return authHeader; } @Override public final void send(Event event) throws ConnectionException { try { if (lockdownManager.isLockedDown()) { /* An exception is thrown to signal that this Event was not sent, which may be important in, for example, a BufferedConnection where the Event would be deleted from the Buffer if an exception isn't raised in the call to send. */ throw new LockedDownException("Dropping an Event due to lockdown: " + event); } doSend(event); lockdownManager.resetState(); } catch (ConnectionException e) { for (EventSendFailureCallback eventSendFailureCallback : eventSendFailureCallbacks) { try { eventSendFailureCallback.onFailure(event, e); } catch (Exception exc) { logger.warn("An exception occurred while running an EventSendFailureCallback: " + eventSendFailureCallback.getClass().getName(), exc); } } logger.warn("An exception due to the connection occurred, a lockdown will be initiated.", e); lockdownManager.setState(e); throw e; } } /** * Sends an event to the Sentry server. * * @param event captured event to add in Sentry. * @throws ConnectionException whenever a temporary exception due to the connection happened. */ protected abstract void doSend(Event event) throws ConnectionException; /** * Add a callback that is called when an exception occurs while attempting to * send events to the Sentry server. * * @param eventSendFailureCallback callback instance */ public void addEventSendFailureCallback(EventSendFailureCallback eventSendFailureCallback) { eventSendFailureCallbacks.add(eventSendFailureCallback); } }