Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,14 @@
import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
import org.apache.hertzbeat.common.support.exception.SendMessageException;
import org.apache.hertzbeat.common.util.JsonUtil;
import org.apache.hertzbeat.common.util.LogUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -65,7 +61,6 @@ public class AlibabaSmsClientImpl implements SmsClient {
private final String accessKeySecret;
private final String signName;
private final String templateCode;
private static final Logger logger = LoggerFactory.getLogger(AlibabaSmsClientImpl.class);

public AlibabaSmsClientImpl(AlibabaSmsProperties config) {
if (config != null) {
Expand Down Expand Up @@ -154,31 +149,37 @@ private void sendSms(String phoneNumber, String templateParam) {
httpPost.setHeader("x-acs-content-sha256",
CryptoUtils.sha256Hex(""));

log.info("Sending Alibaba SMS request to {}", url + ", params: " + templateParam + "headers: " + Arrays.toString(httpPost.getAllHeaders()));
log.debug("Sending SMS request via Alibaba Cloud");

// Send request and handle response
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());

log.info("SMS response status: {}, body: {}", statusCode, responseBody);
log.debug("Alibaba Cloud SMS response status: {}", statusCode);

if (statusCode != 200) {
throw new SendMessageException("HTTP request failed with status code: " + statusCode + ", response: " + responseBody);
throw SmsFailureMessages.httpStatus("Alibaba Cloud SMS", statusCode);
}

JsonNode jsonResponse = JsonUtil.fromJson(responseBody);
if (jsonResponse == null || jsonResponse.get("Code") == null) {
throw SmsFailureMessages.invalidResponse("Alibaba Cloud SMS");
}
String code = jsonResponse.get("Code").asText();
if (!"OK".equals(code)) {
String message = jsonResponse.get("Message").asText();
throw new SendMessageException(code + ":" + message);
throw SmsFailureMessages.providerCode("Alibaba Cloud SMS", code);
}

log.info("Successfully sent SMS to phone: {}", phoneNumber);
log.info("Successfully sent SMS via Alibaba Cloud");
}
} catch (SendMessageException e) {
log.warn("Failed to send SMS via Alibaba Cloud");
throw e;
} catch (Exception e) {
LogUtil.warn(logger, "Failed to send SMS: {0}", e.getMessage());
throw new SendMessageException(e.getMessage());
log.warn("Failed to send SMS via Alibaba Cloud, failure type: {}",
e.getClass().getSimpleName());
throw SmsFailureMessages.requestFailed("Alibaba Cloud SMS");
}
}

Expand All @@ -196,7 +197,8 @@ private String calculateAuthorization(String canonicalQueryString, String timest
// Step 4: Build authorization header
return ALGORITHM + " Credential=" + accessKeyId + ",SignedHeaders=host;x-acs-action;x-acs-content-sha256;x-acs-date;" + "x-acs-signature-nonce;x-acs-version,Signature=" + signature;
} catch (Exception e) {
LogUtil.warn(logger, "Failed to calculate authorization {0}", e.getMessage());
log.warn("Failed to calculate Alibaba Cloud authorization, failure type: {}",
e.getClass().getSimpleName());
throw new RuntimeException("Failed to calculate authorization", e);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
Expand Down Expand Up @@ -116,11 +115,14 @@ private void send(String phoneNumber, String message) {
URI requestUri = new URI(endpoint);

HttpPost httpPost = createHttpPost(requestUri, amzDate, payloadInString);
log.info("Sending AWS SMS request to {}", requestUri + "," + "headers: " + Arrays.toString(httpPost.getAllHeaders()));
executeRequest(httpClient, httpPost, phoneNumber);
log.debug("Sending SMS request via AWS");
executeRequest(httpClient, httpPost);
} catch (SendMessageException e) {
log.warn("Failed to send SMS via AWS");
throw e;
} catch (Exception e) {
log.warn("Failed to send SMS: {}", e.getMessage());
throw new SendMessageException(e.getMessage());
log.warn("Failed to send SMS via AWS, failure type: {}", e.getClass().getSimpleName());
throw SmsFailureMessages.requestFailed("AWS SMS");
}
}

Expand Down Expand Up @@ -149,28 +151,27 @@ private HttpPost createHttpPost(URI requestUri, String amzDate, String payloadIn
return httpPost;
}

