/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
* WSO2 Inc. 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.wso2.carbon.inbound.endpoint.protocol.websocket;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axis2.AxisFault;
import org.apache.axis2.transport.MessageFormatter;
import org.apache.axis2.transport.base.BaseUtils;
import org.apache.axis2.util.MessageProcessorSelector;
import org.apache.commons.io.output.WriterOutputStream;
import org.apache.log4j.Logger;
import org.apache.synapse.MessageContext;
import org.apache.synapse.core.axis2.Axis2MessageContext;
import org.apache.synapse.inbound.InboundResponseSender;
import org.apache.synapse.transport.passthru.util.RelayUtils;
import org.wso2.carbon.inbound.endpoint.protocol.websocket.management.WebsocketEndpointManager;
import org.wso2.carbon.inbound.endpoint.protocol.websocket.management.WebsocketSubscriberPathManager;
import javax.xml.stream.XMLStreamException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
public class InboundWebsocketResponseSender implements InboundResponseSender {
private Logger log = Logger.getLogger(InboundWebsocketResponseSender.class);
private InboundWebsocketSourceHandler sourceHandler;
public InboundWebsocketResponseSender(InboundWebsocketSourceHandler sourceHandler) {
this.sourceHandler = sourceHandler;
}
public InboundWebsocketSourceHandler getSourceHandler() {
return sourceHandler;
}
@Override
public void sendBack(MessageContext msgContext) {
String defaultContentType = sourceHandler.getDefaultContentType();
if (msgContext != null) {
Integer errorCode = null;
String errorMessage = null;
if (msgContext.getProperty("errorCode") != null) {
errorCode = Integer.parseInt(msgContext.getProperty("errorCode").toString());
}
if (msgContext.getProperty("ERROR_MESSAGE") != null) {
errorMessage = msgContext.getProperty("ERROR_MESSAGE").toString();
}
try {
if (errorCode != null && errorMessage != null) {
sourceHandler.handleClientWebsocketChannelTermination(
new CloseWebSocketFrame(errorCode, errorMessage));
}
} catch (AxisFault fault) {
log.error("Error occurred while sending close frames", fault);
}
Object isTCPTransport = ((Axis2MessageContext) msgContext).getAxis2MessageContext()
.getProperty(InboundWebsocketConstants.IS_TCP_TRANSPORT);
if (msgContext.getProperty(InboundWebsocketConstants.SOURCE_HANDSHAKE_PRESENT) != null &&
msgContext.getProperty(InboundWebsocketConstants.SOURCE_HANDSHAKE_PRESENT).equals(true)) {
return;
} else if (msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_TARGET_HANDSHAKE_PRESENT) != null &&
msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_TARGET_HANDSHAKE_PRESENT).equals(true)) {
if (msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_TARGET_HANDLER_CONTEXT) != null) {
ChannelHandlerContext targetCtx = (ChannelHandlerContext) msgContext
.getProperty(InboundWebsocketConstants.WEBSOCKET_TARGET_HANDLER_CONTEXT);
sourceHandler.getChannelHandlerContext().addCloseListener(targetCtx);
}
return;
} else if (msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_BINARY_FRAME_PRESENT) != null &&
msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_BINARY_FRAME_PRESENT).equals(true)) {
BinaryWebSocketFrame frame = (BinaryWebSocketFrame)
msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_BINARY_FRAME);
if (isTCPTransport != null && (boolean) isTCPTransport) {
try {
RelayUtils.buildMessage(((Axis2MessageContext) msgContext)
.getAxis2MessageContext(), false);
if (defaultContentType != null && defaultContentType.startsWith(
InboundWebsocketConstants.BINARY)) {
org.apache.axis2.context.MessageContext msgCtx =
((Axis2MessageContext) msgContext).getAxis2MessageContext();
MessageFormatter messageFormatter = BaseUtils.getMessageFormatter(msgCtx);
OMOutputFormat format = BaseUtils.getOMOutputFormat(msgCtx);
byte[] message = messageFormatter.getBytes(msgCtx, format);
frame = new BinaryWebSocketFrame(Unpooled.copiedBuffer(message));
InboundWebsocketChannelContext ctx = sourceHandler.getChannelHandlerContext();
int clientBroadcastLevel = sourceHandler.getClientBroadcastLevel();
String subscriberPath = sourceHandler.getSubscriberPath();
WebsocketSubscriberPathManager pathManager = WebsocketSubscriberPathManager.getInstance();
handleSendBack(frame, ctx, clientBroadcastLevel, subscriberPath, pathManager);
return;
}
} catch (XMLStreamException ex) {
log.error("Error while building message", ex);
} catch (IOException ex) {
log.error("Failed for format message to specified output format", ex);
}
}
InboundWebsocketChannelContext ctx = sourceHandler.getChannelHandlerContext();
int clientBroadcastLevel = sourceHandler.getClientBroadcastLevel();
String subscriberPath = sourceHandler.getSubscriberPath();
WebsocketSubscriberPathManager pathManager = WebsocketSubscriberPathManager.getInstance();
handleSendBack(frame, ctx, clientBroadcastLevel, subscriberPath, pathManager);
} else if (msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_TEXT_FRAME_PRESENT) != null &&
msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_TEXT_FRAME_PRESENT).equals(true)) {
TextWebSocketFrame frame = (TextWebSocketFrame)
msgContext.getProperty(InboundWebsocketConstants.WEBSOCKET_TEXT_FRAME);
if (isTCPTransport != null && (boolean) isTCPTransport) {
try {
RelayUtils.buildMessage(((Axis2MessageContext) msgContext)
.getAxis2MessageContext(), false);
if (defaultContentType != null && defaultContentType.startsWith(
InboundWebsocketConstants.TEXT)) {
String backendMessageType = (String) (((Axis2MessageContext) msgContext)
.getAxis2MessageContext())
.getProperty(InboundWebsocketConstants.BACKEND_MESSAGE_TYPE);
((Axis2MessageContext) msgContext).getAxis2MessageContext().setProperty(
InboundWebsocketConstants.MESSAGE_TYPE, backendMessageType);
frame = new TextWebSocketFrame(messageContextToText(((Axis2MessageContext)
msgContext).getAxis2MessageContext()));
InboundWebsocketChannelContext ctx = sourceHandler.getChannelHandlerContext();
int clientBroadcastLevel = sourceHandler.getClientBroadcastLevel();
String subscriberPath = sourceHandler.getSubscriberPath();
WebsocketSubscriberPathManager pathManager = WebsocketSubscriberPathManager.getInstance();
handleSendBack(frame, ctx, clientBroadcastLevel, subscriberPath, pathManager);
return;
}
} catch (XMLStreamException ex) {
log.error("Error while building message", ex);
} catch (IOException ex) {
log.error("Failed for format message to specified output format", ex);
}
}
InboundWebsocketChannelContext ctx = sourceHandler.getChannelHandlerContext();
int clientBroadcastLevel = sourceHandler.getClientBroadcastLevel();
String subscriberPath = sourceHandler.getSubscriberPath();
WebsocketSubscriberPathManager pathManager = WebsocketSubscriberPathManager.getInstance();
handleSendBack(frame, ctx, clientBroadcastLevel, subscriberPath, pathManager);
} else {
try {
Object wsCloseFrameStatusCode = msgContext.getProperty(
InboundWebsocketConstants.WS_CLOSE_FRAME_STATUS_CODE);
String wsCloseFrameReasonText = (String) (msgContext.getProperty(
InboundWebsocketConstants.WS_CLOSE_FRAME_REASON_TEXT));
int statusCode = InboundWebsocketConstants.WS_CLOSE_DEFAULT_CODE;
if (wsCloseFrameStatusCode != null) {
statusCode = (int) wsCloseFrameStatusCode;
}
if (wsCloseFrameStatusCode == null) {
wsCloseFrameReasonText = "Unexpected frame type";
}
if (wsCloseFrameStatusCode != null && wsCloseFrameReasonText != null) {
sourceHandler.handleClientWebsocketChannelTermination(new CloseWebSocketFrame(statusCode,
wsCloseFrameReasonText));
return;
}
RelayUtils.buildMessage(((Axis2MessageContext) msgContext).getAxis2MessageContext(), false);
TextWebSocketFrame frame = new TextWebSocketFrame(messageContextToText(((Axis2MessageContext) msgContext)
.getAxis2MessageContext()));
InboundWebsocketChannelContext ctx = sourceHandler.getChannelHandlerContext();
int clientBroadcastLevel = sourceHandler.getClientBroadcastLevel();
String subscriberPath = sourceHandler.getSubscriberPath();
WebsocketSubscriberPathManager pathManager = WebsocketSubscriberPathManager.getInstance();
handleSendBack(frame, ctx, clientBroadcastLevel, subscriberPath, pathManager);
} catch (IOException ex) {
log.error("Failed for format message to specified output format", ex);
} catch (XMLStreamException e) {
log.error("Error while building message", e);
}
}
}
}
protected void handleSendBack(WebSocketFrame frame,
InboundWebsocketChannelContext ctx,
int clientBroadcastLevel,
String subscriberPath,
WebsocketSubscriberPathManager pathManager) {
if (clientBroadcastLevel == 0) {
ctx.writeToChannel(frame);
} else if (clientBroadcastLevel == 1) {
String endpointName =
WebsocketEndpointManager.getInstance().getEndpointName(sourceHandler.getPort(),
sourceHandler.getTenantDomain());
pathManager.broadcastOnSubscriberPath(frame, endpointName, subscriberPath);
} else if (clientBroadcastLevel == 2) {
String endpointName =
WebsocketEndpointManager.getInstance().getEndpointName(sourceHandler.getPort(),
sourceHandler.getTenantDomain());
pathManager.exclusiveBroadcastOnSubscriberPath(frame, endpointName, subscriberPath, ctx);
}
}
protected String messageContextToText(org.apache.axis2.context.MessageContext msgCtx)
throws IOException {
OMOutputFormat format = BaseUtils.getOMOutputFormat(msgCtx);
MessageFormatter messageFormatter =
MessageProcessorSelector.getMessageFormatter(msgCtx);
StringWriter sw = new StringWriter();
OutputStream out = new WriterOutputStream(sw, format.getCharSetEncoding());
messageFormatter.writeTo(msgCtx, format, out, true);
out.close();
return sw.toString();
}
}