From d111ee34c899bb6184332cc2b9b2aaa090dd0425 Mon Sep 17 00:00:00 2001 From: Wojciech Strzalka Date: Tue, 10 Jun 2025 15:43:30 +0200 Subject: [PATCH 1/2] Add handling of unexpected end of data stream --- CHANGELOG.md | 2 + resources/broken7000bytes.pbf | Bin 0 -> 12595 bytes .../crosby/binary/file/BlockInputStream.java | 8 +- .../binary/file/BlockReaderAdapter.java | 2 + test.java/crosby/binary/ReadFileTest.java | 311 +++++++++++++++++- 5 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 resources/broken7000bytes.pbf diff --git a/CHANGELOG.md b/CHANGELOG.md index baf32dc..7b75345 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Unreleased +- Java: Add handling of unexpected end of data stream [ISSUE#80](https://github.com/openstreetmap/OSM-binary/issues/80) + ## Release notes for 1.6.0 (2024-12-10) - Java: Update to latest protoc and protobuf runtime versions [#84](https://github.com/openstreetmap/OSM-binary/pull/84) diff --git a/resources/broken7000bytes.pbf b/resources/broken7000bytes.pbf new file mode 100644 index 0000000000000000000000000000000000000000..e06e17daadfcd8f230de553ef417e52575dfc80d GIT binary patch literal 12595 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 literal 0 HcmV?d00001 diff --git a/src.java/crosby/binary/file/BlockInputStream.java b/src.java/crosby/binary/file/BlockInputStream.java index 1da99bc..04ffffd 100644 --- a/src.java/crosby/binary/file/BlockInputStream.java +++ b/src.java/crosby/binary/file/BlockInputStream.java @@ -18,7 +18,6 @@ License, or (at your option) any later version. package crosby.binary.file; import java.io.Closeable; -import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -29,13 +28,14 @@ public BlockInputStream(InputStream input, BlockReaderAdapter adaptor) { this.adaptor = adaptor; } - public void process() throws IOException { + public void process() throws Exception { try { - while (true) { + while (input.available() > 0) { FileBlock.process(input, adaptor); } - } catch (EOFException e) { adaptor.complete(); + } catch (Exception e) { + adaptor.failure(e); } } diff --git a/src.java/crosby/binary/file/BlockReaderAdapter.java b/src.java/crosby/binary/file/BlockReaderAdapter.java index 4c8e4c2..a52a62e 100644 --- a/src.java/crosby/binary/file/BlockReaderAdapter.java +++ b/src.java/crosby/binary/file/BlockReaderAdapter.java @@ -37,4 +37,6 @@ public interface BlockReaderAdapter { /** Called when the file is fully read. */ void complete(); + + void failure(Exception e) throws Exception; } diff --git a/test.java/crosby/binary/ReadFileTest.java b/test.java/crosby/binary/ReadFileTest.java index 4c4641f..9c39073 100644 --- a/test.java/crosby/binary/ReadFileTest.java +++ b/test.java/crosby/binary/ReadFileTest.java @@ -20,7 +20,7 @@ public class ReadFileTest { @Test - public void test() throws Exception { + public void testValidFile() throws Exception { String expected = ("" + "Got header block.\n" + "Dense node, ID 653970877 @ 51.763603,-0.228757\n" + @@ -447,7 +447,309 @@ public void test() throws Exception { " Key=value pairs: cycleway=track highway=cycleway name=Alban Way ncn_ref=61 \n" + "Got some relations to parse.\n" + "Complete!\n").replace("\n", System.lineSeparator()); - try (InputStream input = ReadFileTest.class.getResourceAsStream("/sample.pbf"); + test("/sample.pbf", expected); + } + + @Test + public void testBrokenFile() throws Exception { + String expected = (""+ + "Got header block.\n" + + "Dense node, ID 653970877 @ 51.763603,-0.228757\n" + + "Dense node, ID 647105170 @ 51.763591,-0.234465\n" + + "Dense node, ID 672663476 @ 51.765749,-0.229070\n" + + "Dense node, ID 241806356 @ 51.768945,-0.232662\n" + + "Dense node, ID 692945017 @ 51.766185,-0.230069\n" + + "Dense node, ID 1709246734 @ 51.766433,-0.230854\n" + + "Dense node, ID 175685506 @ 51.765169,-0.229374\n" + + "Dense node, ID 647105129 @ 51.769327,-0.218457\n" + + "Dense node, ID 647105160 @ 51.768192,-0.231686\n" + + "Dense node, ID 672663473 @ 51.765530,-0.229187\n" + + "Dense node, ID 647105141 @ 51.773204,-0.222598\n" + + "Dense node, ID 25365926 @ 51.766340,-0.233556\n" + + "Dense node, ID 1685167296 @ 51.766924,-0.234783\n" + + "Dense node, ID 677439943 @ 51.763178,-0.230230\n" + + "Dense node, ID 1701110757 @ 51.766400,-0.228489\n" + + "Dense node, ID 663806673 @ 51.765470,-0.229220\n" + + "Dense node, ID 502550970 @ 51.765118,-0.233667\n" + + "Dense node, ID 692887095 @ 51.766318,-0.229190\n" + + "Dense node, ID 1685167376 @ 51.760411,-0.241161\n" + + "Dense node, ID 175697821 @ 51.765000,-0.232204\n" + + "Dense node, ID 677438877 @ 51.764126,-0.228303\n" + + "Dense node, ID 175685111 @ 51.764882,-0.229966\n" + + "Dense node, ID 647105131 @ 51.769022,-0.217223\n" + + "Dense node, ID 240134267 @ 51.764217,-0.233120\n" + + "Dense node, ID 691203111 @ 51.765755,-0.230230\n" + + "Dense node, ID 1685167394 @ 51.761213,-0.240218\n" + + "Dense node, ID 534873274 @ 51.763918,-0.236563\n" + + "Dense node, ID 676945192 @ 51.765148,-0.230615\n" + + "Dense node, ID 691203106 @ 51.764494,-0.233449\n" + + "Dense node, ID 647105155 @ 51.769580,-0.232061\n" + + "Dense node, ID 32950368 @ 51.769048,-0.232790\n" + + "Dense node, ID 647105133 @ 51.769183,-0.216784\n" + + "Dense node, ID 175683944 @ 51.763140,-0.232112\n" + + "Dense node, ID 623540467 @ 51.765719,-0.225990\n" + + "Dense node, ID 647225601 @ 51.762732,-0.231722\n" + + "Dense node, ID 32953195 @ 51.761987,-0.231091\n" + + "Dense node, ID 653970876 @ 51.763436,-0.229153\n" + + "Dense node, ID 676945352 @ 51.765646,-0.228469\n" + + "Dense node, ID 663806670 @ 51.765540,-0.228771\n" + + "Dense node, ID 1709246676 @ 51.766438,-0.231121\n" + + "Dense node, ID 647105047 @ 51.774057,-0.222895\n" + + "Dense node, ID 175697862 @ 51.765004,-0.232747\n" + + "Dense node, ID 647105145 @ 51.771007,-0.230355\n" + + "Dense node, ID 647105167 @ 51.762860,-0.236278\n" + + "Dense node, ID 1111758067 @ 51.771433,-0.216984\n" + + "Dense node, ID 647105166 @ 51.767468,-0.234229\n" + + "Dense node, ID 692887118 @ 51.766186,-0.228918\n" + + "Dense node, ID 663806658 @ 51.765679,-0.228614\n" + + "Dense node, ID 175685507 @ 51.765508,-0.229788\n" + + "Dense node, ID 647224486 @ 51.766388,-0.228706\n" + + "Dense node, ID 502552074 @ 51.766711,-0.229590\n" + + "Dense node, ID 647105132 @ 51.768905,-0.216932\n" + + "Dense node, ID 25365925 @ 51.766651,-0.233518\n" + + "Dense node, ID 623540472 @ 51.765321,-0.225475\n" + + "Dense node, ID 691202857 @ 51.766804,-0.231711\n" + + "Dense node, ID 175686201 @ 51.765721,-0.228361\n" + + "Dense node, ID 927070648 @ 51.763087,-0.232061\n" + + "Dense node, ID 25365924 @ 51.767090,-0.233453\n" + + "Dense node, ID 676945335 @ 51.765388,-0.228437\n" + + "Dense node, ID 647105127 @ 51.769321,-0.219637\n" + + "Dense node, ID 647105134 @ 51.769124,-0.216290\n" + + "Dense node, ID 30983853 @ 51.764268,-0.233185\n" + + "Dense node, ID 647105164 @ 51.767548,-0.233295\n" + + "Dense node, ID 502552081 @ 51.766833,-0.233484\n" + + "Dense node, ID 691202855 @ 51.766809,-0.231946\n" + + "Dense node, ID 647057820 @ 51.765382,-0.226710\n" + + "Dense node, ID 691202869 @ 51.767216,-0.231947\n" + + "Dense node, ID 647105159 @ 51.768849,-0.232458\n" + + "Dense node, ID 1739780291 @ 51.764890,-0.226086\n" + + "Dense node, ID 676945267 @ 51.763905,-0.228040\n" + + "Dense node, ID 663806664 @ 51.765444,-0.229274\n" + + "Dense node, ID 647105143 @ 51.771399,-0.230034\n" + + "Dense node, ID 691202858 @ 51.765928,-0.232698\n" + + "Dense node, ID 1701110775 @ 51.766290,-0.228709\n" + + "Dense node, ID 365548881 @ 51.763854,-0.232807\n" + + "Dense node, ID 647224465 @ 51.765604,-0.226263\n" + + "Dense node, ID 691202873 @ 51.766711,-0.232826\n" + + "Dense node, ID 287659881 @ 51.766233,-0.228823\n" + + "Dense node, ID 1685167328 @ 51.765389,-0.235803\n" + + "Dense node, ID 1685167381 @ 51.762135,-0.238938\n" + + "Dense node, ID 1685167371 @ 51.768683,-0.233758\n" + + "Dense node, ID 1709246791 @ 51.765771,-0.229747\n" + + "Dense node, ID 647105156 @ 51.769420,-0.232072\n" + + "Dense node, ID 647105139 @ 51.773291,-0.221257\n" + + "Dense node, ID 32953193 @ 51.763418,-0.232387\n" + + "Dense node, ID 676945199 @ 51.765151,-0.230782\n" + + "Dense node, ID 647105147 @ 51.770210,-0.231976\n" + + "Dense node, ID 672628083 @ 51.764391,-0.225433\n" + + "Dense node, ID 25365922 @ 51.768145,-0.233167\n" + + "Dense node, ID 1709246741 @ 51.765960,-0.229886\n" + + "Dense node, ID 647105153 @ 51.769673,-0.232265\n" + + "Dense node, ID 30983851 @ 51.765372,-0.233546\n" + + "Dense node, ID 691202863 @ 51.765224,-0.232225\n" + + "Dense node, ID 691202838 @ 51.767798,-0.233387\n" + + "Dense node, ID 175684459 @ 51.763370,-0.231564\n" + + "Dense node, ID 1685167313 @ 51.762503,-0.238485\n" + + "Dense node, ID 692945016 @ 51.765714,-0.230069\n" + + "Dense node, ID 25365921 @ 51.768513,-0.232722\n" + + "Dense node, ID 676945322 @ 51.765118,-0.229479\n" + + "Dense node, ID 534873251 @ 51.763658,-0.236760\n" + + "Dense node, ID 1685167341 @ 51.768171,-0.234063\n" + + "Dense node, ID 691203110 @ 51.765769,-0.230874\n" + + "Dense node, ID 676945292 @ 51.764506,-0.228754\n" + + "Dense node, ID 1685167391 @ 51.761506,-0.239827\n" + + "Dense node, ID 676945241 @ 51.763212,-0.229644\n" + + "Dense node, ID 663806653 @ 51.765898,-0.228877\n" + + "Dense node, ID 623624259 @ 51.764905,-0.234965\n" + + "Dense node, ID 1685167373 @ 51.763777,-0.237235\n" + + "Dense node, ID 676945320 @ 51.765375,-0.230143\n" + + "Dense node, ID 240134268 @ 51.764403,-0.232382\n" + + "Dense node, ID 676945316 @ 51.764949,-0.230532\n" + + "Dense node, ID 623624154 @ 51.765244,-0.234365\n" + + "Dense node, ID 647105142 @ 51.774147,-0.226321\n" + + "Dense node, ID 1739780285 @ 51.764824,-0.226000\n" + + "Dense node, ID 175697671 @ 51.765012,-0.233620\n" + + "Dense node, ID 647224613 @ 51.764970,-0.229134\n" + + "Dense node, ID 647105121 @ 51.769055,-0.221268\n" + + "Dense node, ID 692887101 @ 51.766293,-0.228488\n" + + "Dense node, ID 175683342 @ 51.763273,-0.229558\n" + + "Dense node, ID 240134269 @ 51.765577,-0.230133\n" + + "Dense node, ID 691203053 @ 51.766871,-0.230638\n" + + "Dense node, ID 1697422651 @ 51.763725,-0.228467\n" + + "Dense node, ID 534873285 @ 51.764110,-0.236786\n" + + "Dense node, ID 647105148 @ 51.770131,-0.232104\n" + + "Dense node, ID 647105165 @ 51.767482,-0.233317\n" + + "Dense node, ID 534873185 @ 51.763403,-0.236752\n" + + "Dense node, ID 175685104 @ 51.764391,-0.231506\n" + + "Dense node, ID 647105163 @ 51.768079,-0.233048\n" + + "Dense node, ID 651652536 @ 51.764591,-0.224432\n" + + "Dense node, ID 647105115 @ 51.766990,-0.227373\n" + + "Dense node, ID 677439944 @ 51.763332,-0.229790\n" + + "Dense node, ID 647105162 @ 51.768232,-0.232866\n" + + "Dense node, ID 676945319 @ 51.765218,-0.230449\n" + + "Dense node, ID 1539682123 @ 51.769102,-0.232828\n" + + "Dense node, ID 534873208 @ 51.763536,-0.236889\n" + + "Dense node, ID 647105128 @ 51.769354,-0.219090\n" + + "Dense node, ID 1739780280 @ 51.764758,-0.225914\n" + + "Dense node, ID 175698323 @ 51.767216,-0.231110\n" + + "Dense node, ID 676945189 @ 51.764650,-0.230926\n" + + "Dense node, ID 1739780294 @ 51.764955,-0.224922\n" + + "Dense node, ID 676945326 @ 51.765291,-0.229382\n" + + "Dense node, ID 663806672 @ 51.765417,-0.229059\n" + + "Dense node, ID 45169425 @ 51.769130,-0.233478\n" + + "Dense node, ID 672663469 @ 51.765930,-0.229036\n" + + "Dense node, ID 675146 @ 51.769270,-0.232860\n" + + "Dense node, ID 691203054 @ 51.766658,-0.230273\n" + + "Dense node, ID 1606957353 @ 51.760049,-0.241558\n" + + "Dense node, ID 647105125 @ 51.769248,-0.220260\n" + + "Dense node, ID 534874147 @ 51.765262,-0.235825\n" + + "Dense node, ID 14713407 @ 51.765828,-0.227391\n" + + "Dense node, ID 818056434 @ 51.766040,-0.233470\n" + + "Dense node, ID 1111758069 @ 51.769198,-0.216444\n" + + "Dense node, ID 175699187 @ 51.765663,-0.231004\n" + + "Dense node, ID 175698155 @ 51.767389,-0.230809\n" + + "Dense node, ID 691202861 @ 51.765516,-0.231002\n" + + "Dense node, ID 651594517 @ 51.763745,-0.228419\n" + + "Dense node, ID 691203051 @ 51.765901,-0.231217\n" + + "Dense node, ID 647224485 @ 51.765127,-0.226399\n" + + "Dense node, ID 1709246749 @ 51.765632,-0.230025\n" + + "Dense node, ID 677440300 @ 51.762625,-0.231624\n" + + "Dense node, ID 647105172 @ 51.764294,-0.233070\n" + + "Dense node, ID 175686498 @ 51.765424,-0.228052\n" + + "Dense node, ID 692944963 @ 51.764665,-0.233953\n" + + "Dense node, ID 663806656 @ 51.765763,-0.228715\n" + + "Dense node, ID 647105154 @ 51.769626,-0.232179\n" + + "Dense node, ID 676945317 @ 51.765015,-0.230385\n" + + "Dense node, ID 647105169 @ 51.763033,-0.235323\n" + + "Dense node, ID 692945021 @ 51.766617,-0.229479\n" + + "Dense node, ID 1709246789 @ 51.766231,-0.230173\n" + + "Dense node, ID 175686499 @ 51.765976,-0.228635\n" + + "Dense node, ID 691202866 @ 51.767110,-0.232955\n" + + "Dense node, ID 1111758072 @ 51.769507,-0.216315\n" + + "Dense node, ID 647105123 @ 51.769155,-0.220818\n" + + "Dense node, ID 672663468 @ 51.765622,-0.228672\n" + + "Dense node, ID 676945197 @ 51.765281,-0.230541\n" + + "Dense node, ID 692945020 @ 51.766471,-0.229673\n" + + "Dense node, ID 175697881 @ 51.764664,-0.232747\n" + + "Dense node, ID 175685109 @ 51.764946,-0.230095\n" + + "Dense node, ID 1685167304 @ 51.760787,-0.240738\n" + + "Dense node, ID 692944951 @ 51.764943,-0.234254\n" + + "Dense node, ID 692945019 @ 51.766225,-0.229673\n" + + "Dense node, ID 676945334 @ 51.765467,-0.228255\n" + + "Dense node, ID 175684463 @ 51.765445,-0.226790\n" + + "Dense node, ID 692944957 @ 51.764651,-0.234168\n" + + "Dense node, ID 647105144 @ 51.771332,-0.229905\n" + + "Dense node, ID 691203055 @ 51.765928,-0.230187\n" + + "Dense node, ID 676945331 @ 51.765589,-0.229749\n" + + "Dense node, ID 672663474 @ 51.765638,-0.229315\n" + + "Dense node, ID 647105146 @ 51.770283,-0.231836\n" + + "Dense node, ID 534873171 @ 51.763005,-0.237147\n" + + "Dense node, ID 647105157 @ 51.769307,-0.232308\n" + + "Dense node, ID 676945327 @ 51.765347,-0.229744\n" + + "Dense node, ID 675150 @ 51.766907,-0.229904\n" + + "Dense node, ID 663806666 @ 51.765165,-0.228973\n" + + "Dense node, ID 691202871 @ 51.766950,-0.232826\n" + + "Dense node, ID 672663477 @ 51.765646,-0.228948\n" + + "Dense node, ID 647105158 @ 51.769015,-0.232297\n" + + "Dense node, ID 673784380 @ 51.762202,-0.231241\n" + + "Dense node, ID 647105152 @ 51.769739,-0.232330\n" + + "Dense node, ID 692945022 @ 51.766344,-0.228825\n" + + "Dense node, ID 676945315 @ 51.764929,-0.230336\n" + + "Dense node, ID 676945346 @ 51.765450,-0.228506\n" + + "Dense node, ID 647105119 @ 51.768119,-0.223854\n" + + "Dense node, ID 175698430 @ 51.766924,-0.231110\n" + + "Dense node, ID 1685167387 @ 51.765901,-0.235408\n" + + "Dense node, ID 175685910 @ 51.766003,-0.227820\n" + + "Dense node, ID 820969139 @ 51.767836,-0.231358\n" + + "Dense node, ID 647105102 @ 51.763883,-0.232727\n" + + "Dense node, ID 675151 @ 51.766141,-0.228136\n" + + "Dense node, ID 175698324 @ 51.766008,-0.231131\n" + + "Dense node, ID 1685167282 @ 51.762958,-0.237989\n" + + "Dense node, ID 502552090 @ 51.765557,-0.233577\n" + + "Dense node, ID 623624155 @ 51.765449,-0.234590\n" + + "Dense node, ID 267826070 @ 51.764017,-0.232970\n" + + "Dense node, ID 25365930 @ 51.766791,-0.234972\n" + + "Dense node, ID 676945195 @ 51.765156,-0.230570\n" + + "Dense node, ID 1709246675 @ 51.766423,-0.230168\n" + + "Dense node, ID 647105137 @ 51.774248,-0.218055\n" + + "Dense node, ID 651652534 @ 51.764261,-0.225160\n" + + "Dense node, ID 676945293 @ 51.764816,-0.229133\n" + + "Dense node, ID 1692947499 @ 51.773860,-0.225851\n" + + "Dense node, ID 623624257 @ 51.765396,-0.234075\n" + + "Dense node, ID 175697824 @ 51.764998,-0.232032\n" + + "Dense node, ID 672663478 @ 51.765575,-0.229104\n" + + "Dense node, ID 1685167290 @ 51.763311,-0.237639\n" + + "Dense node, ID 390911769 @ 51.766861,-0.229798\n" + + "Dense node, ID 676945323 @ 51.765506,-0.229937\n" + + "Dense node, ID 647105136 @ 51.773720,-0.217976\n" + + "Dense node, ID 1539682039 @ 51.768036,-0.233265\n" + + "Dense node, ID 691202860 @ 51.766247,-0.230595\n" + + "Dense node, ID 1145410964 @ 51.769148,-0.232860\n" + + "Dense node, ID 647105130 @ 51.769188,-0.217728\n" + + "Dense node, ID 691203049 @ 51.766645,-0.234564\n" + + "Dense node, ID 1539682089 @ 51.768368,-0.232938\n" + + "Dense node, ID 175698550 @ 51.766911,-0.230809\n" + + "Dense node, ID 623540479 @ 51.765560,-0.224961\n" + + "Dense node, ID 677439941 @ 51.763240,-0.230472\n" + + "Dense node, ID 25365927 @ 51.766333,-0.232681\n" + + "Dense node, ID 647105135 @ 51.770431,-0.216476\n" + + "Dense node, ID 30983852 @ 51.764773,-0.233577\n" + + "Dense node, ID 647105150 @ 51.769938,-0.232265\n" + + "Dense node, ID 623624261 @ 51.764407,-0.235985\n" + + "Dense node, ID 647105149 @ 51.770024,-0.232212\n" + + "Dense node, ID 677439946 @ 51.763219,-0.229690\n" + + "Dense node, ID 691203109 @ 51.765671,-0.232912\n" + + "Dense node, ID 647105171 @ 51.764248,-0.233242\n" + + "Dense node, ID 1709246746 @ 51.766196,-0.230058\n" + + "Dense node, ID 175685106 @ 51.764728,-0.230781\n" + + "Dense node, ID 663806661 @ 51.765857,-0.228507\n" + + "Dense node, ID 677439947 @ 51.764197,-0.228387\n" + + "Dense node, ID 647105117 @ 51.767435,-0.226150\n" + + "Dense node, ID 647105168 @ 51.762754,-0.235838\n" + + "Dense node, ID 623624267 @ 51.764016,-0.233964\n" + + "Dense node, ID 1709246737 @ 51.766423,-0.230671\n" + + "Dense node, ID 175684462 @ 51.764271,-0.229245\n" + + "Dense node, ID 175698551 @ 51.766539,-0.230166\n" + + "Dense node, ID 675148 @ 51.768657,-0.232378\n" + + "Dense node, ID 676945332 @ 51.764887,-0.229157\n" + + "Dense node, ID 675149 @ 51.767913,-0.231459\n" + + "Dense node, ID 692945018 @ 51.766178,-0.229758\n" + + "Dense node, ID 623540483 @ 51.765155,-0.224456\n" + + "Dense node, ID 676945350 @ 51.765533,-0.228712\n" + + "Dense node, ID 175698975 @ 51.765729,-0.233577\n" + + "Dense node, ID 175685102 @ 51.764176,-0.232069\n" + + "Dense node, ID 676945347 @ 51.765417,-0.228572\n" + + "Dense node, ID 534873262 @ 51.763775,-0.236889\n" + + "Dense node, ID 25365931 @ 51.765437,-0.236066\n" + + "Dense node, ID 672663470 @ 51.765668,-0.229614\n" + + "Dense node, ID 647105138 @ 51.773941,-0.221418\n" + + "Dense node, ID 647105151 @ 51.769819,-0.232340\n" + + "Dense node, ID 32953194 @ 51.762232,-0.231263\n" + + "Dense node, ID 1685167315 @ 51.766349,-0.235121\n" + + "Dense node, ID 1111758071 @ 51.769058,-0.216775\n" + + "Dense node, ID 691203098 @ 51.764324,-0.234279\n" + + "Dense node, ID 175698553 @ 51.766253,-0.230172\n" + + "Dense node, ID 1685167287 @ 51.767572,-0.234395\n" + + "Dense node, ID 672663471 @ 51.765452,-0.229359\n" + + "Dense node, ID 676945325 @ 51.765209,-0.229575\n" + + "Dense node, ID 623624156 @ 51.765622,-0.234693\n" + + "Dense node, ID 647105140 @ 51.773198,-0.222341\n" + + "Dense node, ID 25365928 @ 51.766333,-0.232198\n" + + "Dense node, ID 676945329 @ 51.765389,-0.229634\n" + + "Dense node, ID 663806668 @ 51.765337,-0.228533\n" + + "Dense node, ID 692944966 @ 51.764977,-0.233985\n" + + "Dense node, ID 691203099 @ 51.764162,-0.234157\n" + + "Dense node, ID 175685100 @ 51.764091,-0.232103\n" + + "Dense node, ID 25365923 @ 51.767521,-0.233449\n" + + "Dense node, ID 647105161 @ 51.767973,-0.232169\n" + + "Dense node, ID 672663467 @ 51.765478,-0.228989\n" + + "Dense node, ID 691202854 @ 51.766818,-0.232419\n" + + "Failure!\n").replace("\n", System.lineSeparator()); + test("/broken7000bytes.pbf", expected); + } + + private static void test(String fileName, String expected) throws Exception { + try (InputStream input = ReadFileTest.class.getResourceAsStream(fileName); StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter)) { BlockReaderAdapter brad = new TestBinaryParser(printWriter); @@ -522,10 +824,15 @@ protected void parse(HeaderBlock header) { writer.println("Got header block."); } + @Override public void complete() { writer.println("Complete!"); } + @Override + public void failure(Exception e) { + writer.println("Failure!"); + } } } From f3a6b65273a461f157d5dee8acad41dc8454cc1f Mon Sep 17 00:00:00 2001 From: Wojciech Strzalka Date: Wed, 11 Jun 2025 11:25:50 +0200 Subject: [PATCH 2/2] Add handling of unexpected end of data stream --- resources/broken7000bytes.pbf | Bin 12595 -> 7000 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/broken7000bytes.pbf b/resources/broken7000bytes.pbf index e06e17daadfcd8f230de553ef417e52575dfc80d..4b461f9045e24941800f1f605f2608fe0aee1548 100644 GIT binary patch literal 7000 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 literal 12595 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