private void executeRequest(CloseableHttpClient httpClient, HttpPost httpPost, String phoneNumber) throws Exception {
private void executeRequest(CloseableHttpClient httpClient, HttpPost httpPost) throws Exception {
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());
log.info("SMS response status: {}, body: {}", statusCode, responseBody);
log.debug("AWS SMS response status: {}", statusCode);

if (statusCode != 200) {
throw new SendMessageException("HTTP request failed with status code: " + statusCode + ", response: " + responseBody);
throw SmsFailureMessages.httpStatus("AWS SMS", statusCode);
}

JsonNode jsonResponse = JsonUtil.fromJson(responseBody);
if (jsonResponse == null) {
throw new SendMessageException(statusCode + ":" + responseBody);
throw SmsFailureMessages.invalidResponse("AWS SMS");
}

JsonNode responseNode = jsonResponse.get("MessageId");
if (responseNode == null) {
throw new SendMessageException(statusCode + ":" + responseBody);
throw SmsFailureMessages.invalidResponse("AWS SMS");
}

String messageId = responseNode.asText();
log.info("Successfully sent SMS to phone: {}, messageId: {}", phoneNumber, messageId);
log.info("Successfully sent SMS via AWS");
}
}

Expand Down Expand Up @@ -285,5 +286,3 @@ private byte[] getSignatureKey(String key, String dateStamp, String regionName,

}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.hertzbeat.alert.service.impl;

import java.util.regex.Pattern;
import org.apache.hertzbeat.common.support.exception.SendMessageException;

