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