1781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller/* 2781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * Copyright (C) 2015 Square, Inc. 3781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * 4781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 5781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * you may not use this file except in compliance with the License. 6781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * You may obtain a copy of the License at 7781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * 8781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * http://www.apache.org/licenses/LICENSE-2.0 9781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * 10781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * Unless required by applicable law or agreed to in writing, software 11781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 12781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * See the License for the specific language governing permissions and 14781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * limitations under the License. 15781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller */ 16781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerpackage com.squareup.okhttp; 17781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 18781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport java.util.Collections; 19781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport java.util.LinkedHashMap; 20781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport java.util.Map; 21781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport okio.Buffer; 22781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport okio.ByteString; 23781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 24781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerimport static org.junit.Assert.assertEquals; 25781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 26781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller/** Tests how each code point is encoded and decoded in the context of each URL component. */ 27781c9c216deed11c44044d23841a4ba6a012106eNeil Fullerclass UrlComponentEncodingTester { 28781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller /** 29781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * The default encode set for the ASCII range. The specific rules vary per-component: for example, 30781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * '?' may be identity-encoded in a fragment, but must be percent-encoded in a path. 31781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * 32781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller * See https://url.spec.whatwg.org/#percent-encoded-bytes 33781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller */ 34781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller private static final Map<Integer, Encoding> defaultEncodings; 35781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller static { 36781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller Map<Integer, Encoding> map = new LinkedHashMap<>(); 37781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x0, Encoding.PERCENT); // Null character 38781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x1, Encoding.PERCENT); // Start of Header 39781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x2, Encoding.PERCENT); // Start of Text 40781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x3, Encoding.PERCENT); // End of Text 41781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x4, Encoding.PERCENT); // End of Transmission 42781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x5, Encoding.PERCENT); // Enquiry 43781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x6, Encoding.PERCENT); // Acknowledgment 44781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x7, Encoding.PERCENT); // Bell 45781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '\b', Encoding.PERCENT); // Backspace 46781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '\t', Encoding.SKIP); // Horizontal Tab 47781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '\n', Encoding.SKIP); // Line feed 48781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0xb, Encoding.PERCENT); // Vertical Tab 49781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '\f', Encoding.SKIP); // Form feed 50781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '\r', Encoding.SKIP); // Carriage return 51781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0xe, Encoding.PERCENT); // Shift Out 52781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0xf, Encoding.PERCENT); // Shift In 53781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x10, Encoding.PERCENT); // Data Link Escape 54781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x11, Encoding.PERCENT); // Device Control 1 (oft. XON) 55781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x12, Encoding.PERCENT); // Device Control 2 56781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x13, Encoding.PERCENT); // Device Control 3 (oft. XOFF) 57781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x14, Encoding.PERCENT); // Device Control 4 58781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x15, Encoding.PERCENT); // Negative Acknowledgment 59781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x16, Encoding.PERCENT); // Synchronous idle 60781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x17, Encoding.PERCENT); // End of Transmission Block 61781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x18, Encoding.PERCENT); // Cancel 62781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x19, Encoding.PERCENT); // End of Medium 63781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x1a, Encoding.PERCENT); // Substitute 64781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x1b, Encoding.PERCENT); // Escape 65781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x1c, Encoding.PERCENT); // File Separator 66781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x1d, Encoding.PERCENT); // Group Separator 67781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x1e, Encoding.PERCENT); // Record Separator 68781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x1f, Encoding.PERCENT); // Unit Separator 69781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) ' ', Encoding.PERCENT); 70781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '!', Encoding.IDENTITY); 71781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '"', Encoding.PERCENT); 72781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '#', Encoding.PERCENT); 73781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '$', Encoding.IDENTITY); 74781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '%', Encoding.IDENTITY); 75781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '&', Encoding.IDENTITY); 76781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '\'', Encoding.IDENTITY); 77781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '(', Encoding.IDENTITY); 78781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) ')', Encoding.IDENTITY); 79781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '*', Encoding.IDENTITY); 80781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '+', Encoding.IDENTITY); 81781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) ',', Encoding.IDENTITY); 82781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '-', Encoding.IDENTITY); 83781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '.', Encoding.IDENTITY); 84781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '/', Encoding.IDENTITY); 85781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '0', Encoding.IDENTITY); 86781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '1', Encoding.IDENTITY); 87781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '2', Encoding.IDENTITY); 88781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '3', Encoding.IDENTITY); 89781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '4', Encoding.IDENTITY); 90781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '5', Encoding.IDENTITY); 91781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '6', Encoding.IDENTITY); 92781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '7', Encoding.IDENTITY); 93781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '8', Encoding.IDENTITY); 94781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '9', Encoding.IDENTITY); 95781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) ':', Encoding.IDENTITY); 96781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) ';', Encoding.IDENTITY); 97781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '<', Encoding.PERCENT); 98781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '=', Encoding.IDENTITY); 99781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '>', Encoding.PERCENT); 100781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '?', Encoding.PERCENT); 101781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '@', Encoding.IDENTITY); 102781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'A', Encoding.IDENTITY); 103781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'B', Encoding.IDENTITY); 104781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'C', Encoding.IDENTITY); 105781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'D', Encoding.IDENTITY); 106781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'E', Encoding.IDENTITY); 107781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'F', Encoding.IDENTITY); 108781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'G', Encoding.IDENTITY); 109781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'H', Encoding.IDENTITY); 110781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'I', Encoding.IDENTITY); 111781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'J', Encoding.IDENTITY); 112781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'K', Encoding.IDENTITY); 113781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'L', Encoding.IDENTITY); 114781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'M', Encoding.IDENTITY); 115781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'N', Encoding.IDENTITY); 116781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'O', Encoding.IDENTITY); 117781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'P', Encoding.IDENTITY); 118781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'Q', Encoding.IDENTITY); 119781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'R', Encoding.IDENTITY); 120781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'S', Encoding.IDENTITY); 121781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'T', Encoding.IDENTITY); 122781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'U', Encoding.IDENTITY); 123781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'V', Encoding.IDENTITY); 124781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'W', Encoding.IDENTITY); 125781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'X', Encoding.IDENTITY); 126781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'Y', Encoding.IDENTITY); 127781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'Z', Encoding.IDENTITY); 128781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '[', Encoding.IDENTITY); 129781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '\\', Encoding.IDENTITY); 130781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) ']', Encoding.IDENTITY); 131781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '^', Encoding.IDENTITY); 132781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '_', Encoding.IDENTITY); 133781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '`', Encoding.PERCENT); 134781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'a', Encoding.IDENTITY); 135781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'b', Encoding.IDENTITY); 136781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'c', Encoding.IDENTITY); 137781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'd', Encoding.IDENTITY); 138781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'e', Encoding.IDENTITY); 139781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'f', Encoding.IDENTITY); 140781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'g', Encoding.IDENTITY); 141781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'h', Encoding.IDENTITY); 142781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'i', Encoding.IDENTITY); 143781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'j', Encoding.IDENTITY); 144781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'k', Encoding.IDENTITY); 145781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'l', Encoding.IDENTITY); 146781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'm', Encoding.IDENTITY); 147781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'n', Encoding.IDENTITY); 148781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'o', Encoding.IDENTITY); 149781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'p', Encoding.IDENTITY); 150781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'q', Encoding.IDENTITY); 151781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'r', Encoding.IDENTITY); 152781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 's', Encoding.IDENTITY); 153781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 't', Encoding.IDENTITY); 154781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'u', Encoding.IDENTITY); 155781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'v', Encoding.IDENTITY); 156781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'w', Encoding.IDENTITY); 157781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'x', Encoding.IDENTITY); 158781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'y', Encoding.IDENTITY); 159781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) 'z', Encoding.IDENTITY); 160781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '{', Encoding.IDENTITY); 161781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '|', Encoding.IDENTITY); 162781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '}', Encoding.IDENTITY); 163781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put((int) '~', Encoding.IDENTITY); 164781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller map.put( 0x7f, Encoding.PERCENT); // Delete 165781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller defaultEncodings = Collections.unmodifiableMap(map); 166781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 167781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 168781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller private final Map<Integer, Encoding> encodings; 169781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 170781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public UrlComponentEncodingTester() { 171781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller this.encodings = new LinkedHashMap<>(defaultEncodings); 172781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 173781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 174781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public UrlComponentEncodingTester override(Encoding encoding, int... codePoints) { 175781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller for (int codePoint : codePoints) { 176781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller encodings.put(codePoint, encoding); 177781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 178781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return this; 179781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 180781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 181781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public UrlComponentEncodingTester test(Component component) { 182781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller for (Map.Entry<Integer, Encoding> entry : encodings.entrySet()) { 183781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller if (entry.getValue() == Encoding.SKIP) continue; 184781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 185781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller testParseOriginal(entry.getKey(), entry.getValue(), component); 186781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller testParseAlreadyEncoded(entry.getKey(), entry.getValue(), component); 187781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller testSerialize(entry.getKey(), entry.getValue(), component); 188781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 189781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return this; 190781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 191781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 192781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller private void testParseAlreadyEncoded(int codePoint, Encoding encoding, Component component) { 193781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String encoded = encoding.encode(codePoint); 194781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String urlString = component.urlString(encoded); 195781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller HttpUrl url = HttpUrl.parse(urlString); 196781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller if (!component.decodedValue(url).equals(encoded)) { 197781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(String.format("Encoding %s %#x using %s", component, codePoint, encoding), 198781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller encoded, component.decodedValue(url)); 199781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 200781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 201781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 202781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller private void testParseOriginal(int codePoint, Encoding encoding, Component component) { 203781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String encoded = encoding.encode(codePoint); 204781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller if (encoding != Encoding.PERCENT) return; 205781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String identity = Encoding.IDENTITY.encode(codePoint); 206781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String urlString = component.urlString(identity); 207781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller HttpUrl url = HttpUrl.parse(urlString); 208781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 209781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String s = component.decodedValue(url); 210781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller if (!s.equals(encoded)) { 211781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller assertEquals(String.format("Encoding %s %#02x using %s", component, codePoint, encoding), 212781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller encoded, component.decodedValue(url)); 213781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 214781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 215781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 216781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller private void testSerialize(int codePoint, Encoding encoding, Component component) { 217781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller // TODO. 218781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 219781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 220781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public enum Encoding { 221781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller IDENTITY { 222781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public String encode(int codePoint) { 223781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return new String(new int[] { codePoint }, 0, 1); 224781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 225781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 226781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 227781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller PERCENT { 228781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public String encode(int codePoint) { 229781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller ByteString utf8 = ByteString.encodeUtf8(IDENTITY.encode(codePoint)); 230781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller Buffer percentEncoded = new Buffer(); 231781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller for (int i = 0; i < utf8.size(); i++) { 232781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller percentEncoded.writeUtf8(String.format("%%%02X", utf8.getByte(i) & 0xff)); 233781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 234781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return percentEncoded.readUtf8(); 235781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 236781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 237781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 238781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller SKIP { 239781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public String encode(int codePoint) { 240781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller throw new UnsupportedOperationException(); 241781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 242781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }; 243781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 244781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public abstract String encode(int codePoint); 245781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 246781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 247781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 248781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public enum Component { 249781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller USER { 250781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String urlString(String value) { 251781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return "http://" + value + "@example.com/"; 252781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 253781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String decodedValue(HttpUrl url) { 254781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return url.username(); 255781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 256781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 257781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller PASSWORD { 258781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String urlString(String value) { 259781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return "http://:" + value + "@example.com/"; 260781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 261781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String decodedValue(HttpUrl url) { 262781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return url.password(); 263781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 264781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 265781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller HOST { 266781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String urlString(String value) { 267781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller throw new UnsupportedOperationException("TODO"); 268781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 269781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 270781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String decodedValue(HttpUrl url) { 271781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller throw new UnsupportedOperationException("TODO"); 272781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 273781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 274781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller PORT { 275781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String urlString(String value) { 276781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller throw new UnsupportedOperationException("TODO"); 277781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 278781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 279781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String decodedValue(HttpUrl url) { 280781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller throw new UnsupportedOperationException("TODO"); 281781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 282781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 283781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller PATH { 284781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String urlString(String value) { 285781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return "http://example.com/a" + value + "z/"; 286781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 287781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String decodedValue(HttpUrl url) { 288781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String path = url.path(); 289781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return path.substring(2, path.length() - 2); 290781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 291781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 292781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller QUERY { 293781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String urlString(String value) { 294781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return "http://example.com/?a" + value + "z"; 295781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 296781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 297781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String decodedValue(HttpUrl url) { 298781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String query = url.query(); 299781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return query.substring(1, query.length() - 1); 300781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 301781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }, 302781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller FRAGMENT { 303781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String urlString(String value) { 304781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return "http://example.com/#a" + value + "z"; 305781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 306781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 307781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller @Override public String decodedValue(HttpUrl url) { 308781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller String fragment = url.fragment(); 309781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller return fragment.substring(1, fragment.length() - 1); 310781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 311781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller }; 312781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 313781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public abstract String urlString(String value); 314781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller 315781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller public abstract String decodedValue(HttpUrl url); 316781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller } 317781c9c216deed11c44044d23841a4ba6a012106eNeil Fuller} 318