RFC 7231: parse capable of parsing multiple auth challenges

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1687909 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2015-06-27 10:50:53 +00:00
parent fb56b49465
commit 11612ab974
4 changed files with 560 additions and 0 deletions

View File

@ -0,0 +1,83 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.auth;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.annotation.Immutable;
import org.apache.http.util.Args;
/**
* This class represents an authentication challenge consisting of a auth scheme
* and either a single parameter or a list of name / value pairs.
*
* @since 5.0
*/
@Immutable
public final class AuthChallenge {
private final String scheme;
private final String value;
private final List<NameValuePair> params;
public AuthChallenge(final String scheme, final String value, final List<? extends NameValuePair> params) {
super();
Args.notNull(scheme, "Auth scheme");
this.scheme = scheme;
this.value = value;
this.params = params != null ? Collections.unmodifiableList(new ArrayList<>(params)) : null;
}
public String getScheme() {
return scheme;
}
public String getValue() {
return value;
}
public List<NameValuePair> getParams() {
return params;
}
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
buffer.append(scheme).append(" ");
if (value != null) {
buffer.append(value);
} else if (params != null) {
buffer.append(params);
}
return buffer.toString();
}
}

View File

@ -0,0 +1,122 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthChallenge;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.message.ParserCursor;
import org.apache.http.message.TokenParser;
import org.apache.http.util.CharArrayBuffer;
public class AuthChallengeParser {
private final TokenParser tokenParser = TokenParser.INSTANCE;
private final static char BLANK = ' ';
private final static char COMMA_CHAR = ',';
private final static char EQUAL_CHAR = '=';
// IMPORTANT!
// These private static variables must be treated as immutable and never exposed outside this class
private static final BitSet TERMINATORS = TokenParser.INIT_BITSET(BLANK, EQUAL_CHAR, COMMA_CHAR);
private static final BitSet DELIMITER = TokenParser.INIT_BITSET(COMMA_CHAR);
NameValuePair parseTokenOrParameter(final CharArrayBuffer buffer, final ParserCursor cursor) throws ParseException {
tokenParser.skipWhiteSpace(buffer, cursor);
final String token = tokenParser.parseToken(buffer, cursor, TERMINATORS);
if (!cursor.atEnd()) {
if (buffer.charAt(cursor.getPos()) == BLANK) {
tokenParser.skipWhiteSpace(buffer, cursor);
}
if (buffer.charAt(cursor.getPos()) == EQUAL_CHAR) {
cursor.updatePos(cursor.getPos() + 1);
final String value = tokenParser.parseValue(buffer, cursor, DELIMITER);
return new BasicNameValuePair(token, value);
}
}
return new BasicNameValuePair(token, null);
}
public List<AuthChallenge> parse(final CharArrayBuffer buffer, final ParserCursor cursor) throws ParseException {
final List<AuthChallenge> list = new ArrayList<>();
String scheme = null;
final List<NameValuePair> params = new ArrayList<>();
while (!cursor.atEnd()) {
final NameValuePair tokenOrParameter = parseTokenOrParameter(buffer, cursor);
if (tokenOrParameter.getValue() == null && !cursor.atEnd() && buffer.charAt(cursor.getPos()) != COMMA_CHAR) {
if (scheme != null) {
if (params.isEmpty()) {
throw new ParseException("Malformed auth challenge");
}
list.add(createAuthChallenge(scheme, params));
params.clear();
}
scheme = tokenOrParameter.getName();
} else {
params.add(tokenOrParameter);
if (!cursor.atEnd() && buffer.charAt(cursor.getPos()) != COMMA_CHAR) {
scheme = null;
}
}
if (!cursor.atEnd() && buffer.charAt(cursor.getPos()) == COMMA_CHAR) {
cursor.updatePos(cursor.getPos() + 1);
}
}
list.add(createAuthChallenge(scheme, params));
return list;
}
private static AuthChallenge createAuthChallenge(final String scheme, final List<NameValuePair> params) {
if (scheme != null) {
if (params.size() == 1) {
final NameValuePair nvp = params.get(0);
if (nvp.getValue() == null) {
return new AuthChallenge(scheme, nvp.getName(), null);
}
}
return new AuthChallenge(scheme, null, params.size() > 0 ? params : null);
} else {
if (params.size() == 1) {
final NameValuePair nvp = params.get(0);
if (nvp.getValue() == null) {
return new AuthChallenge(nvp.getName(), null, null);
}
}
throw new ParseException("Malformed auth challenge");
}
}
}

