• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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