/**
* Builds bounded SMS failures without copying provider-controlled response
* bodies, request URLs, or transport exception messages.
*/
final class SmsFailureMessages {

private static final Pattern SAFE_PROVIDER_CODE = Pattern.compile("[-A-Za-z0-9_.]{1,64}");
private static final String UNKNOWN_PROVIDER_CODE = "UNKNOWN_PROVIDER_ERROR";

private SmsFailureMessages() {
}

static SendMessageException requestFailed(String providerLabel) {
return new SendMessageException(providerLabel + " request failed");
}

static SendMessageException httpStatus(String providerLabel, int statusCode) {
return new SendMessageException(
providerLabel + " request failed with HTTP status " + statusCode);
}

static SendMessageException providerCode(String providerLabel, String code) {
String safeCode = code != null && SAFE_PROVIDER_CODE.matcher(code).matches()
? code
: UNKNOWN_PROVIDER_CODE;
return new SendMessageException(
providerLabel + " request failed (code: " + safeCode + ")");
}

static SendMessageException invalidResponse(String providerLabel) {
return new SendMessageException(providerLabel + " provider returned an invalid response");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public SmsLocalSmsClientImpl(SmslocalSmsProperties smslocalSmsProperties) {
@Override
public void sendMessage(NoticeReceiver receiver, NoticeTemplate noticeTemplate, GroupAlert alert) {
if (Objects.isNull(receiver) || Objects.isNull(alert)) {
log.warn("receiver and alert can not be null! receiver: {}, alert:{}", receiver, alert);
log.warn("SMSLocal receiver and alert cannot be null");
return;
}

Expand All @@ -79,36 +79,42 @@ public void sendMessage(NoticeReceiver receiver, NoticeTemplate noticeTemplate,
httpPost.setHeader("Token", config.getApiKey());
httpPost.setEntity(new StringEntity(payload, StandardCharsets.UTF_8));

log.debug("Sending SMS request to {}, payload: {}", httpPost.getURI(), payload);
log.debug("Sending SMS request via SMSLocal");

// send http request and handle response
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());

log.debug("SMS response status: {}, body: {}", statusCode, responseBody);
log.debug("SMSLocal response status: {}", statusCode);

if (statusCode != 200) {
throw new SendMessageException("HTTP request failed with status code: " + statusCode);
throw SmsFailureMessages.httpStatus("SMSLocal", statusCode);
}

JsonNode jsonResponse = JsonUtil.fromJson(responseBody);
if (jsonResponse == null || !jsonResponse.isArray() || jsonResponse.isEmpty()) {
throw SmsFailureMessages.invalidResponse("SMSLocal");
}
JsonNode jsonNode = jsonResponse.get(0);
if (Objects.isNull(jsonNode)) {
log.warn("jsonResponse parse errorCode failed: {}", jsonResponse);
return;
JsonNode errorCodeNode = jsonNode.get("errorCode");
if (errorCodeNode == null) {
throw SmsFailureMessages.invalidResponse("SMSLocal");
}
String errorCode = jsonNode.get("errorCode").asText();
String errorCode = errorCodeNode.asText();
if (!SUCCESS_CODE.equals(errorCode)) {
String msgid = jsonNode.get("id").asText();
throw new SendMessageException(errorCode + ":" + msgid);
throw SmsFailureMessages.providerCode("SMSLocal", errorCode);
}

log.info("Successfully sent SMS to phone: {}", receiver.getPhone());
log.info("Successfully sent SMS via SMSLocal");
}
} catch (SendMessageException e) {
log.warn("Failed to send SMS via SMSLocal");
throw e;
} catch (Exception e) {
log.error("Failed to send SMS: {}", e.getMessage());
throw new SendMessageException(e.getMessage());
log.warn("Failed to send SMS via SMSLocal, failure type: {}",
e.getClass().getSimpleName());
throw SmsFailureMessages.requestFailed("SMSLocal");
}

}
Expand All @@ -121,7 +127,7 @@ public String getType() {
@Override
public boolean checkConfig() {
if (Objects.isNull(config) || Objects.isNull(config.getApiKey()) || config.getApiKey().isBlank()) {
log.warn("smslocal properties can not be null: {}", config);
log.warn("SMSLocal properties cannot be null or blank");
return false;
}
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,41 +134,56 @@ public void sendSms(String appId, String signName, String templateId,
httpPost.setHeader("Authorization", authorization);
httpPost.setEntity(new StringEntity(payload, StandardCharsets.UTF_8));

log.debug("Sending SMS request to {}, payload: {}", httpPost.getURI(), payload);
log.debug("Sending SMS request via Tencent Cloud");

// send http request and handle response
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
int statusCode = response.getStatusLine().getStatusCode();
String responseBody = EntityUtils.toString(response.getEntity());

log.debug("SMS response status: {}, body: {}", statusCode, responseBody);
log.debug("Tencent Cloud SMS response status: {}", statusCode);

if (statusCode != 200) {
throw new SendMessageException("HTTP request failed with status code: " + statusCode);
throw SmsFailureMessages.httpStatus("Tencent Cloud SMS", statusCode);
}

JsonNode jsonResponse = JsonUtil.fromJson(responseBody);
if (jsonResponse == null) {
throw SmsFailureMessages.invalidResponse("Tencent Cloud SMS");
}
JsonNode responseNode = jsonResponse.get("Response");
if (responseNode == null) {
throw SmsFailureMessages.invalidResponse("Tencent Cloud SMS");
}
JsonNode error = responseNode.get("Error");
if (error != null) {
String code = error.get("Code").asText();
String message = error.get("Message").asText();
throw new SendMessageException(code + ":" + message);
JsonNode codeNode = error.get("Code");
if (codeNode == null) {
throw SmsFailureMessages.invalidResponse("Tencent Cloud SMS");
}
throw SmsFailureMessages.providerCode("Tencent Cloud SMS", codeNode.asText());
}
JsonNode sendStatusSet = responseNode.get("SendStatusSet");
if (sendStatusSet != null && sendStatusSet.isArray() && sendStatusSet.size() > 0) {
JsonNode firstStatus = sendStatusSet.get(0);
String code = firstStatus.get("Code").asText();
String message = firstStatus.get("Message").asText();
if (!RESPONSE_OK.equals(code)) {
throw new SendMessageException(code + ":" + message);
}
if (sendStatusSet == null || !sendStatusSet.isArray() || sendStatusSet.isEmpty()) {
throw SmsFailureMessages.invalidResponse("Tencent Cloud SMS");
}
JsonNode codeNode = sendStatusSet.get(0).get("Code");
if (codeNode == null) {
throw SmsFailureMessages.invalidResponse("Tencent Cloud SMS");
}
String code = codeNode.asText();
if (!RESPONSE_OK.equals(code)) {
throw SmsFailureMessages.providerCode("Tencent Cloud SMS", code);
}
log.info("Successfully sent SMS to phones: {}", String.join(",", phones));
log.info("Successfully sent SMS via Tencent Cloud");
}
} catch (SendMessageException e) {
log.warn("Failed to send SMS via Tencent Cloud");
throw e;
} catch (Exception e) {
log.warn("Failed to send SMS: {}", e.getMessage());
throw new SendMessageException(e.getMessage());
log.warn("Failed to send SMS via Tencent Cloud, failure type: {}",
e.getClass().getSimpleName());
throw SmsFailureMessages.requestFailed("Tencent Cloud SMS");
}
}

Expand Down
Loading
Loading