1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style 5 // license that can be found in the LICENSE file or at 6 // https://developers.google.com/open-source/licenses/bsd 7 8 package com.google.protobuf; 9 10 import static com.google.common.truth.Truth.assertThat; 11 import static org.junit.Assert.fail; 12 13 import com.google.protobuf.DescriptorProtos.DescriptorProto; 14 import java.io.ByteArrayInputStream; 15 import java.io.ByteArrayOutputStream; 16 import java.io.FilterInputStream; 17 import java.io.IOException; 18 import java.io.InputStream; 19 import org.junit.Test; 20 import org.junit.runner.RunWith; 21 import org.junit.runners.JUnit4; 22 23 /** 24 * Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser} 25 * interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want 26 * to distinguish between invalid protos vs. actual I/O errors (like failures reading from a socket, 27 * etc.). So, when we're not using the parser directly, an {@link IOException} should be thrown 28 * where appropriate, instead of always an {@link InvalidProtocolBufferException}. 29 * 30 * @author jh@squareup.com (Joshua Humphries) 31 */ 32 @RunWith(JUnit4.class) 33 public class ParseExceptionsTest { 34 35 private interface ParseTester { parse(InputStream in)36 DescriptorProto parse(InputStream in) throws IOException; 37 } 38 39 private byte[] serializedProto; 40 setup()41 private void setup() { 42 serializedProto = DescriptorProto.getDescriptor().toProto().toByteArray(); 43 } 44 setupDelimited()45 private void setupDelimited() { 46 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 47 try { 48 DescriptorProto.getDescriptor().toProto().writeDelimitedTo(bos); 49 } catch (IOException e) { 50 fail("Exception not expected: " + e); 51 } 52 serializedProto = bos.toByteArray(); 53 } 54 55 @Test message_parseFrom_InputStream()56 public void message_parseFrom_InputStream() { 57 setup(); 58 verifyExceptions( 59 new ParseTester() { 60 @Override 61 public DescriptorProto parse(InputStream in) throws IOException { 62 return DescriptorProto.parseFrom(in); 63 } 64 }); 65 } 66 67 @Test message_parseFrom_InputStreamAndExtensionRegistry()68 public void message_parseFrom_InputStreamAndExtensionRegistry() { 69 setup(); 70 verifyExceptions( 71 new ParseTester() { 72 @Override 73 public DescriptorProto parse(InputStream in) throws IOException { 74 return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance()); 75 } 76 }); 77 } 78 79 @Test message_parseFrom_CodedInputStream()80 public void message_parseFrom_CodedInputStream() { 81 setup(); 82 verifyExceptions( 83 new ParseTester() { 84 @Override 85 public DescriptorProto parse(InputStream in) throws IOException { 86 return DescriptorProto.parseFrom(CodedInputStream.newInstance(in)); 87 } 88 }); 89 } 90 91 @Test message_parseFrom_CodedInputStreamAndExtensionRegistry()92 public void message_parseFrom_CodedInputStreamAndExtensionRegistry() { 93 setup(); 94 verifyExceptions( 95 new ParseTester() { 96 @Override 97 public DescriptorProto parse(InputStream in) throws IOException { 98 return DescriptorProto.parseFrom( 99 CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()); 100 } 101 }); 102 } 103 104 @Test message_parseDelimitedFrom_InputStream()105 public void message_parseDelimitedFrom_InputStream() { 106 setupDelimited(); 107 verifyExceptions( 108 new ParseTester() { 109 @Override 110 public DescriptorProto parse(InputStream in) throws IOException { 111 return DescriptorProto.parseDelimitedFrom(in); 112 } 113 }); 114 } 115 116 @Test message_parseDelimitedFrom_InputStreamAndExtensionRegistry()117 public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() { 118 setupDelimited(); 119 verifyExceptions( 120 new ParseTester() { 121 @Override 122 public DescriptorProto parse(InputStream in) throws IOException { 123 return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance()); 124 } 125 }); 126 } 127 128 @Test messageBuilder_mergeFrom_InputStream()129 public void messageBuilder_mergeFrom_InputStream() { 130 setup(); 131 verifyExceptions( 132 new ParseTester() { 133 @Override 134 public DescriptorProto parse(InputStream in) throws IOException { 135 return DescriptorProto.newBuilder().mergeFrom(in).build(); 136 } 137 }); 138 } 139 140 @Test messageBuilder_mergeFrom_InputStreamAndExtensionRegistry()141 public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() { 142 setup(); 143 verifyExceptions( 144 new ParseTester() { 145 @Override 146 public DescriptorProto parse(InputStream in) throws IOException { 147 return DescriptorProto.newBuilder() 148 .mergeFrom(in, ExtensionRegistry.newInstance()) 149 .build(); 150 } 151 }); 152 } 153 154 @Test messageBuilder_mergeFrom_CodedInputStream()155 public void messageBuilder_mergeFrom_CodedInputStream() { 156 setup(); 157 verifyExceptions( 158 new ParseTester() { 159 @Override 160 public DescriptorProto parse(InputStream in) throws IOException { 161 return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build(); 162 } 163 }); 164 } 165 166 @Test messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry()167 public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() { 168 setup(); 169 verifyExceptions( 170 new ParseTester() { 171 @Override 172 public DescriptorProto parse(InputStream in) throws IOException { 173 return DescriptorProto.newBuilder() 174 .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()) 175 .build(); 176 } 177 }); 178 } 179 180 @Test messageBuilder_mergeDelimitedFrom_InputStream()181 public void messageBuilder_mergeDelimitedFrom_InputStream() { 182 setupDelimited(); 183 verifyExceptions( 184 new ParseTester() { 185 @Override 186 public DescriptorProto parse(InputStream in) throws IOException { 187 DescriptorProto.Builder builder = DescriptorProto.newBuilder(); 188 builder.mergeDelimitedFrom(in); 189 return builder.build(); 190 } 191 }); 192 } 193 194 @Test messageBuilder_mergeDelimitedFrom_InputStream_malformed()195 public void messageBuilder_mergeDelimitedFrom_InputStream_malformed() throws Exception { 196 byte[] body = new byte[80]; 197 CodedOutputStream cos = CodedOutputStream.newInstance(body); 198 cos.writeUInt32NoTag(90); // Greater than bytes in stream 199 cos.writeTag(DescriptorProto.ENUM_TYPE_FIELD_NUMBER, WireFormat.WIRETYPE_LENGTH_DELIMITED); 200 cos.writeUInt32NoTag(98); // Nested message with size larger than parent 201 cos.writeTag(1000, WireFormat.WIRETYPE_LENGTH_DELIMITED); 202 cos.writeUInt32NoTag(100); // Unknown field with size larger than parent 203 ByteArrayInputStream bais = new ByteArrayInputStream(body); 204 try { 205 DescriptorProto.parseDelimitedFrom(bais); 206 fail(); 207 } catch (InvalidProtocolBufferException expected) { 208 } 209 } 210 211 @Test messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry()212 public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() { 213 setupDelimited(); 214 verifyExceptions( 215 new ParseTester() { 216 @Override 217 public DescriptorProto parse(InputStream in) throws IOException { 218 DescriptorProto.Builder builder = DescriptorProto.newBuilder(); 219 builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance()); 220 return builder.build(); 221 } 222 }); 223 } 224 verifyExceptions(ParseTester parseTester)225 private void verifyExceptions(ParseTester parseTester) { 226 // No exception 227 try { 228 assertThat(parseTester.parse(new ByteArrayInputStream(serializedProto))) 229 .isEqualTo(DescriptorProto.getDescriptor().toProto()); 230 } catch (IOException e) { 231 fail("No exception expected: " + e); 232 } 233 234 // IOException 235 try { 236 // using a "broken" stream that will throw part-way through reading the message 237 parseTester.parse(broken(new ByteArrayInputStream(serializedProto))); 238 fail("IOException expected but not thrown"); 239 } catch (IOException e) { 240 assertThat(e).isNotInstanceOf(InvalidProtocolBufferException.class); 241 } 242 243 // InvalidProtocolBufferException 244 try { 245 // make the serialized proto invalid 246 for (int i = 0; i < 50; i++) { 247 serializedProto[i] = -1; 248 } 249 parseTester.parse(new ByteArrayInputStream(serializedProto)); 250 fail("InvalidProtocolBufferException expected but not thrown"); 251 } catch (IOException e) { 252 assertThat(e).isInstanceOf(InvalidProtocolBufferException.class); 253 } 254 } 255 broken(InputStream i)256 private InputStream broken(InputStream i) { 257 return new FilterInputStream(i) { 258 int count = 0; 259 260 @Override 261 public int read() throws IOException { 262 if (count++ >= 50) { 263 throw new IOException("I'm broken!"); 264 } 265 return super.read(); 266 } 267 268 @Override 269 public int read(byte[] b, int off, int len) throws IOException { 270 if ((count += len) >= 50) { 271 throw new IOException("I'm broken!"); 272 } 273 return super.read(b, off, len); 274 } 275 }; 276 } 277 } 278