org.apache.activemq.transport.stomp.StompCodec Maven / Gradle / Ivy
/**
* 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.activemq.transport.stomp;
import java.io.ByteArrayInputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.activemq.transport.tcp.TcpTransport;
import org.apache.activemq.util.ByteArrayOutputStream;
import org.apache.activemq.util.DataByteArrayInputStream;
public class StompCodec {
final static byte[] crlfcrlf = new byte[]{'\r','\n','\r','\n'};
TcpTransport transport;
StompWireFormat wireFormat;
AtomicLong frameSize = new AtomicLong();
ByteArrayOutputStream currentCommand = new ByteArrayOutputStream();
boolean processedHeaders = false;
String action;
HashMap headers;
int contentLength = -1;
int readLength = 0;
int previousByte = -1;
boolean awaitingCommandStart = true;
String version = Stomp.DEFAULT_VERSION;
public StompCodec(TcpTransport transport) {
this.transport = transport;
this.wireFormat = (StompWireFormat) transport.getWireFormat();
}
public void parse(ByteArrayInputStream input, int readSize) throws Exception {
int i = 0;
int b;
while(i++ < readSize) {
b = input.read();
// skip repeating nulls
if (!processedHeaders && previousByte == 0 && b == 0) {
continue;
}
if (!processedHeaders) {
// skip heart beat commands.
if (awaitingCommandStart && b == '\n') {
continue;
} else {
awaitingCommandStart = false; // non-newline indicates next frame.
}
currentCommand.write(b);
// end of headers section, parse action and header
if (b == '\n' && (previousByte == '\n' || currentCommand.endsWith(crlfcrlf))) {
DataByteArrayInputStream data = new DataByteArrayInputStream(currentCommand.toByteArray());
try {
action = wireFormat.parseAction(data, frameSize);
headers = wireFormat.parseHeaders(data, frameSize);
String contentLengthHeader = headers.get(Stomp.Headers.CONTENT_LENGTH);
if ((action.equals(Stomp.Commands.SEND) || action.equals(Stomp.Responses.MESSAGE)) && contentLengthHeader != null) {
contentLength = wireFormat.parseContentLength(contentLengthHeader, frameSize);
} else {
contentLength = -1;
}
} catch (ProtocolException e) {
transport.doConsume(new StompFrameError(e));
return;
}
processedHeaders = true;
currentCommand.reset();
}
} else {
if (contentLength == -1) {
// end of command reached, unmarshal
if (b == 0) {
processCommand();
} else {
currentCommand.write(b);
if (currentCommand.size() > wireFormat.getMaxDataLength()) {
StompFrameError errorFrame = new StompFrameError(new ProtocolException("The maximum data length was exceeded", true));
errorFrame.setAction(this.action);
transport.doConsume(errorFrame);
return;
}
if (frameSize.incrementAndGet() > wireFormat.getMaxFrameSize()) {
StompFrameError errorFrame = new StompFrameError(new ProtocolException("The maximum frame size was exceeded", true));
errorFrame.setAction(this.action);
transport.doConsume(errorFrame);
return;
}
}
} else {
// read desired content length
if (readLength++ == contentLength) {
processCommand();
readLength = 0;
} else {
currentCommand.write(b);
}
}
}
previousByte = b;
}
}
protected void processCommand() throws Exception {
StompFrame frame = new StompFrame(action, headers, currentCommand.toByteArray());
transport.doConsume(frame);
processedHeaders = false;
awaitingCommandStart = true;
currentCommand.reset();
contentLength = -1;
frameSize.set(0);
}
public static String detectVersion(Map headers) throws ProtocolException {
String accepts = headers.get(Stomp.Headers.Connect.ACCEPT_VERSION);
if (accepts == null) {
accepts = Stomp.DEFAULT_VERSION;
}
HashSet acceptsVersions = new HashSet(Arrays.asList(accepts.trim().split(Stomp.COMMA)));
acceptsVersions.retainAll(Arrays.asList(Stomp.SUPPORTED_PROTOCOL_VERSIONS));
if (acceptsVersions.isEmpty()) {
throw new ProtocolException("Invalid Protocol version[" + accepts +"], supported versions are: " +
Arrays.toString(Stomp.SUPPORTED_PROTOCOL_VERSIONS), true);
} else {
return Collections.max(acceptsVersions);
}
}
}