blob: 37fbc64150496182968b0190e82e4db49d990fa3 [file] [log] [blame]
lryane4bd1c72014-09-08 14:03:35 -07001package com.google.net.stubby;
2
ejona8c76c8a2014-10-09 09:53:41 -07003import static com.google.common.base.Charsets.US_ASCII;
4import static com.google.common.base.Charsets.UTF_8;
lryane4bd1c72014-09-08 14:03:35 -07005
lryane4bd1c72014-09-08 14:03:35 -07006import com.google.common.base.Function;
7import com.google.common.base.Preconditions;
8import com.google.common.collect.Iterables;
9import com.google.common.collect.Iterators;
10import com.google.common.collect.LinkedListMultimap;
11import com.google.common.collect.ListMultimap;
12import com.google.common.collect.Lists;
13
lryane4bd1c72014-09-08 14:03:35 -070014import java.util.Iterator;
15import java.util.List;
16import java.util.Map;
17import java.util.Set;
18
19import javax.annotation.concurrent.NotThreadSafe;
20
21/**
22 * Provides access to read and write metadata values to be exchanged during a call.
23 * <p>
24 * This class is not thread safe, implementations should ensure that header reads and writes
25 * do not occur in multiple threads concurrently.
26 * </p>
27 */
28@NotThreadSafe
lryan1369ea12014-09-23 10:36:55 -070029public abstract class Metadata {
lryane4bd1c72014-09-08 14:03:35 -070030
31 /**
32 * Interleave keys and values into a single iterator.
33 */
34 private static Iterator<String> fromMapEntries(Iterable<Map.Entry<String, String>> entries) {
35 final Iterator<Map.Entry<String, String>> iterator = entries.iterator();
36 return new Iterator<String>() {
37 Map.Entry<String, String> last;
38 @Override
39 public boolean hasNext() {
40 return last != null || iterator.hasNext();
41 }
42
43 @Override
44 public String next() {
45 if (last == null) {
46 last = iterator.next();
47 return last.getKey();
48 } else {
49 String val = last.getValue();
50 last = null;
51 return val;
52 }
53 }
54
55 @Override
56 public void remove() {
57 throw new UnsupportedOperationException();
58 }
59 };
60 }
61
62 /**
63 * Simple metadata marshaller that encodes strings as either UTF-8 or ASCII bytes.
64 */
65 public static final Marshaller<String> STRING_MARSHALLER =
66 new Marshaller<String>() {
67
68 @Override
69 public byte[] toBytes(String value) {
70 return value.getBytes(UTF_8);
71 }
72
73 @Override
74 public String toAscii(String value) {
75 return value;
76 }
77
78 @Override
79 public String parseBytes(byte[] serialized) {
80 return new String(serialized, UTF_8);
81 }
82
83 @Override
84 public String parseAscii(String ascii) {
85 return ascii;
86 }
87 };
88
ejona9d502992014-09-22 12:23:19 -070089 /**
90 * Simple metadata marshaller that encodes an integer as a signed decimal string or as big endian
91 * binary with four bytes.
92 */
93 public static final Marshaller<Integer> INTEGER_MARSHALLER = new Marshaller<Integer>() {
94 @Override
95 public byte[] toBytes(Integer value) {
96 return new byte[] {
97 (byte) (value >>> 24),
98 (byte) (value >>> 16),
99 (byte) (value >>> 8),
100 (byte) (value >>> 0)};
101 }
102
103 @Override
104 public String toAscii(Integer value) {
105 return value.toString();
106 }
107
108 @Override
109 public Integer parseBytes(byte[] serialized) {
110 if (serialized.length != 4) {
111 throw new IllegalArgumentException("Can only deserialize 4 bytes into an integer");
112 }
113 return (serialized[0] << 24)
114 | (serialized[1] << 16)
115 | (serialized[2] << 8)
116 | serialized[3];
117 }
118
119 @Override
120 public Integer parseAscii(String ascii) {
121 return Integer.valueOf(ascii);
122 }
123 };
124
lryane4bd1c72014-09-08 14:03:35 -0700125 private final ListMultimap<String, MetadataEntry> store;
126 private final boolean serializable;
127
128 /**
129 * Constructor called by the transport layer when it receives binary metadata.
130 */
131 // TODO(user): Convert to use ByteString so we can cache transformations
132 private Metadata(byte[]... binaryValues) {
133 store = LinkedListMultimap.create();
134 for (int i = 0; i < binaryValues.length; i++) {
ejona8c76c8a2014-10-09 09:53:41 -0700135 String name = new String(binaryValues[i], US_ASCII);
lryane4bd1c72014-09-08 14:03:35 -0700136 store.put(name, new MetadataEntry(binaryValues[++i]));
137 }
138 this.serializable = false;
139 }
140
141 /**
142 * Constructor called by the transport layer when it receives ASCII metadata.
143 */
144 private Metadata(String... asciiValues) {
145 store = LinkedListMultimap.create();
146 for (int i = 0; i < asciiValues.length; i++) {
147 store.put(asciiValues[i], new MetadataEntry(asciiValues[++i]));
148 }
149 this.serializable = false;
150 }
151
152 /**
153 * Constructor called by the application layer when it wants to send metadata.
154 */
155 private Metadata() {
156 store = LinkedListMultimap.create();
157 this.serializable = true;
158 }
159
160 /**
161 * Returns true if a value is defined for the given key.
162 */
163 public <T> boolean containsKey(Key key) {
164 return store.containsKey(key.name);
165 }
166
167 /**
168 * Returns the last metadata entry added with the name 'name' parsed as T.
lryan1369ea12014-09-23 10:36:55 -0700169 * @return the parsed metadata entry or null if there are none.
lryane4bd1c72014-09-08 14:03:35 -0700170 */
171 public <T> T get(Key<T> key) {
lryan1369ea12014-09-23 10:36:55 -0700172 if (containsKey(key)) {
173 MetadataEntry metadataEntry = Iterables.getLast(store.get(key.name()));
174 return metadataEntry.getParsed(key);
175 }
176 return null;
lryane4bd1c72014-09-08 14:03:35 -0700177 }
178
179 /**
180 * Returns all the metadata entries named 'name', in the order they were received,
lryan1369ea12014-09-23 10:36:55 -0700181 * parsed as T or null if there are none.
lryane4bd1c72014-09-08 14:03:35 -0700182 */
183 public <T> Iterable<T> getAll(final Key<T> key) {
lryan1369ea12014-09-23 10:36:55 -0700184 if (containsKey(key)) {
185 return Iterables.transform(
186 store.get(key.name()),
187 new Function<MetadataEntry, T>() {
188 @Override
189 public T apply(MetadataEntry entry) {
190 return entry.getParsed(key);
191 }
192 });
193 }
194 return null;
lryane4bd1c72014-09-08 14:03:35 -0700195 }
196
197 public <T> void put(Key<T> key, T value) {
198 store.put(key.name(), new MetadataEntry(key, value));
199 }
200
201 /**
202 * Remove a specific value.
203 */
204 public <T> boolean remove(Key<T> key, T value) {
205 return store.remove(key.name(), value);
206 }
207
208 /**
209 * Remove all values for the given key.
210 */
211 public <T> List<T> removeAll(final Key<T> key) {
212 return Lists.transform(store.removeAll(key.name()), new Function<MetadataEntry, T>() {
213 @Override
214 public T apply(MetadataEntry metadataEntry) {
215 return metadataEntry.getParsed(key);
216 }
217 });
218 }
219
220 /**
221 * Can this metadata be serialized. Metadata constructed from raw binary or ascii values
222 * cannot be serialized without merging it into a serializable instance using
223 * {@link #merge(Metadata, java.util.Set)}
224 */
225 public boolean isSerializable() {
226 return serializable;
227 }
228
229 /**
230 * Serialize all the metadata entries
231 */
232 public byte[][] serialize() {
233 Preconditions.checkState(serializable, "Can't serialize raw metadata");
234 byte[][] serialized = new byte[store.size() * 2][];
235 int i = 0;
236 for (Map.Entry<String, MetadataEntry> entry : store.entries()) {
237 serialized[i++] = entry.getValue().key.asciiName();
238 serialized[i++] = entry.getValue().getSerialized();
239 }
240 return serialized;
241 }
242
243 /**
244 * Serialize all the metadata entries
245 */
246 public String[] serializeAscii() {
247 Preconditions.checkState(serializable, "Can't serialize received metadata");
248 String[] serialized = new String[store.size() * 2];
249 int i = 0;
250 for (Map.Entry<String, MetadataEntry> entry : store.entries()) {
251 serialized[i++] = entry.getValue().key.name();
252 serialized[i++] = entry.getValue().getSerializedAscii();
253 }
254 return serialized;
255 }
256
257 /**
258 * Perform a simple merge of two sets of metadata.
259 * <p>
260 * Note that we can't merge non-serializable metadata into serializable.
261 * </p>
262 */
263 public void merge(Metadata other) {
264 Preconditions.checkNotNull(other);
265 if (this.serializable) {
266 if (!other.serializable) {
267 throw new IllegalArgumentException(
268 "Cannot merge non-serializable metadata into serializable metadata without keys");
269 }
270 }
271 store.putAll(other.store);
272 }
273
274 /**
275 * Merge values for the given set of keys into this set of metadata.
276 */
277 public void merge(Metadata other, Set<Key> keys) {
278 Preconditions.checkNotNull(other);
279 for (Key key : keys) {
280 if (other.containsKey(key)) {
281 Iterable values = other.getAll(key);
282 for (Object value : values) {
283 put(key, value);
284 }
285 }
286 }
287 }
288
289 /**
290 * Concrete instance for metadata attached to the start of a call.
291 */
lryan1369ea12014-09-23 10:36:55 -0700292 public static class Headers extends Metadata {
lryane4bd1c72014-09-08 14:03:35 -0700293 private String path;
294 private String authority;
295
296 /**
297 * Called by the transport layer to create headers from their binary serialized values.
298 */
299 public Headers(byte[]... headers) {
300 super(headers);
301 }
302
303 /**
304 * Called by the transport layer to create headers from their ASCII serialized values.
305 */
306 public Headers(String... asciiValues) {
307 super(asciiValues);
308 }
309
310 /**
311 * Called by the transport layer to create headers from their ASCII serialized values.
312 */
313 public Headers(Iterable<Map.Entry<String, String>> mapEntries) {
314 super(Iterators.toArray(fromMapEntries(mapEntries), String.class));
315 }
316
317 /**
318 * Called by the application layer to construct headers prior to passing them to the
319 * transport for serialization.
320 */
321 public Headers() {
322 }
323
324 /**
325 * The path for the operation.
326 */
327 public String getPath() {
328 return path;
329 }
330
331 public void setPath(String path) {
332 this.path = path;
333 }
334
335 /**
336 * The serving authority for the operation.
337 */
338 public String getAuthority() {
339 return authority;
340 }
341
342 public void setAuthority(String authority) {
343 this.authority = authority;
344 }
nathanmittler23fbc7c2014-09-11 12:50:16 -0700345
346 @Override
347 public void merge(Metadata other) {
348 super.merge(other);
349 mergePathAndAuthority(other);
350 }
351
352 @Override
353 public void merge(Metadata other, Set<Key> keys) {
354 super.merge(other, keys);
355 mergePathAndAuthority(other);
356 }
357
358 private void mergePathAndAuthority(Metadata other) {
359 if (other instanceof Headers) {
360 Headers otherHeaders = (Headers) other;
361 path = otherHeaders.path != null ? otherHeaders.path : path;
362 authority = otherHeaders.authority != null ? otherHeaders.authority : authority;
363 }
364 }
lryane4bd1c72014-09-08 14:03:35 -0700365 }
366
367 /**
368 * Concrete instance for metadata attached to the end of the call. Only provided by
369 * servers.
370 */
lryan1369ea12014-09-23 10:36:55 -0700371 public static class Trailers extends Metadata {
lryane4bd1c72014-09-08 14:03:35 -0700372 /**
373 * Called by the transport layer to create trailers from their binary serialized values.
374 */
375 public Trailers(byte[]... headers) {
376 super(headers);
377 }
378
379 /**
380 * Called by the transport layer to create trailers from their ASCII serialized values.
381 */
382 public Trailers(String... asciiValues) {
383 super(asciiValues);
384 }
385
386 /**
387 * Called by the transport layer to create headers from their ASCII serialized values.
388 */
389 public Trailers(Iterable<Map.Entry<String, String>> mapEntries) {
390 super(Iterators.toArray(fromMapEntries(mapEntries), String.class));
391 }
392
393 /**
394 * Called by the application layer to construct trailers prior to passing them to the
395 * transport for serialization.
396 */
397 public Trailers() {
398 }
399 }
400
401
402 /**
403 * Marshaller for metadata values.
404 */
405 public static interface Marshaller<T> {
406 /**
407 * Serialize a metadata value to bytes.
408 * @param value to serialize
409 * @return serialized version of value, or null if value cannot be transmitted.
410 */
411 public byte[] toBytes(T value);
412
413 /**
414 * Serialize a metadata value to an ASCII string
415 * @param value to serialize
416 * @return serialized ascii version of value, or null if value cannot be transmitted.
417 */
418 public String toAscii(T value);
419
420 /**
421 * Parse a serialized metadata value from bytes.
422 * @param serialized value of metadata to parse
423 * @return a parsed instance of type T
424 */
425 public T parseBytes(byte[] serialized);
426
427 /**
428 * Parse a serialized metadata value from an ascii string.
429 * @param ascii string value of metadata to parse
430 * @return a parsed instance of type T
431 */
432 public T parseAscii(String ascii);
433 }
434
435 /**
436 * Key for metadata entries. Allows for parsing and serialization of metadata.
437 */
438 public static class Key<T> {
ejona9d502992014-09-22 12:23:19 -0700439 public static <T> Key<T> of(String name, Marshaller<T> marshaller) {
440 return new Key<T>(name, marshaller);
441 }
lryane4bd1c72014-09-08 14:03:35 -0700442
443 private final String name;
444 private final byte[] asciiName;
445 private final Marshaller<T> marshaller;
446
447 /**
448 * Keys have a name and a marshaller used for serialization.
449 */
ejona9d502992014-09-22 12:23:19 -0700450 private Key(String name, Marshaller<T> marshaller) {
lryan1369ea12014-09-23 10:36:55 -0700451 this.name = Preconditions.checkNotNull(name, "name").toLowerCase().intern();
lryan28497e32014-10-17 16:14:38 -0700452 this.asciiName = this.name.getBytes(US_ASCII);
lryane4bd1c72014-09-08 14:03:35 -0700453 this.marshaller = Preconditions.checkNotNull(marshaller);
454 }
455
456 public String name() {
457 return name;
458 }
459
lryan1369ea12014-09-23 10:36:55 -0700460 // TODO (lryan): Migrate to ByteString
461 public byte[] asciiName() {
lryane4bd1c72014-09-08 14:03:35 -0700462 return asciiName;
463 }
464
465 public Marshaller<T> getMarshaller() {
466 return marshaller;
467 }
lryan1369ea12014-09-23 10:36:55 -0700468
469 @Override
470 public boolean equals(Object o) {
471 if (this == o) return true;
472 if (o == null || getClass() != o.getClass()) return false;
473 Key key = (Key) o;
474 return !(name != null ? !name.equals(key.name) : key.name != null);
475 }
476
477 @Override
478 public int hashCode() {
479 return name != null ? name.hashCode() : 0;
480 }
481
482 @Override
483 public String toString() {
484 return "Key{name='" + name + "'}";
485 }
lryane4bd1c72014-09-08 14:03:35 -0700486 }
487
488 private static class MetadataEntry {
489 Object parsed;
490 Key key;
491 byte[] serializedBinary;
492 String serializedAscii;
493
494 /**
495 * Constructor used when application layer adds a parsed value.
496 */
497 private MetadataEntry(Key key, Object parsed) {
498 this.parsed = Preconditions.checkNotNull(parsed);
499 this.key = Preconditions.checkNotNull(key);
500 }
501
502 /**
503 * Constructor used when reading a value from the transport.
504 */
505 private MetadataEntry(byte[] serialized) {
506 Preconditions.checkNotNull(serialized);
507 this.serializedBinary = serialized;
508 }
509
510 /**
511 * Constructor used when reading a value from the transport.
512 */
513 private MetadataEntry(String serializedAscii) {
514 this.serializedAscii = Preconditions.checkNotNull(serializedAscii);
515 }
516
517 public <T> T getParsed(Key<T> key) {
518 @SuppressWarnings("unchecked")
519 T value = (T) parsed;
520 if (value != null) {
521 if (this.key != key) {
522 // Keys don't match so serialize using the old key
523 serializedBinary = this.key.getMarshaller().toBytes(value);
524 } else {
525 return value;
526 }
527 }
528 this.key = key;
529 if (serializedBinary != null) {
530 value = key.getMarshaller().parseBytes(serializedBinary);
531 } else if (serializedAscii != null) {
532 value = key.getMarshaller().parseAscii(serializedAscii);
533 }
534 parsed = value;
535 return value;
536 }
537
538 @SuppressWarnings("unchecked")
539 public byte[] getSerialized() {
540 return serializedBinary =
541 serializedBinary == null
542 ? key.getMarshaller().toBytes(parsed) :
543 serializedBinary;
544 }
545
546 @SuppressWarnings("unchecked")
547 public String getSerializedAscii() {
548 return serializedAscii =
549 serializedAscii == null
550 ? key.getMarshaller().toAscii(parsed) :
551 serializedAscii;
552 }
553 }
554}