1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.messaging.simp.stomp;
18
19 import java.io.ByteArrayOutputStream;
20 import java.io.DataOutputStream;
21 import java.io.IOException;
22 import java.util.Arrays;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.springframework.messaging.Message;
31 import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
32 import org.springframework.messaging.simp.SimpMessageType;
33 import org.springframework.messaging.support.NativeMessageHeaderAccessor;
34 import org.springframework.util.Assert;
35
36
37
38
39
40
41
42
43 public final class StompEncoder {
44
45 private static final byte LF = '\n';
46
47 private static final byte COLON = ':';
48
49 private final Log logger = LogFactory.getLog(StompEncoder.class);
50
51
52
53
54
55
56
57 public byte[] encode(Message<byte[]> message) {
58 return encode(message.getHeaders(), message.getPayload());
59 }
60
61
62
63
64
65
66
67 public byte[] encode(Map<String, Object> headers, byte[] payload) {
68 Assert.notNull(headers, "'headers' is required");
69 Assert.notNull(payload, "'payload' is required");
70
71 try {
72 ByteArrayOutputStream baos = new ByteArrayOutputStream(128 + payload.length);
73 DataOutputStream output = new DataOutputStream(baos);
74
75 if (SimpMessageType.HEARTBEAT.equals(SimpMessageHeaderAccessor.getMessageType(headers))) {
76 output.write(StompDecoder.HEARTBEAT_PAYLOAD);
77 }
78 else {
79 StompCommand command = StompHeaderAccessor.getCommand(headers);
80 Assert.notNull(command, "Missing STOMP command: " + headers);
81 output.write(command.toString().getBytes(StompDecoder.UTF8_CHARSET));
82 output.write(LF);
83 writeHeaders(command, headers, payload, output);
84 output.write(LF);
85 writeBody(payload, output);
86 output.write((byte) 0);
87 }
88
89 return baos.toByteArray();
90 }
91 catch (IOException ex) {
92 throw new StompConversionException("Failed to encode STOMP frame, headers=" + headers, ex);
93 }
94 }
95
96 private void writeHeaders(StompCommand command, Map<String, Object> headers, byte[] payload, DataOutputStream output)
97 throws IOException {
98
99 @SuppressWarnings("unchecked")
100 Map<String,List<String>> nativeHeaders =
101 (Map<String, List<String>>) headers.get(NativeMessageHeaderAccessor.NATIVE_HEADERS);
102
103 if (logger.isTraceEnabled()) {
104 logger.trace("Encoding STOMP " + command + ", headers=" + nativeHeaders);
105 }
106
107 if (nativeHeaders == null) {
108 return;
109 }
110
111 boolean shouldEscape = (command != StompCommand.CONNECT && command != StompCommand.CONNECTED);
112
113 for (Entry<String, List<String>> entry : nativeHeaders.entrySet()) {
114 byte[] key = encodeHeaderString(entry.getKey(), shouldEscape);
115 if (command.requiresContentLength() && "content-length".equals(entry.getKey())) {
116 continue;
117 }
118 List<String> values = entry.getValue();
119 if (StompCommand.CONNECT.equals(command) &&
120 StompHeaderAccessor.STOMP_PASSCODE_HEADER.equals(entry.getKey())) {
121 values = Arrays.asList(StompHeaderAccessor.getPasscode(headers));
122 }
123 for (String value : values) {
124 output.write(key);
125 output.write(COLON);
126 output.write(encodeHeaderString(value, shouldEscape));
127 output.write(LF);
128 }
129 }
130 if (command.requiresContentLength()) {
131 int contentLength = payload.length;
132 output.write("content-length:".getBytes(StompDecoder.UTF8_CHARSET));
133 output.write(Integer.toString(contentLength).getBytes(StompDecoder.UTF8_CHARSET));
134 output.write(LF);
135 }
136 }
137
138 private byte[] encodeHeaderString(String input, boolean escape) {
139 String inputToUse = (escape ? escape(input) : input);
140 return inputToUse.getBytes(StompDecoder.UTF8_CHARSET);
141 }
142
143
144
145
146
147 private String escape(String inString) {
148 StringBuilder sb = new StringBuilder(inString.length());
149 for (int i = 0; i < inString.length(); i++) {
150 char c = inString.charAt(i);
151 if (c == '\\') {
152 sb.append("\\\\");
153 }
154 else if (c == ':') {
155 sb.append("\\c");
156 }
157 else if (c == '\n') {
158 sb.append("\\n");
159 }
160 else if (c == '\r') {
161 sb.append("\\r");
162 }
163 else {
164 sb.append(c);
165 }
166 }
167 return sb.toString();
168 }
169
170 private void writeBody(byte[] payload, DataOutputStream output) throws IOException {
171 output.write(payload);
172 }
173
174 }