1 // Protocol Buffers - Google's data interchange format 2 // Copyright 2008 Google Inc. All rights reserved. 3 // https://developers.google.com/protocol-buffers/ 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions are 7 // met: 8 // 9 // * Redistributions of source code must retain the above copyright 10 // notice, this list of conditions and the following disclaimer. 11 // * Redistributions in binary form must reproduce the above 12 // copyright notice, this list of conditions and the following disclaimer 13 // in the documentation and/or other materials provided with the 14 // distribution. 15 // * Neither the name of Google Inc. nor the names of its 16 // contributors may be used to endorse or promote products derived from 17 // this software without specific prior written permission. 18 // 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 package com.google.protobuf; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertFalse; 35 import static org.junit.Assert.assertTrue; 36 import static org.junit.Assert.fail; 37 38 import com.google.protobuf.DescriptorProtos.DescriptorProto; 39 import java.io.ByteArrayInputStream; 40 import java.io.ByteArrayOutputStream; 41 import java.io.FilterInputStream; 42 import java.io.IOException; 43 import java.io.InputStream; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.junit.runners.JUnit4; 47 48 /** 49 * Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser} 50 * interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want 51 * to distinguish between invalid protos vs. actual I/O errors (like failures reading from a socket, 52 * etc.). So, when we're not using the parser directly, an {@link IOException} should be thrown 53 * where appropriate, instead of always an {@link InvalidProtocolBufferException}. 54 * 55 * @author jh@squareup.com (Joshua Humphries) 56 */ 57 @RunWith(JUnit4.class) 58 public class ParseExceptionsTest { 59 60 private interface ParseTester { parse(InputStream in)61 DescriptorProto parse(InputStream in) throws IOException; 62 } 63 64 private byte[] serializedProto; 65 setup()66 private void setup() { 67 serializedProto = DescriptorProto.getDescriptor().toProto().toByteArray(); 68 } 69 setupDelimited()70 private void setupDelimited() { 71 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 72 try { 73 DescriptorProto.getDescriptor().toProto().writeDelimitedTo(bos); 74 } catch (IOException e) { 75 fail("Exception not expected: " + e); 76 } 77 serializedProto = bos.toByteArray(); 78 } 79 80 @Test message_parseFrom_InputStream()81 public void message_parseFrom_InputStream() { 82 setup(); 83 verifyExceptions( 84 new ParseTester() { 85 @Override 86 public DescriptorProto parse(InputStream in) throws IOException { 87 return DescriptorProto.parseFrom(in); 88 } 89 }); 90 } 91 92 @Test message_parseFrom_InputStreamAndExtensionRegistry()93 public void message_parseFrom_InputStreamAndExtensionRegistry() { 94 setup(); 95 verifyExceptions( 96 new ParseTester() { 97 @Override 98 public DescriptorProto parse(InputStream in) throws IOException { 99 return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance()); 100 } 101 }); 102 } 103 104 @Test message_parseFrom_CodedInputStream()105 public void message_parseFrom_CodedInputStream() { 106 setup(); 107 verifyExceptions( 108 new ParseTester() { 109 @Override 110 public DescriptorProto parse(InputStream in) throws IOException { 111 return DescriptorProto.parseFrom(CodedInputStream.newInstance(in)); 112 } 113 }); 114 } 115 116 @Test message_parseFrom_CodedInputStreamAndExtensionRegistry()117 public void message_parseFrom_CodedInputStreamAndExtensionRegistry() { 118 setup(); 119 verifyExceptions( 120 new ParseTester() { 121 @Override 122 public DescriptorProto parse(InputStream in) throws IOException { 123 return DescriptorProto.parseFrom( 124 CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()); 125 } 126 }); 127 } 128 129 @Test message_parseDelimitedFrom_InputStream()130 public void message_parseDelimitedFrom_InputStream() { 131 setupDelimited(); 132 verifyExceptions( 133 new ParseTester() { 134 @Override 135 public DescriptorProto parse(InputStream in) throws IOException { 136 return DescriptorProto.parseDelimitedFrom(in); 137 } 138 }); 139 } 140 141 @Test message_parseDelimitedFrom_InputStreamAndExtensionRegistry()142 public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() { 143 setupDelimited(); 144 verifyExceptions( 145 new ParseTester() { 146 @Override 147 public DescriptorProto parse(InputStream in) throws IOException { 148 return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance()); 149 } 150 }); 151 } 152 153 @Test messageBuilder_mergeFrom_InputStream()154 public void messageBuilder_mergeFrom_InputStream() { 155 setup(); 156 verifyExceptions( 157 new ParseTester() { 158 @Override 159 public DescriptorProto parse(InputStream in) throws IOException { 160 return DescriptorProto.newBuilder().mergeFrom(in).build(); 161 } 162 }); 163 } 164 165 @Test messageBuilder_mergeFrom_InputStreamAndExtensionRegistry()166 public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() { 167 setup(); 168 verifyExceptions( 169 new ParseTester() { 170 @Override 171 public DescriptorProto parse(InputStream in) throws IOException { 172 return DescriptorProto.newBuilder() 173 .mergeFrom(in, ExtensionRegistry.newInstance()) 174 .build(); 175 } 176 }); 177 } 178 179 @Test messageBuilder_mergeFrom_CodedInputStream()180 public void messageBuilder_mergeFrom_CodedInputStream() { 181 setup(); 182 verifyExceptions( 183 new ParseTester() { 184 @Override 185 public DescriptorProto parse(InputStream in) throws IOException { 186 return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build(); 187 } 188 }); 189 } 190 191 @Test messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry()192 public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() { 193 setup(); 194 verifyExceptions( 195 new ParseTester() { 196 @Override 197 public DescriptorProto parse(InputStream in) throws IOException { 198 return DescriptorProto.newBuilder() 199 .mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()) 200 .build(); 201 } 202 }); 203 } 204 205 @Test messageBuilder_mergeDelimitedFrom_InputStream()206 public void messageBuilder_mergeDelimitedFrom_InputStream() { 207 setupDelimited(); 208 verifyExceptions( 209 new ParseTester() { 210 @Override 211 public DescriptorProto parse(InputStream in) throws IOException { 212 DescriptorProto.Builder builder = DescriptorProto.newBuilder(); 213 builder.mergeDelimitedFrom(in); 214 return builder.build(); 215 } 216 }); 217 } 218 219 @Test messageBuilder_mergeDelimitedFrom_InputStream_malformed()220 public void messageBuilder_mergeDelimitedFrom_InputStream_malformed() throws Exception { 221 byte[] body = new byte[80]; 222 CodedOutputStream cos = CodedOutputStream.newInstance(body); 223 cos.writeRawVarint32(90); // Greater than bytes in stream 224 cos.writeTag(DescriptorProto.ENUM_TYPE_FIELD_NUMBER, WireFormat.WIRETYPE_LENGTH_DELIMITED); 225 cos.writeRawVarint32(98); // Nested message with size larger than parent 226 cos.writeTag(1000, WireFormat.WIRETYPE_LENGTH_DELIMITED); 227 cos.writeRawVarint32(100); // Unknown field with size larger than parent 228 ByteArrayInputStream bais = new ByteArrayInputStream(body); 229 try { 230 DescriptorProto.parseDelimitedFrom(bais); 231 fail(); 232 } catch (InvalidProtocolBufferException expected) { 233 } 234 } 235 236 @Test messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry()237 public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() { 238 setupDelimited(); 239 verifyExceptions( 240 new ParseTester() { 241 @Override 242 public DescriptorProto parse(InputStream in) throws IOException { 243 DescriptorProto.Builder builder = DescriptorProto.newBuilder(); 244 builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance()); 245 return builder.build(); 246 } 247 }); 248 } 249 verifyExceptions(ParseTester parseTester)250 private void verifyExceptions(ParseTester parseTester) { 251 // No exception 252 try { 253 assertEquals( 254 DescriptorProto.getDescriptor().toProto(), 255 parseTester.parse(new ByteArrayInputStream(serializedProto))); 256 } catch (IOException e) { 257 fail("No exception expected: " + e); 258 } 259 260 // IOException 261 try { 262 // using a "broken" stream that will throw part-way through reading the message 263 parseTester.parse(broken(new ByteArrayInputStream(serializedProto))); 264 fail("IOException expected but not thrown"); 265 } catch (IOException e) { 266 assertFalse(e instanceof InvalidProtocolBufferException); 267 } 268 269 // InvalidProtocolBufferException 270 try { 271 // make the serialized proto invalid 272 for (int i = 0; i < 50; i++) { 273 serializedProto[i] = -1; 274 } 275 parseTester.parse(new ByteArrayInputStream(serializedProto)); 276 fail("InvalidProtocolBufferException expected but not thrown"); 277 } catch (IOException e) { 278 assertTrue(e instanceof InvalidProtocolBufferException); 279 } 280 } 281 broken(InputStream i)282 private InputStream broken(InputStream i) { 283 return new FilterInputStream(i) { 284 int count = 0; 285 286 @Override 287 public int read() throws IOException { 288 if (count++ >= 50) { 289 throw new IOException("I'm broken!"); 290 } 291 return super.read(); 292 } 293 294 @Override 295 public int read(byte[] b, int off, int len) throws IOException { 296 if ((count += len) >= 50) { 297 throw new IOException("I'm broken!"); 298 } 299 return super.read(b, off, len); 300 } 301 }; 302 } 303 } 304