1c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath/* 2c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Copyright (C) 2011 The Android Open Source Project 3c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 4c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); 5c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * you may not use this file except in compliance with the License. 6c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * You may obtain a copy of the License at 7c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 8c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 9c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * 10c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Unless required by applicable law or agreed to in writing, software 11c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, 12c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * See the License for the specific language governing permissions and 14c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * limitations under the License. 15c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 16c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 17c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathpackage libcore.net.http; 18c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 19c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.ArrayList; 20c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathimport java.util.List; 21c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 22c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamathfinal class HeaderParser { 23c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 24c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public interface CacheControlHandler { 25c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath void handle(String directive, String parameter); 26c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 27c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 28c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 29c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Parse a comma-separated list of cache control header values. 30c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 31c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public static void parseCacheControl(String value, CacheControlHandler handler) { 32c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int pos = 0; 33c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath while (pos < value.length()) { 34c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int tokenStart = pos; 35c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipUntil(value, pos, "=,"); 36c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String directive = value.substring(tokenStart, pos).trim(); 37c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 38c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (pos == value.length() || value.charAt(pos) == ',') { 39c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos++; // consume ',' (if necessary) 40c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath handler.handle(directive, null); 41c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath continue; 42c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 43c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 44c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos++; // consume '=' 45c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipWhitespace(value, pos); 46c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 47c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String parameter; 48c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 49c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // quoted string 50c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (pos < value.length() && value.charAt(pos) == '\"') { 51c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos++; // consume '"' open quote 52c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int parameterStart = pos; 53c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipUntil(value, pos, "\""); 54c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath parameter = value.substring(parameterStart, pos); 55c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos++; // consume '"' close quote (if necessary) 56c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 57c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // unquoted string 58c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } else { 59c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int parameterStart = pos; 60c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipUntil(value, pos, ","); 61c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath parameter = value.substring(parameterStart, pos).trim(); 62c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 63c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 64c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath handler.handle(directive, parameter); 65c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 66c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 67c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 68c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 69c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Parse RFC 2617 challenges. This API is only interested in the scheme 70c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * name and realm. 71c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 72c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public static List<Challenge> parseChallenges( 73c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath RawHeaders responseHeaders, String challengeHeader) { 74c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /* 75c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * auth-scheme = token 76c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * auth-param = token "=" ( token | quoted-string ) 77c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * challenge = auth-scheme 1*SP 1#auth-param 78c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * realm = "realm" "=" realm-value 79c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * realm-value = quoted-string 80c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 81c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath List<Challenge> result = new ArrayList<Challenge>(); 82c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath for (int h = 0; h < responseHeaders.length(); h++) { 83c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!challengeHeader.equalsIgnoreCase(responseHeaders.getFieldName(h))) { 84c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath continue; 85c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 86c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String value = responseHeaders.getValue(h); 87c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int pos = 0; 88c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath while (pos < value.length()) { 89c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int tokenStart = pos; 90c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipUntil(value, pos, " "); 91c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 92c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String scheme = value.substring(tokenStart, pos).trim(); 93c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipWhitespace(value, pos); 94c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 95c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // TODO: This currently only handles schemes with a 'realm' parameter; 96c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // It needs to be fixed to handle any scheme and any parameters 97c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath // http://code.google.com/p/android/issues/detail?id=11140 98c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 99c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (!value.regionMatches(pos, "realm=\"", 0, "realm=\"".length())) { 100c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath break; // unexpected challenge parameter; give up 101c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 102c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 103c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos += "realm=\"".length(); 104c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath int realmStart = pos; 105c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipUntil(value, pos, "\""); 106c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath String realm = value.substring(realmStart, pos); 107c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos++; // consume '"' close quote 108c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipUntil(value, pos, ","); 109c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos++; // consume ',' comma 110c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath pos = skipWhitespace(value, pos); 111c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath result.add(new Challenge(scheme, realm)); 112c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 113c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 114c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return result; 115c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 116c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 117c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 118c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Returns the next index in {@code input} at or after {@code pos} that 119c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * contains a character from {@code characters}. Returns the input length if 120c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * none of the requested characters can be found. 121c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 122c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static int skipUntil(String input, int pos, String characters) { 123c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath for (; pos < input.length(); pos++) { 124c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (characters.indexOf(input.charAt(pos)) != -1) { 125c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath break; 126c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 127c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 128c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return pos; 129c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 130c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 131c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 132c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Returns the next non-whitespace character in {@code input} that is white 133c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * space. Result is undefined if input contains newline characters. 134c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 135c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private static int skipWhitespace(String input, int pos) { 136c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath for (; pos < input.length(); pos++) { 137c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath char c = input.charAt(pos); 138c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (c != ' ' && c != '\t') { 139c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath break; 140c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 141c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 142c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return pos; 143c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 144c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 145c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath /** 146c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * Returns {@code value} as a positive integer, or 0 if it is negative, or 147c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath * -1 if it cannot be parsed. 148c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath */ 149c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath public static int parseSeconds(String value) { 150c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath try { 151c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath long seconds = Long.parseLong(value); 152c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath if (seconds > Integer.MAX_VALUE) { 153c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return Integer.MAX_VALUE; 154c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } else if (seconds < 0) { 155c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return 0; 156c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } else { 157c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return (int) seconds; 158c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 159c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } catch (NumberFormatException e) { 160c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath return -1; 161c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 162c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 163c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath 164c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath private HeaderParser() { 165c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath } 166c3f6f16bd4a2338e88275641b9f2f56e816ca377Narayan Kamath} 167