use of org.mobicents.tools.heartbeat.api.Node in project load-balancer by RestComm.
the class SIPBalancerForwarder method processRequest.
/*
* (non-Javadoc)
* @see javax.sip.SipListener#processRequest(javax.sip.RequestEvent)
*/
public void processRequest(RequestEvent requestEvent) {
// This will be invoked only by external endpoint
BalancerAppContent content = (BalancerAppContent) requestEvent.getSource();
boolean isIpv6 = content.isIpv6();
final SipProvider sipProvider = content.getProvider();
final Request request = requestEvent.getRequest();
final String requestMethod = request.getMethod();
if (logger.isDebugEnabled()) {
logger.debug("got request:\n" + request);
}
if ((requestMethod.equals(Request.OPTIONS) || requestMethod.equals(Request.INFO)) && request.getHeader("Mobicents-Heartbeat") != null && sipProvider == balancerRunner.balancerContext.internalSipProvider) {
byte[] bytes = (byte[]) request.getContent();
Properties prop = new Properties();
try {
prop.load(new ByteArrayInputStream(bytes, 0, bytes.length));
Node node = new Node(prop.getProperty("hostname"), prop.getProperty("ip"));
for (String id : prop.stringPropertyNames()) {
node.getProperties().put(id, prop.getProperty(id));
}
ArrayList<Node> list = new ArrayList<Node>();
list.add(node);
this.register.handlePingInRegister(list);
Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.OK, request);
sipProvider.sendResponse(response);
return;
} catch (Exception e) {
logger.error("Failure parsing heartbeat properties from this request " + request, e);
}
}
// Issue 10: https://telestax.atlassian.net/browse/LB-10
if (request.getContent() != null || (requestMethod.equals(Request.REGISTER) && sipProvider != balancerRunner.balancerContext.internalSipProvider)) {
SIPMessage message = (SIPMessage) request;
String initialRemoteAddr = message.getPeerPacketSourceAddress().getHostAddress();
String initialRemotePort = String.valueOf(message.getPeerPacketSourcePort());
Header remoteAddrHeader = null;
Header remotePortHeader = null;
try {
remoteAddrHeader = SipFactory.getInstance().createHeaderFactory().createHeader("X-Sip-Balancer-InitialRemoteAddr", initialRemoteAddr);
remotePortHeader = SipFactory.getInstance().createHeaderFactory().createHeader("X-Sip-Balancer-InitialRemotePort", initialRemotePort);
} catch (PeerUnavailableException e) {
logger.error("Unexpected exception while creating custom headers for REGISTER message ", e);
} catch (ParseException e) {
logger.error("Unexpected exception while creating custom headers for REGISTER message ", e);
}
if (remoteAddrHeader != null)
request.addHeader(remoteAddrHeader);
if (remotePortHeader != null)
request.addHeader(remotePortHeader);
}
try {
updateStats(request);
forwardRequest(sipProvider, request, isIpv6);
} catch (Throwable throwable) {
logger.error("Unexpected exception while forwarding the request " + request, throwable);
if (!Request.ACK.equalsIgnoreCase(requestMethod)) {
try {
Response response = balancerRunner.balancerContext.messageFactory.createResponse(Response.SERVER_INTERNAL_ERROR, request);
sipProvider.sendResponse(response);
} catch (Exception e) {
logger.error("Unexpected exception while trying to send the error response for this " + request, e);
}
}
}
}
use of org.mobicents.tools.heartbeat.api.Node in project load-balancer by RestComm.
the class SIPBalancerForwarder method setExtraServerNodes.
private void setExtraServerNodes(String extraServerNodesString) {
ArrayList<Node> extraServerNodes = new ArrayList<Node>();
extraServerAddresses = extraServerNodesString.split(",");
extraServerPorts = new int[extraServerAddresses.length];
for (int q = 0; q < extraServerAddresses.length; q++) {
int indexOfPort = extraServerAddresses[q].indexOf(':');
if (indexOfPort > 0) {
extraServerPorts[q] = Integer.parseInt(extraServerAddresses[q].substring(indexOfPort + 1, extraServerAddresses[q].length()));
extraServerAddresses[q] = extraServerAddresses[q].substring(0, indexOfPort);
} else {
extraServerPorts[q] = 5060;
}
ExtraServerNode extraServerNode = new ExtraServerNode("ExtraServerNode" + q + "-" + extraServerAddresses[q] + ":" + extraServerPorts[q], extraServerAddresses[q]);
HashMap<String, String> properties = new HashMap<String, String>();
properties.put("udpPort", "" + extraServerPorts[q]);
properties.put("tcpPort", "" + extraServerPorts[q]);
properties.put("httpPort", "808" + q);
properties.put("version", "0");
extraServerNode.setProperties(properties);
extraServerNodes.add(extraServerNode);
logger.info("Extra Server: " + extraServerAddresses[q] + ":" + extraServerPorts[q]);
}
if (balancerRunner.balancerContext.lbConfig.getSipConfiguration().isPerformanceTestingMode()) {
register.handlePingInRegister(extraServerNodes);
logger.info("Extra Servers registered as active nodes!");
}
}
use of org.mobicents.tools.heartbeat.api.Node in project load-balancer by RestComm.
the class SIPBalancerForwarder method removeRouteHeadersMeantForLB.
/**
* Remove the different route headers that are meant for the Load balancer.
* There is two cases here :
* <ul>
* <li>* Requests coming from external and going to the cluster : dialog creating requests can have route header so that they go through the LB and subsequent requests
* will have route headers since the LB record routed</li>
* <li>* Requests coming from the cluster and going to external : dialog creating requests can have route header so that they go through the LB - those requests will define in the route header
* the originating node of the request so that that subsequent requests are routed to the originating node if still alive</li>
* </ul>
*
* @param request
*/
private RouteHeaderHints removeRouteHeadersMeantForLB(Request request, boolean isIpv6) {
if (logger.isDebugEnabled()) {
logger.debug("Checking if there is any route headers meant for the LB to remove...");
}
Node node = null;
String callVersion = null;
int numberOfRemovedRouteHeaders = 0;
if (balancerRunner.balancerContext.matchingHostnameForRoute != null) {
RouteHeader routeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME);
if (routeHeader != null) {
if (logger.isDebugEnabled()) {
logger.debug("Matching host name for route is : " + balancerRunner.balancerContext.matchingHostnameForRoute);
logger.debug("Matching host name and subdomain: " + balancerRunner.balancerContext.isFilterSubdomain);
}
if (!balancerRunner.balancerContext.isFilterSubdomain) {
if (((SipURI) routeHeader.getAddress().getURI()).getHost().equals(balancerRunner.balancerContext.matchingHostnameForRoute))
request.removeFirst(RouteHeader.NAME);
} else {
if (((SipURI) routeHeader.getAddress().getURI()).getHost().endsWith("." + balancerRunner.balancerContext.matchingHostnameForRoute))
request.removeFirst(RouteHeader.NAME);
}
}
}
// Removing first routeHeader if it is for the sip balancer
RouteHeader routeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME);
if (routeHeader != null) {
SipURI routeUri = (SipURI) routeHeader.getAddress().getURI();
callVersion = routeUri.getParameter(ROUTE_PARAM_NODE_VERSION);
String transport = ((ViaHeader) request.getHeader("Via")).getTransport();
if (routeUri.getTransportParam() != null)
transport = routeUri.getTransportParam();
if (!isHeaderExternal(routeUri.getHost(), routeUri.getPort(), transport, isIpv6)) {
if (logger.isDebugEnabled()) {
logger.debug("this route header is for the LB removing it " + routeUri);
}
numberOfRemovedRouteHeaders++;
request.removeFirst(RouteHeader.NAME);
routeHeader = (RouteHeader) request.getHeader(RouteHeader.NAME);
// since we used double record routing we may have 2 routes corresponding to us here
// for ACK and BYE from caller for example
node = checkRouteHeaderForSipNode(routeUri);
if (routeHeader != null) {
routeUri = (SipURI) routeHeader.getAddress().getURI();
transport = ((ViaHeader) request.getHeader("Via")).getTransport();
if (routeUri.getTransportParam() != null)
transport = routeUri.getTransportParam();
if (!isHeaderExternal(routeUri.getHost(), routeUri.getPort(), transport, isIpv6)) {
if (logger.isDebugEnabled()) {
logger.debug("this route header is for the LB removing it " + routeUri);
}
numberOfRemovedRouteHeaders++;
request.removeFirst(RouteHeader.NAME);
if (node == null) {
node = checkRouteHeaderForSipNode(routeUri);
}
// SIPP sometimes appends more headers and lets remove them here. There is no legitimate reason
// more than two SIP LB headers to be place next to each-other, so this cleanup is SAFE!
boolean moreHeaders = true;
while (moreHeaders) {
RouteHeader extraHeader = (RouteHeader) request.getHeader(RouteHeader.NAME);
if (extraHeader != null) {
SipURI u = (SipURI) extraHeader.getAddress().getURI();
transport = ((ViaHeader) request.getHeader("Via")).getTransport();
if (u.getTransportParam() != null)
transport = u.getTransportParam();
if (!isHeaderExternal(u.getHost(), u.getPort(), transport, isIpv6)) {
numberOfRemovedRouteHeaders++;
request.removeFirst(RouteHeader.NAME);
} else {
moreHeaders = false;
}
} else {
moreHeaders = false;
}
}
}
}
}
}
if (node == null) {
if (request.getRequestURI().isSipURI()) {
node = checkRouteHeaderForSipNode((SipURI) request.getRequestURI());
}
}
// logger.info(request.ge + " has this hint " + node);
ToHeader to = (ToHeader) (request.getHeader(ToHeader.NAME));
/*
* We determine if this is subsequent based on To tag instead of checking route header metadata.
*/
boolean subsequent = to.getTag() != null;
if (logger.isDebugEnabled()) {
logger.debug("Number of removed Route headers is " + numberOfRemovedRouteHeaders);
}
// https://github.com/RestComm/load-balancer/issues/128
if (numberOfRemovedRouteHeaders != 2 && subsequent && !request.getMethod().equalsIgnoreCase(Request.ACK)) {
logger.warn("A subsequent request should have two Route headers. Number of removed Route headers is " + numberOfRemovedRouteHeaders + ". This indicates a client is removing important headers.");
}
return new RouteHeaderHints(node, subsequent, callVersion);
}
use of org.mobicents.tools.heartbeat.api.Node in project load-balancer by RestComm.
the class HttpResponseHandler method handleHttpResponse.
private void handleHttpResponse(ChannelHandlerContext ctx, final MessageEvent e) throws Exception {
if (e.getMessage() instanceof HttpChunkTrailer) {
HttpChunkTrailer chunk = (HttpChunkTrailer) e.getMessage();
balancerRunner.balancerContext.httpBytesToClient.addAndGet(chunk.getContent().capacity());
if (chunk.isLast())
readingChunks = false;
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
Channel channel = null;
if (ac != null)
channel = ac.getChannel();
if (channel != null) {
if (logger.isDebugEnabled())
logger.debug("Send chunked response from : " + e.getChannel().getRemoteAddress() + " to : " + channel.getRemoteAddress() + " capacity : " + chunk.getContent().capacity());
channel.write(chunk);
}
} else if (!readingChunks || !(e.getMessage() instanceof DefaultHttpChunk)) {
response = (HttpResponse) e.getMessage();
int stsusCode = response.getStatus().getCode();
if (stsusCode > 399 && stsusCode < 600) {
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
if (ac != null && ac.isCheckNeed()) {
InetSocketAddress address = (InetSocketAddress) e.getChannel().getRemoteAddress();
InvocationContext invocationContext = balancerRunner.getLatestInvocationContext();
KeySip keySip = new KeySip(address.getHostString(), address.getPort(), false);
Node currNode = invocationContext.sipNodeMap(false).get(keySip);
if (currNode != null) {
currNode.setBad(true);
String instanseId = currNode.getProperties().get(Protocol.RESTCOMM_INSTANCE_ID);
if (instanseId != null)
invocationContext.httpNodeMap.get(new KeyHttp(currNode.getProperties().get(Protocol.RESTCOMM_INSTANCE_ID))).setBad(true);
logger.error("Error code [" + stsusCode + "] detected in HTTP response. From node : " + currNode + ". This node will marked as bad.");
String currInstanceId = (String) currNode.getProperties().get("Restcomm-Instance-Id");
if (currInstanceId != null)
logger.warn("Node : " + invocationContext.httpNodeMap.remove(new KeyHttp(currInstanceId)) + " from httpNodeMap");
// invocationContext.badSipNodeMap(false).put(keySip, currNode);
invocationContext.balancerAlgorithm.nodeRemoved(currNode);
}
// TODO CHECK REQUEST AND REMOVE NODE
}
}
updateStatistic(response);
balancerRunner.balancerContext.httpBytesToClient.addAndGet(response.getContent().capacity());
if (response.isChunked()) {
readingChunks = true;
}
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
Channel channel = null;
if (ac != null)
channel = ac.getChannel();
if (channel != null) {
if (logger.isDebugEnabled())
logger.debug("Send response from : " + e.getChannel().getRemoteAddress() + " to : " + channel.getRemoteAddress() + " capacity : " + response.getContent().capacity());
channel.write(response);
}
Set<String> headers = response.getHeaderNames();
if (headers.contains("Sec-WebSocket-Protocol")) {
if (response.getHeader("Sec-WebSocket-Protocol").equalsIgnoreCase("sip")) {
if (logger.isDebugEnabled()) {
logger.debug("WebSocket response");
}
wsVersion = response.getHeader(Names.SEC_WEBSOCKET_VERSION);
// Modify the Server pipeline
ChannelPipeline p = channel.getPipeline();
websocketModifyServerPipelineFactory = new WebsocketModifyServerPipelineFactory();
websocketModifyServerPipelineFactory.upgradeServerPipelineFactory(p, wsVersion);
}
}
} else {
HttpChunk chunk = (HttpChunk) e.getMessage();
balancerRunner.balancerContext.httpBytesToClient.addAndGet(chunk.getContent().capacity());
if (chunk.isLast())
readingChunks = false;
AdvancedChannel ac = HttpChannelAssociations.channels.get(new AdvancedChannel(e.getChannel()));
Channel channel = null;
if (ac != null)
channel = ac.getChannel();
if (channel != null) {
if (logger.isDebugEnabled())
logger.debug("Send chunked response from : " + e.getChannel().getRemoteAddress() + " to : " + channel.getRemoteAddress() + " capacity : " + chunk.getContent().capacity());
channel.write(chunk);
}
}
}
use of org.mobicents.tools.heartbeat.api.Node in project load-balancer by RestComm.
the class NodeRegisterImpl method shutdownRequestReceived.
@Override
public synchronized void shutdownRequestReceived(MessageEvent e, JsonObject json) {
boolean was = false;
logger.info("LB got graceful shutdown from Node : " + json);
KeySession keySession = new KeySession(json.get(Protocol.SESSION_ID).toString());
for (Entry<String, InvocationContext> ctxEntry : balancerRunner.contexts.entrySet()) {
InvocationContext ctx = ctxEntry.getValue();
Node nodePresentIPv4 = ctx.sessionNodeMap(false).get(keySession);
Node nodePresentIPv6 = null;
if (nodePresentIPv4 != null) {
logger.info("LB will exclude node " + nodePresentIPv4 + "for new calls because of shutdown request");
nodePresentIPv4.setGracefulShutdown(true);
String instanseId = nodePresentIPv4.getProperties().get(Protocol.RESTCOMM_INSTANCE_ID);
if (instanseId != null)
ctx.httpNodeMap.get(new KeyHttp(instanseId)).setGracefulShutdown(true);
was = true;
} else if ((nodePresentIPv6 = balancerRunner.getLatestInvocationContext().sessionNodeMap(true).get(keySession)) != null) {
logger.info("LB will exclude node " + nodePresentIPv6 + "for new calls because of shutdown request");
nodePresentIPv6.setGracefulShutdown(true);
was = true;
}
}
if (!was) {
logger.error("LB got shutdown request ( " + json + " ) from node which not pesent in maps");
}
if (e != null)
writeResponse(e, HttpResponseStatus.OK, Protocol.SHUTDOWN, Protocol.OK);
}
Aggregations