View File

@ -0,0 +1,57 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.auth;
import java.util.Arrays;
import org.apache.http.message.BasicNameValuePair;
import org.junit.Assert;
import org.junit.Test;
public class TestAuthChallenge {
@Test
public void testAuthChallengeWithValue() {
final AuthChallenge authChallenge = new AuthChallenge("Basic", "blah", null);
Assert.assertEquals("Basic", authChallenge.getScheme());
Assert.assertEquals("blah", authChallenge.getValue());
Assert.assertEquals(null, authChallenge.getParams());
Assert.assertEquals("Basic blah", authChallenge.toString());
}
@Test
public void testAuthChallengeWithParams() {
final AuthChallenge authChallenge = new AuthChallenge("Basic", null,
Arrays.asList(new BasicNameValuePair("blah", "this"), new BasicNameValuePair("blah", "that")));
Assert.assertEquals("Basic", authChallenge.getScheme());
Assert.assertEquals(null, authChallenge.getValue());
Assert.assertNotNull(authChallenge.getParams());
Assert.assertEquals("Basic [blah=this, blah=that]", authChallenge.toString());
}
}

View File

@ -0,0 +1,298 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.auth;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthChallenge;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.message.ParserCursor;
import org.apache.http.util.CharArrayBuffer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class TestAuthChallengeParser {
private AuthChallengeParser parser;
@Before
public void setUp() throws Exception {
this.parser = new AuthChallengeParser();
}
@Test
public void testParseBasicToken() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("blah");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("blah", nvp.getName());
Assert.assertEquals(null, nvp.getValue());
}
@Test
public void testParseTokenWithBlanks() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append(" blah blah ");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("blah", nvp.getName());
Assert.assertEquals(null, nvp.getValue());
}
@Test
public void testParseTokenDelimited() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("blah,blah");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("blah", nvp.getName());
Assert.assertEquals(null, nvp.getValue());
}
@Test
public void testParseParameterSimple() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("param=blah");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("param", nvp.getName());
Assert.assertEquals("blah", nvp.getValue());
}
@Test
public void testParseParameterDelimited() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("param = blah , ");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("param", nvp.getName());
Assert.assertEquals("blah", nvp.getValue());
}
@Test
public void testParseParameterQuoted() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append(" param = \" blah blah \"");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("param", nvp.getName());
Assert.assertEquals(" blah blah ", nvp.getValue());
}
@Test
public void testParseParameterEscaped() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append(" param = \" blah \\\"blah\\\" \"");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("param", nvp.getName());
Assert.assertEquals(" blah \"blah\" ", nvp.getValue());
}
@Test
public void testParseParameterNoValue() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("param = , ");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor);
Assert.assertNotNull(nvp);
Assert.assertEquals("param", nvp.getName());
Assert.assertEquals("", nvp.getValue());
}
@Test
public void testParseBasicAuthChallenge() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("Basic realm=blah");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final List<AuthChallenge> challenges = parser.parse(buffer, cursor);
Assert.assertNotNull(challenges);
Assert.assertEquals(1, challenges.size());
final AuthChallenge challenge1 = challenges.get(0);
Assert.assertEquals("Basic", challenge1.getScheme());
Assert.assertEquals(null, challenge1.getValue());
final List<NameValuePair> params = challenge1.getParams();
Assert.assertNotNull(params);
Assert.assertEquals(1, params.size());
Assert.assertEquals(new BasicNameValuePair("realm", "blah"), params.get(0));
}
@Test
public void testParseAuthChallengeWithBlanks() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append(" Basic realm = blah ");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final List<AuthChallenge> challenges = parser.parse(buffer, cursor);
Assert.assertNotNull(challenges);
Assert.assertEquals(1, challenges.size());
final AuthChallenge challenge1 = challenges.get(0);
Assert.assertEquals("Basic", challenge1.getScheme());
Assert.assertEquals(null, challenge1.getValue());
final List<NameValuePair> params = challenge1.getParams();
Assert.assertNotNull(params);
Assert.assertEquals(1, params.size());
Assert.assertEquals(new BasicNameValuePair("realm", "blah"), params.get(0));
}
@Test
public void testParseMultipleAuthChallenge() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("This xxxxxxxxxxxxxxxxxxxxxx, " +
"That yyyyyyyyyyyyyyyyyyyyyy ");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final List<AuthChallenge> challenges = parser.parse(buffer, cursor);
Assert.assertNotNull(challenges);
Assert.assertEquals(2, challenges.size());
final AuthChallenge challenge1 = challenges.get(0);
Assert.assertEquals("This", challenge1.getScheme());
Assert.assertEquals("xxxxxxxxxxxxxxxxxxxxxx", challenge1.getValue());
Assert.assertNull(challenge1.getParams());
final AuthChallenge challenge2 = challenges.get(1);
Assert.assertEquals("That", challenge2.getScheme());
Assert.assertEquals("yyyyyyyyyyyyyyyyyyyyyy", challenge2.getValue());
Assert.assertNull(challenge2.getParams());
}
@Test
public void testParseMultipleAuthChallengeWithParams() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("Basic realm=blah, param1 = this, param2=that, " +
"Basic realm=\"\\\"yada\\\"\", this, that=,this-and-that ");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final List<AuthChallenge> challenges = parser.parse(buffer, cursor);
Assert.assertNotNull(challenges);
Assert.assertEquals(2, challenges.size());
final AuthChallenge challenge1 = challenges.get(0);
Assert.assertEquals("Basic", challenge1.getScheme());
Assert.assertEquals(null, challenge1.getValue());
final List<NameValuePair> params1 = challenge1.getParams();
Assert.assertNotNull(params1);
Assert.assertEquals(3, params1.size());
Assert.assertEquals(new BasicNameValuePair("realm", "blah"), params1.get(0));
Assert.assertEquals(new BasicNameValuePair("param1", "this"), params1.get(1));
Assert.assertEquals(new BasicNameValuePair("param2", "that"), params1.get(2));
final AuthChallenge challenge2 = challenges.get(1);
Assert.assertEquals("Basic", challenge2.getScheme());
Assert.assertEquals(null, challenge2.getValue());
final List<NameValuePair> params2 = challenge2.getParams();
Assert.assertNotNull(params2);
Assert.assertEquals(4, params2.size());
Assert.assertEquals(new BasicNameValuePair("realm", "\"yada\""), params2.get(0));
Assert.assertEquals(new BasicNameValuePair("this", null), params2.get(1));
Assert.assertEquals(new BasicNameValuePair("that", ""), params2.get(2));
Assert.assertEquals(new BasicNameValuePair("this-and-that", null), params2.get(3));
}
@Test
public void testParseEmptyAuthChallenge1() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("This");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final List<AuthChallenge> challenges = parser.parse(buffer, cursor);
Assert.assertNotNull(challenges);
Assert.assertEquals(1, challenges.size());
final AuthChallenge challenge1 = challenges.get(0);
Assert.assertEquals("This", challenge1.getScheme());
Assert.assertEquals(null, challenge1.getValue());
Assert.assertNull(challenge1.getParams());
}
@Test(expected = ParseException.class)
public void testParseMalformedAuthChallenge1() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("This , ");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
parser.parse(buffer, cursor);
}
@Test(expected = ParseException.class)
public void testParseMalformedAuthChallenge2() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("This = that");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
parser.parse(buffer, cursor);
}
@Test(expected = ParseException.class)
public void testParseMalformedAuthChallenge3() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("blah blah blah");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
parser.parse(buffer, cursor);
}
@Test
public void testParseValidAuthChallenge1() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("blah blah");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final List<AuthChallenge> challenges = parser.parse(buffer, cursor);
Assert.assertNotNull(challenges);
Assert.assertEquals(1, challenges.size());
final AuthChallenge challenge1 = challenges.get(0);
Assert.assertEquals("blah", challenge1.getScheme());
Assert.assertEquals("blah", challenge1.getValue());
Assert.assertNull(challenge1.getParams());
}
@Test
public void testParseValidAuthChallenge2() throws Exception {
final CharArrayBuffer buffer = new CharArrayBuffer(64);
buffer.append("blah blah, blah");
final ParserCursor cursor = new ParserCursor(0, buffer.length());
final List<AuthChallenge> challenges = parser.parse(buffer, cursor);
Assert.assertNotNull(challenges);
Assert.assertEquals(1, challenges.size());
final AuthChallenge challenge1 = challenges.get(0);
Assert.assertEquals("blah", challenge1.getScheme());
Assert.assertEquals(null, challenge1.getValue());
final List<NameValuePair> params1 = challenge1.getParams();
Assert.assertNotNull(params1);
Assert.assertEquals(2, params1.size());
Assert.assertEquals(new BasicNameValuePair("blah", null), params1.get(0));
Assert.assertEquals(new BasicNameValuePair("blah", null), params1.get(1));
}
}