parent
958ada9110
commit
f8063f8faf
|
@ -54,6 +54,7 @@ import org.whispersystems.textsecuregcm.providers.MemcachedClientFactory;
|
|||
import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
|
||||
import org.whispersystems.textsecuregcm.providers.RedisHealthCheck;
|
||||
import org.whispersystems.textsecuregcm.providers.TimeProvider;
|
||||
import org.whispersystems.textsecuregcm.push.FeedbackHandler;
|
||||
import org.whispersystems.textsecuregcm.push.PushSender;
|
||||
import org.whispersystems.textsecuregcm.push.PushServiceClient;
|
||||
import org.whispersystems.textsecuregcm.push.WebsocketSender;
|
||||
|
@ -158,8 +159,11 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
|||
SmsSender smsSender = new SmsSender(twilioSmsSender, nexmoSmsSender, config.getTwilioConfiguration().isInternational());
|
||||
UrlSigner urlSigner = new UrlSigner(config.getS3Configuration());
|
||||
PushSender pushSender = new PushSender(pushServiceClient, websocketSender);
|
||||
FeedbackHandler feedbackHandler = new FeedbackHandler(pushServiceClient, accountsManager);
|
||||
Optional<byte[]> authorizationKey = config.getRedphoneConfiguration().getAuthorizationKey();
|
||||
|
||||
environment.lifecycle().manage(feedbackHandler);
|
||||
|
||||
AttachmentController attachmentController = new AttachmentController(rateLimiters, federatedClientManager, urlSigner);
|
||||
KeysControllerV1 keysControllerV1 = new KeysControllerV1(rateLimiters, keys, accountsManager, federatedClientManager);
|
||||
KeysControllerV2 keysControllerV2 = new KeysControllerV2(rateLimiters, keys, accountsManager, federatedClientManager);
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
|
||||
public class UnregisteredEvent {
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String registrationId;
|
||||
|
||||
@JsonProperty
|
||||
@NotEmpty
|
||||
private String number;
|
||||
|
||||
@JsonProperty
|
||||
@Min(1)
|
||||
private int deviceId;
|
||||
|
||||
public String getRegistrationId() {
|
||||
return registrationId;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public int getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class UnregisteredEventList {
|
||||
|
||||
@JsonProperty
|
||||
private List<UnregisteredEvent> devices;
|
||||
|
||||
public List<UnregisteredEvent> getDevices() {
|
||||
if (devices == null) return new LinkedList<>();
|
||||
else return devices;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package org.whispersystems.textsecuregcm.push;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.entities.UnregisteredEvent;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.dropwizard.lifecycle.Managed;
|
||||
|
||||
public class FeedbackHandler implements Managed, Runnable {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PushServiceClient.class);
|
||||
|
||||
private final PushServiceClient client;
|
||||
private final AccountsManager accountsManager;
|
||||
|
||||
private ScheduledExecutorService executor;
|
||||
|
||||
public FeedbackHandler(PushServiceClient client, AccountsManager accountsManager) {
|
||||
this.client = client;
|
||||
this.accountsManager = accountsManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
this.executor = Executors.newSingleThreadScheduledExecutor();
|
||||
this.executor.scheduleAtFixedRate(this, 0, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
if (this.executor != null) {
|
||||
this.executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
List<UnregisteredEvent> gcmFeedback = client.getGcmFeedback();
|
||||
List<UnregisteredEvent> apnFeedback = client.getApnFeedback();
|
||||
|
||||
for (UnregisteredEvent gcmEvent : gcmFeedback) {
|
||||
handleGcmUnregistered(gcmEvent);
|
||||
}
|
||||
|
||||
for (UnregisteredEvent apnEvent : apnFeedback) {
|
||||
handleApnUnregistered(apnEvent);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.warn("Error retrieving feedback: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGcmUnregistered(UnregisteredEvent event) {
|
||||
logger.warn("Got GCM Unregistered: " + event.getNumber() + "," + event.getDeviceId());
|
||||
|
||||
Optional<Account> account = accountsManager.get(event.getNumber());
|
||||
|
||||
if (account.isPresent()) {
|
||||
Optional<Device> device = account.get().getDevice(event.getDeviceId());
|
||||
|
||||
if (device.isPresent()) {
|
||||
if (event.getRegistrationId().equals(device.get().getGcmId())) {
|
||||
logger.warn("GCM Unregister GCM ID matches!");
|
||||
device.get().setGcmId(null);
|
||||
accountsManager.update(account.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleApnUnregistered(UnregisteredEvent event) {
|
||||
logger.warn("Got APN Unregistered: " + event.getNumber() + "," + event.getDeviceId());
|
||||
|
||||
Optional<Account> account = accountsManager.get(event.getNumber());
|
||||
|
||||
if (account.isPresent()) {
|
||||
Optional<Device> device = account.get().getDevice(event.getDeviceId());
|
||||
|
||||
if (device.isPresent()) {
|
||||
if (event.getRegistrationId().equals(device.get().getApnId())) {
|
||||
logger.warn("APN Unregister APN ID matches!");
|
||||
device.get().setApnId(null);
|
||||
accountsManager.update(account.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,29 @@
|
|||
package org.whispersystems.textsecuregcm.push;
|
||||
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.api.client.ClientHandlerException;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.UniformInterfaceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
|
||||
import org.whispersystems.textsecuregcm.entities.ApnMessage;
|
||||
import org.whispersystems.textsecuregcm.entities.GcmMessage;
|
||||
import org.whispersystems.textsecuregcm.entities.UnregisteredEvent;
|
||||
import org.whispersystems.textsecuregcm.entities.UnregisteredEventList;
|
||||
import org.whispersystems.textsecuregcm.util.Base64;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class PushServiceClient {
|
||||
|
||||
private static final String PUSH_GCM_PATH = "/api/v1/push/gcm";
|
||||
private static final String PUSH_APN_PATH = "/api/v1/push/apn";
|
||||
private static final String PUSH_GCM_PATH = "/api/v1/push/gcm";
|
||||
private static final String PUSH_APN_PATH = "/api/v1/push/apn";
|
||||
|
||||
private static final String APN_FEEDBACK_PATH = "/api/v1/feedback/apn";
|
||||
private static final String GCM_FEEDBACK_PATH = "/api/v1/feedback/gcm";
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(PushServiceClient.class);
|
||||
|
||||
|
@ -38,15 +47,41 @@ public class PushServiceClient {
|
|||
sendPush(PUSH_APN_PATH, message);
|
||||
}
|
||||
|
||||
private void sendPush(String path, Object entity) throws TransientPushFailureException {
|
||||
ClientResponse response = client.resource("http://" + host + ":" + port + path)
|
||||
.header("Authorization", authorization)
|
||||
.entity(entity, MediaType.APPLICATION_JSON)
|
||||
.put(ClientResponse.class);
|
||||
public List<UnregisteredEvent> getGcmFeedback() throws IOException {
|
||||
return getFeedback(GCM_FEEDBACK_PATH);
|
||||
}
|
||||
|
||||
if (response.getStatus() != 204 && response.getStatus() != 200) {
|
||||
logger.warn("PushServer response: " + response.getStatus() + " " + response.getStatusInfo().getReasonPhrase());
|
||||
throw new TransientPushFailureException("Bad response: " + response.getStatus());
|
||||
public List<UnregisteredEvent> getApnFeedback() throws IOException {
|
||||
return getFeedback(APN_FEEDBACK_PATH);
|
||||
}
|
||||
|
||||
private void sendPush(String path, Object entity) throws TransientPushFailureException {
|
||||
try {
|
||||
ClientResponse response = client.resource("http://" + host + ":" + port + path)
|
||||
.header("Authorization", authorization)
|
||||
.entity(entity, MediaType.APPLICATION_JSON)
|
||||
.put(ClientResponse.class);
|
||||
|
||||
if (response.getStatus() != 204 && response.getStatus() != 200) {
|
||||
logger.warn("PushServer response: " + response.getStatus() + " " + response.getStatusInfo().getReasonPhrase());
|
||||
throw new TransientPushFailureException("Bad response: " + response.getStatus());
|
||||
}
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
logger.warn("Push error: ", e);
|
||||
throw new TransientPushFailureException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<UnregisteredEvent> getFeedback(String path) throws IOException {
|
||||
try {
|
||||
UnregisteredEventList unregisteredEvents = client.resource("http://" + host + ":" + port + path)
|
||||
.header("Authorization", authorization)
|
||||
.get(UnregisteredEventList.class);
|
||||
|
||||
return unregisteredEvents.getDevices();
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
logger.warn("Request error:", e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue