| OLD | NEW |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.net; | 5 package org.chromium.net; |
| 6 | 6 |
| 7 import android.os.ConditionVariable; | 7 import android.os.ConditionVariable; |
| 8 import android.test.suitebuilder.annotation.SmallTest; | 8 import android.test.suitebuilder.annotation.SmallTest; |
| 9 | 9 |
| 10 import org.chromium.base.test.util.Feature; | 10 import org.chromium.base.test.util.Feature; |
| 11 import org.chromium.net.CronetTestBase.OnlyRunNativeCronet; | 11 import org.chromium.net.CronetTestBase.OnlyRunNativeCronet; |
| 12 import org.chromium.net.impl.ChromiumUrlRequestFactory; | |
| 13 import org.chromium.net.impl.CronetUrlRequestContext; | 12 import org.chromium.net.impl.CronetUrlRequestContext; |
| 14 import org.json.JSONException; | 13 import org.json.JSONException; |
| 15 import org.json.JSONObject; | 14 import org.json.JSONObject; |
| 16 | 15 |
| 17 import java.io.BufferedReader; | 16 import java.io.BufferedReader; |
| 18 import java.io.FileReader; | 17 import java.io.FileReader; |
| 19 import java.io.IOException; | 18 import java.io.IOException; |
| 20 import java.util.ArrayList; | 19 import java.util.ArrayList; |
| 21 import java.util.Arrays; | 20 import java.util.Arrays; |
| 22 import java.util.HashMap; | |
| 23 import java.util.List; | 21 import java.util.List; |
| 24 import java.util.Map; | |
| 25 | 22 |
| 26 /** | 23 /** |
| 27 * Tests Sdch support. | 24 * Tests Sdch support. |
| 28 */ | 25 */ |
| 29 public class SdchTest extends CronetTestBase { | 26 public class SdchTest extends CronetTestBase { |
| 30 private CronetTestFramework mTestFramework; | 27 private CronetTestFramework mTestFramework; |
| 31 | 28 |
| 32 private enum Sdch { | 29 private enum Sdch { |
| 33 ENABLED, | 30 ENABLED, |
| 34 DISABLED, | 31 DISABLED, |
| 35 } | 32 } |
| 36 | 33 |
| 37 private enum Api { | |
| 38 LEGACY, | |
| 39 ASYNC, | |
| 40 } | |
| 41 | |
| 42 @SuppressWarnings("deprecation") | 34 @SuppressWarnings("deprecation") |
| 43 private void setUp(Sdch setting, Api api) throws JSONException { | 35 private void setUp(Sdch setting) throws JSONException { |
| 44 List<String> commandLineArgs = new ArrayList<String>(); | 36 List<String> commandLineArgs = new ArrayList<String>(); |
| 45 commandLineArgs.add(CronetTestFramework.CACHE_KEY); | 37 commandLineArgs.add(CronetTestFramework.CACHE_KEY); |
| 46 commandLineArgs.add(CronetTestFramework.CACHE_DISK); | 38 commandLineArgs.add(CronetTestFramework.CACHE_DISK); |
| 47 if (setting == Sdch.ENABLED) { | 39 if (setting == Sdch.ENABLED) { |
| 48 commandLineArgs.add(CronetTestFramework.SDCH_KEY); | 40 commandLineArgs.add(CronetTestFramework.SDCH_KEY); |
| 49 commandLineArgs.add(CronetTestFramework.SDCH_ENABLE); | 41 commandLineArgs.add(CronetTestFramework.SDCH_ENABLE); |
| 50 } | 42 } |
| 51 | 43 |
| 52 if (api == Api.LEGACY) { | 44 commandLineArgs.add(CronetTestFramework.LIBRARY_INIT_KEY); |
| 53 commandLineArgs.add(CronetTestFramework.LIBRARY_INIT_KEY); | 45 commandLineArgs.add(CronetTestFramework.LibraryInitType.CRONET); |
| 54 commandLineArgs.add(CronetTestFramework.LibraryInitType.LEGACY); | |
| 55 } else { | |
| 56 commandLineArgs.add(CronetTestFramework.LIBRARY_INIT_KEY); | |
| 57 commandLineArgs.add(CronetTestFramework.LibraryInitType.CRONET); | |
| 58 } | |
| 59 | 46 |
| 60 String[] args = new String[commandLineArgs.size()]; | 47 String[] args = new String[commandLineArgs.size()]; |
| 61 CronetEngine.Builder builder = new CronetEngine.Builder(getContext()); | 48 CronetEngine.Builder builder = new CronetEngine.Builder(getContext()); |
| 62 JSONObject hostResolverParams = CronetTestUtil.generateHostResolverRules
(); | 49 JSONObject hostResolverParams = CronetTestUtil.generateHostResolverRules
(); |
| 63 JSONObject experimentalOptions = | 50 JSONObject experimentalOptions = |
| 64 new JSONObject().put("HostResolverRules", hostResolverParams); | 51 new JSONObject().put("HostResolverRules", hostResolverParams); |
| 65 builder.setExperimentalOptions(experimentalOptions.toString()); | 52 builder.setExperimentalOptions(experimentalOptions.toString()); |
| 66 mTestFramework = | 53 mTestFramework = |
| 67 new CronetTestFramework(null, commandLineArgs.toArray(args), get
Context(), builder); | 54 new CronetTestFramework(null, commandLineArgs.toArray(args), get
Context(), builder); |
| 68 // Start NativeTestServer. | 55 // Start NativeTestServer. |
| 69 assertTrue(NativeTestServer.startNativeTestServer(getContext())); | 56 assertTrue(NativeTestServer.startNativeTestServer(getContext())); |
| 70 } | 57 } |
| 71 | 58 |
| 72 @Override | 59 @Override |
| 73 protected void tearDown() throws Exception { | 60 protected void tearDown() throws Exception { |
| 74 NativeTestServer.shutdownNativeTestServer(); | 61 NativeTestServer.shutdownNativeTestServer(); |
| 75 super.tearDown(); | 62 super.tearDown(); |
| 76 } | 63 } |
| 77 | 64 |
| 78 @SmallTest | 65 @SmallTest |
| 79 @Feature({"Cronet"}) | 66 @Feature({"Cronet"}) |
| 80 @SuppressWarnings("deprecation") | |
| 81 @OnlyRunNativeCronet | |
| 82 public void testSdchEnabled_LegacyApi() throws Exception { | |
| 83 setUp(Sdch.ENABLED, Api.LEGACY); | |
| 84 String targetUrl = NativeTestServer.getSdchURL() + "/sdch/test"; | |
| 85 long contextAdapter = | |
| 86 getContextAdapter((ChromiumUrlRequestFactory) mTestFramework.mRe
questFactory); | |
| 87 DictionaryAddedObserver observer = | |
| 88 new DictionaryAddedObserver(targetUrl, contextAdapter, true /**
Legacy Api */); | |
| 89 | |
| 90 // Make a request to /sdch/index which advertises the dictionary. | |
| 91 TestHttpUrlRequestListener listener1 = | |
| 92 startAndWaitForComplete_LegacyApi(mTestFramework.mRequestFactory
, | |
| 93 NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"
); | |
| 94 assertEquals(200, listener1.mHttpStatusCode); | |
| 95 assertEquals("This is an index page.\n", listener1.mResponseAsString); | |
| 96 assertEquals(Arrays.asList("/sdch/dict/LeQxM80O"), | |
| 97 listener1.mResponseHeaders.get("Get-Dictionary")); | |
| 98 | |
| 99 observer.waitForDictionaryAdded(); | |
| 100 | |
| 101 // Make a request to fetch encoded response at /sdch/test. | |
| 102 TestHttpUrlRequestListener listener2 = | |
| 103 startAndWaitForComplete_LegacyApi(mTestFramework.mRequestFactory
, targetUrl); | |
| 104 assertEquals(200, listener2.mHttpStatusCode); | |
| 105 assertEquals("The quick brown fox jumps over the lazy dog.\n", listener2
.mResponseAsString); | |
| 106 } | |
| 107 | |
| 108 @SmallTest | |
| 109 @Feature({"Cronet"}) | |
| 110 @SuppressWarnings("deprecation") | |
| 111 @OnlyRunNativeCronet | |
| 112 public void testSdchDisabled_LegacyApi() throws Exception { | |
| 113 setUp(Sdch.DISABLED, Api.LEGACY); | |
| 114 // Make a request to /sdch/index. | |
| 115 // Since Sdch is not enabled, no dictionary should be advertised. | |
| 116 TestHttpUrlRequestListener listener = | |
| 117 startAndWaitForComplete_LegacyApi(mTestFramework.mRequestFactory
, | |
| 118 NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"
); | |
| 119 assertEquals(200, listener.mHttpStatusCode); | |
| 120 assertEquals("This is an index page.\n", listener.mResponseAsString); | |
| 121 assertEquals(null, listener.mResponseHeaders.get("Get-Dictionary")); | |
| 122 } | |
| 123 | |
| 124 @SmallTest | |
| 125 @Feature({"Cronet"}) | |
| 126 @SuppressWarnings("deprecation") | |
| 127 @OnlyRunNativeCronet | |
| 128 public void testDictionaryNotFound_LegacyApi() throws Exception { | |
| 129 setUp(Sdch.ENABLED, Api.LEGACY); | |
| 130 // Make a request to /sdch/index which advertises a bad dictionary that | |
| 131 // does not exist. | |
| 132 TestHttpUrlRequestListener listener1 = | |
| 133 startAndWaitForComplete_LegacyApi(mTestFramework.mRequestFactory
, | |
| 134 NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound"
); | |
| 135 assertEquals(200, listener1.mHttpStatusCode); | |
| 136 assertEquals("This is an index page.\n", listener1.mResponseAsString); | |
| 137 assertEquals(Arrays.asList("/sdch/dict/NotFound"), | |
| 138 listener1.mResponseHeaders.get("Get-Dictionary")); | |
| 139 | |
| 140 // Make a request to fetch /sdch/test, and make sure request succeeds. | |
| 141 TestHttpUrlRequestListener listener2 = startAndWaitForComplete_LegacyApi
( | |
| 142 mTestFramework.mRequestFactory, NativeTestServer.getSdchURL() +
"/sdch/test"); | |
| 143 assertEquals(200, listener2.mHttpStatusCode); | |
| 144 assertEquals("Sdch is not used.\n", listener2.mResponseAsString); | |
| 145 } | |
| 146 | |
| 147 @SmallTest | |
| 148 @Feature({"Cronet"}) | |
| 149 @OnlyRunNativeCronet | 67 @OnlyRunNativeCronet |
| 150 public void testSdchEnabled() throws Exception { | 68 public void testSdchEnabled() throws Exception { |
| 151 setUp(Sdch.ENABLED, Api.ASYNC); | 69 setUp(Sdch.ENABLED); |
| 152 String targetUrl = NativeTestServer.getSdchURL() + "/sdch/test"; | 70 String targetUrl = NativeTestServer.getSdchURL() + "/sdch/test"; |
| 153 long contextAdapter = | 71 long contextAdapter = |
| 154 getContextAdapter((CronetUrlRequestContext) mTestFramework.mCron
etEngine); | 72 getContextAdapter((CronetUrlRequestContext) mTestFramework.mCron
etEngine); |
| 155 DictionaryAddedObserver observer = | 73 DictionaryAddedObserver observer = new DictionaryAddedObserver(targetUrl
, contextAdapter); |
| 156 new DictionaryAddedObserver(targetUrl, contextAdapter, false /**
Legacy Api */); | |
| 157 | 74 |
| 158 // Make a request to /sdch which advertises the dictionary. | 75 // Make a request to /sdch which advertises the dictionary. |
| 159 TestUrlRequestCallback callback1 = startAndWaitForComplete(mTestFramewor
k.mCronetEngine, | 76 TestUrlRequestCallback callback1 = startAndWaitForComplete(mTestFramewor
k.mCronetEngine, |
| 160 NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); | 77 NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); |
| 161 assertEquals(200, callback1.mResponseInfo.getHttpStatusCode()); | 78 assertEquals(200, callback1.mResponseInfo.getHttpStatusCode()); |
| 162 assertEquals("This is an index page.\n", callback1.mResponseAsString); | 79 assertEquals("This is an index page.\n", callback1.mResponseAsString); |
| 163 assertEquals(Arrays.asList("/sdch/dict/LeQxM80O"), | 80 assertEquals(Arrays.asList("/sdch/dict/LeQxM80O"), |
| 164 callback1.mResponseInfo.getAllHeaders().get("Get-Dictionary")); | 81 callback1.mResponseInfo.getAllHeaders().get("Get-Dictionary")); |
| 165 | 82 |
| 166 observer.waitForDictionaryAdded(); | 83 observer.waitForDictionaryAdded(); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 177 // writes to disk. | 94 // writes to disk. |
| 178 String dictUrl = NativeTestServer.getSdchURL() + "/sdch/dict/LeQxM80O"; | 95 String dictUrl = NativeTestServer.getSdchURL() + "/sdch/dict/LeQxM80O"; |
| 179 assertTrue(fileContainsString("local_prefs.json", dictUrl)); | 96 assertTrue(fileContainsString("local_prefs.json", dictUrl)); |
| 180 | 97 |
| 181 // Test persistence. | 98 // Test persistence. |
| 182 mTestFramework = startCronetTestFrameworkWithUrlAndCronetEngineBuilder( | 99 mTestFramework = startCronetTestFrameworkWithUrlAndCronetEngineBuilder( |
| 183 null, mTestFramework.getCronetEngineBuilder()); | 100 null, mTestFramework.getCronetEngineBuilder()); |
| 184 CronetUrlRequestContext newContext = (CronetUrlRequestContext) mTestFram
ework.mCronetEngine; | 101 CronetUrlRequestContext newContext = (CronetUrlRequestContext) mTestFram
ework.mCronetEngine; |
| 185 long newContextAdapter = getContextAdapter(newContext); | 102 long newContextAdapter = getContextAdapter(newContext); |
| 186 DictionaryAddedObserver newObserver = | 103 DictionaryAddedObserver newObserver = |
| 187 new DictionaryAddedObserver(targetUrl, newContextAdapter, false
/** Legacy Api */); | 104 new DictionaryAddedObserver(targetUrl, newContextAdapter); |
| 188 newObserver.waitForDictionaryAdded(); | 105 newObserver.waitForDictionaryAdded(); |
| 189 | 106 |
| 190 // Make a request to fetch encoded response at /sdch/test. | 107 // Make a request to fetch encoded response at /sdch/test. |
| 191 TestUrlRequestCallback callback3 = startAndWaitForComplete(newContext, t
argetUrl); | 108 TestUrlRequestCallback callback3 = startAndWaitForComplete(newContext, t
argetUrl); |
| 192 assertEquals(200, callback3.mResponseInfo.getHttpStatusCode()); | 109 assertEquals(200, callback3.mResponseInfo.getHttpStatusCode()); |
| 193 assertEquals("The quick brown fox jumps over the lazy dog.\n", callback3
.mResponseAsString); | 110 assertEquals("The quick brown fox jumps over the lazy dog.\n", callback3
.mResponseAsString); |
| 194 } | 111 } |
| 195 | 112 |
| 196 @SmallTest | 113 @SmallTest |
| 197 @Feature({"Cronet"}) | 114 @Feature({"Cronet"}) |
| 198 @OnlyRunNativeCronet | 115 @OnlyRunNativeCronet |
| 199 public void testSdchDisabled() throws Exception { | 116 public void testSdchDisabled() throws Exception { |
| 200 setUp(Sdch.DISABLED, Api.ASYNC); | 117 setUp(Sdch.DISABLED); |
| 201 // Make a request to /sdch. | 118 // Make a request to /sdch. |
| 202 // Since Sdch is not enabled, no dictionary should be advertised. | 119 // Since Sdch is not enabled, no dictionary should be advertised. |
| 203 TestUrlRequestCallback callback = startAndWaitForComplete(mTestFramework
.mCronetEngine, | 120 TestUrlRequestCallback callback = startAndWaitForComplete(mTestFramework
.mCronetEngine, |
| 204 NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); | 121 NativeTestServer.getSdchURL() + "/sdch/index?q=LeQxM80O"); |
| 205 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); | 122 assertEquals(200, callback.mResponseInfo.getHttpStatusCode()); |
| 206 assertEquals("This is an index page.\n", callback.mResponseAsString); | 123 assertEquals("This is an index page.\n", callback.mResponseAsString); |
| 207 assertEquals(null, callback.mResponseInfo.getAllHeaders().get("Get-Dicti
onary")); | 124 assertEquals(null, callback.mResponseInfo.getAllHeaders().get("Get-Dicti
onary")); |
| 208 } | 125 } |
| 209 | 126 |
| 210 @SmallTest | 127 @SmallTest |
| 211 @Feature({"Cronet"}) | 128 @Feature({"Cronet"}) |
| 212 @OnlyRunNativeCronet | 129 @OnlyRunNativeCronet |
| 213 public void testDictionaryNotFound() throws Exception { | 130 public void testDictionaryNotFound() throws Exception { |
| 214 setUp(Sdch.ENABLED, Api.ASYNC); | 131 setUp(Sdch.ENABLED); |
| 215 // Make a request to /sdch/index which advertises a bad dictionary that | 132 // Make a request to /sdch/index which advertises a bad dictionary that |
| 216 // does not exist. | 133 // does not exist. |
| 217 TestUrlRequestCallback callback1 = startAndWaitForComplete(mTestFramewor
k.mCronetEngine, | 134 TestUrlRequestCallback callback1 = startAndWaitForComplete(mTestFramewor
k.mCronetEngine, |
| 218 NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound"); | 135 NativeTestServer.getSdchURL() + "/sdch/index?q=NotFound"); |
| 219 assertEquals(200, callback1.mResponseInfo.getHttpStatusCode()); | 136 assertEquals(200, callback1.mResponseInfo.getHttpStatusCode()); |
| 220 assertEquals("This is an index page.\n", callback1.mResponseAsString); | 137 assertEquals("This is an index page.\n", callback1.mResponseAsString); |
| 221 assertEquals(Arrays.asList("/sdch/dict/NotFound"), | 138 assertEquals(Arrays.asList("/sdch/dict/NotFound"), |
| 222 callback1.mResponseInfo.getAllHeaders().get("Get-Dictionary")); | 139 callback1.mResponseInfo.getAllHeaders().get("Get-Dictionary")); |
| 223 | 140 |
| 224 // Make a request to fetch /sdch/test, and make sure Sdch encoding is no
t used. | 141 // Make a request to fetch /sdch/test, and make sure Sdch encoding is no
t used. |
| 225 TestUrlRequestCallback callback2 = startAndWaitForComplete( | 142 TestUrlRequestCallback callback2 = startAndWaitForComplete( |
| 226 mTestFramework.mCronetEngine, NativeTestServer.getSdchURL() + "/
sdch/test"); | 143 mTestFramework.mCronetEngine, NativeTestServer.getSdchURL() + "/
sdch/test"); |
| 227 assertEquals(200, callback2.mResponseInfo.getHttpStatusCode()); | 144 assertEquals(200, callback2.mResponseInfo.getHttpStatusCode()); |
| 228 assertEquals("Sdch is not used.\n", callback2.mResponseAsString); | 145 assertEquals("Sdch is not used.\n", callback2.mResponseAsString); |
| 229 } | 146 } |
| 230 | 147 |
| 231 private static class DictionaryAddedObserver extends SdchObserver { | 148 private static class DictionaryAddedObserver extends SdchObserver { |
| 232 ConditionVariable mBlock = new ConditionVariable(); | 149 ConditionVariable mBlock = new ConditionVariable(); |
| 233 | 150 |
| 234 public DictionaryAddedObserver(String targetUrl, long contextAdapter, bo
olean isLegacyAPI) { | 151 public DictionaryAddedObserver(String targetUrl, long contextAdapter) { |
| 235 super(targetUrl, contextAdapter, isLegacyAPI); | 152 super(targetUrl, contextAdapter); |
| 236 } | 153 } |
| 237 | 154 |
| 238 @Override | 155 @Override |
| 239 public void onDictionaryAdded() { | 156 public void onDictionaryAdded() { |
| 240 mBlock.open(); | 157 mBlock.open(); |
| 241 } | 158 } |
| 242 | 159 |
| 243 public void waitForDictionaryAdded() { | 160 public void waitForDictionaryAdded() { |
| 244 if (!mDictionaryAlreadyPresent) { | 161 if (!mDictionaryAlreadyPresent) { |
| 245 mBlock.block(); | 162 mBlock.block(); |
| 246 mBlock.close(); | 163 mBlock.close(); |
| 247 } | 164 } |
| 248 } | 165 } |
| 249 } | 166 } |
| 250 | 167 |
| 251 @SuppressWarnings("deprecation") | |
| 252 private long getContextAdapter(ChromiumUrlRequestFactory factory) { | |
| 253 return factory.getRequestContext().getUrlRequestContextAdapter(); | |
| 254 } | |
| 255 | |
| 256 private long getContextAdapter(CronetUrlRequestContext requestContext) { | 168 private long getContextAdapter(CronetUrlRequestContext requestContext) { |
| 257 return requestContext.getUrlRequestContextAdapter(); | 169 return requestContext.getUrlRequestContextAdapter(); |
| 258 } | 170 } |
| 259 | 171 |
| 260 @SuppressWarnings("deprecation") | |
| 261 private TestHttpUrlRequestListener startAndWaitForComplete_LegacyApi( | |
| 262 HttpUrlRequestFactory factory, String url) throws Exception { | |
| 263 Map<String, String> headers = new HashMap<String, String>(); | |
| 264 TestHttpUrlRequestListener listener = new TestHttpUrlRequestListener(); | |
| 265 HttpUrlRequest request = factory.createRequest( | |
| 266 url, HttpUrlRequest.REQUEST_PRIORITY_MEDIUM, headers, listener); | |
| 267 request.start(); | |
| 268 listener.blockForComplete(); | |
| 269 return listener; | |
| 270 } | |
| 271 | |
| 272 private TestUrlRequestCallback startAndWaitForComplete(CronetEngine cronetEn
gine, String url) | 172 private TestUrlRequestCallback startAndWaitForComplete(CronetEngine cronetEn
gine, String url) |
| 273 throws Exception { | 173 throws Exception { |
| 274 TestUrlRequestCallback callback = new TestUrlRequestCallback(); | 174 TestUrlRequestCallback callback = new TestUrlRequestCallback(); |
| 275 UrlRequest.Builder builder = | 175 UrlRequest.Builder builder = |
| 276 new UrlRequest.Builder(url, callback, callback.getExecutor(), cr
onetEngine); | 176 new UrlRequest.Builder(url, callback, callback.getExecutor(), cr
onetEngine); |
| 277 builder.build().start(); | 177 builder.build().start(); |
| 278 callback.blockForDone(); | 178 callback.blockForDone(); |
| 279 return callback; | 179 return callback; |
| 280 } | 180 } |
| 281 | 181 |
| 282 // Returns whether a file contains a particular string. | 182 // Returns whether a file contains a particular string. |
| 283 private boolean fileContainsString(String filename, String content) throws I
OException { | 183 private boolean fileContainsString(String filename, String content) throws I
OException { |
| 284 BufferedReader reader = new BufferedReader(new FileReader( | 184 BufferedReader reader = new BufferedReader(new FileReader( |
| 285 CronetTestFramework.getTestStorage(getContext()) + "/prefs/" + f
ilename)); | 185 CronetTestFramework.getTestStorage(getContext()) + "/prefs/" + f
ilename)); |
| 286 String line; | 186 String line; |
| 287 while ((line = reader.readLine()) != null) { | 187 while ((line = reader.readLine()) != null) { |
| 288 if (line.contains(content)) { | 188 if (line.contains(content)) { |
| 289 reader.close(); | 189 reader.close(); |
| 290 return true; | 190 return true; |
| 291 } | 191 } |
| 292 } | 192 } |
| 293 reader.close(); | 193 reader.close(); |
| 294 return false; | 194 return false; |
| 295 } | 195 } |
| 296 } | 196 } |
| OLD | NEW |