Skip to content

Commit

Permalink
Add logging to RfcUriParser
Browse files Browse the repository at this point in the history
  • Loading branch information
rstoyanchev committed Oct 7, 2024
1 parent ef0a21e commit d6fcad9
Showing 1 changed file with 83 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

import java.util.Set;

import org.springframework.lang.Contract;
import org.apache.commons.logging.Log;

import org.springframework.core.log.LogDelegateFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
* Parser for URI's based on the syntax in RFC 3986.
* Parser for URI's based on RFC 3986 syntax.
*
* @author Rossen Stoyanchev
* @since 6.2
Expand All @@ -32,6 +34,8 @@
*/
abstract class RfcUriParser {

private static final Log logger = LogDelegateFactory.getHiddenLog(RfcUriParser.class);


/**
* Parse the given URI string.
Expand All @@ -44,19 +48,20 @@ public static UriRecord parse(String uri) {
}


@Contract("false, _ -> fail")
private static void verify(boolean expression, String message) {
private static void verify(boolean expression, InternalParser parser, String message) {
if (!expression) {
fail(message);
fail(parser, message);
}
}

public static void verifyIsHexDigit(char c, String message) {
verify((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'), message);
private static void verifyIsHexDigit(char c, InternalParser parser, String message) {
verify((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9'), parser, message);
}

@Contract("_ -> fail")
private static void fail(String message) {
private static void fail(InternalParser parser, String message) {
if (logger.isTraceEnabled()) {
logger.trace(InvalidUrlException.class.getSimpleName() + ": \"" + message + "\" " + parser);
}
throw new InvalidUrlException(message);
}

Expand Down Expand Up @@ -202,7 +207,7 @@ public void handleNext(InternalParser parser, char c, int i) {
parser.captureUser().componentIndex(i + 1);
break;
case '[':
verify(parser.isAtStartOfComponent(), "Bad authority");
verify(parser.isAtStartOfComponent(), parser, "Bad authority");
parser.advanceTo(IPV6);
break;
case '%':
Expand All @@ -212,7 +217,7 @@ public void handleNext(InternalParser parser, char c, int i) {
boolean isAllowed = (parser.processCurlyBrackets(c) ||
parser.countDownPercentEncodingInHost(c) ||
HierarchicalUriComponents.Type.URI.isUnreservedOrSubDelimiter(c));
verify(isAllowed, "Bad authority");
verify(isAllowed, parser, "Bad authority");
}
}

Expand Down Expand Up @@ -242,13 +247,13 @@ public void handleNext(InternalParser parser, char c, int i) {
case ':':
break;
default:
verifyIsHexDigit(c, "Bad authority");
verifyIsHexDigit(c, parser, "Bad authority");
}
}

@Override
public void handleEnd(InternalParser parser) {
verify(parser.hasHost(), "Bad authority"); // no closing ']'
verify(parser.hasHost(), parser, "Bad authority"); // no closing ']'
}
},

Expand All @@ -257,7 +262,7 @@ public void handleEnd(InternalParser parser) {
@Override
public void handleNext(InternalParser parser, char c, int i) {
if (c == '@') {
verify(!parser.hasUser(), "Bad authority");
verify(!parser.hasUser(), parser, "Bad authority");
parser.switchPortForFullPassword().advanceTo(HOST, i + 1);
}
else if (c == '/') {
Expand All @@ -274,7 +279,7 @@ else if (HierarchicalUriComponents.Type.URI.isUnreservedOrSubDelimiter(c) || c =
parser.switchPortForPassword().advanceTo(HOST);
return;
}
fail("Bad authority");
fail(parser, "Bad authority");
}
}

Expand Down Expand Up @@ -342,7 +347,7 @@ public void handleEnd(InternalParser parser) {

@Override
public void handleNext(InternalParser parser, char c, int i) {
fail("Bad character '*'");
fail(parser, "Bad character '*'");
}

@Override
Expand Down Expand Up @@ -422,31 +427,6 @@ public InternalParser(String uri) {
this.uri = uri;
}

/**
* Parse the input string and return a {@link UriRecord} with the results.
*/
public UriRecord parse() {
Assert.isTrue(this.state == State.START && this.index == 0, "Internal Error");

while (hasNext()) {
this.state.handleNext(this, charAtIndex(), this.index);
this.index++;
}

this.state.handleEnd(this);

return new UriRecord(this.scheme, this.isOpaque,
this.user, this.host, this.port, this.path, this.query, this.fragment);
}

public boolean hasNext() {
return (this.index < this.uri.length());
}

public char charAtIndex() {
return this.uri.charAt(this.index);
}

// Check internal state

public boolean hasScheme() {
Expand All @@ -469,15 +449,43 @@ public boolean isAtStartOfComponent() {
return (this.index == this.componentIndex);
}

// Top-level parse loop, iterate over chars and delegate to states

public UriRecord parse() {
Assert.isTrue(this.state == State.START && this.index == 0, "Internal Error");

while (hasNext()) {
this.state.handleNext(this, charAtIndex(), this.index);
this.index++;
}

this.state.handleEnd(this);

return new UriRecord(this.scheme, this.isOpaque,
this.user, this.host, this.port, this.path, this.query, this.fragment);
}

public boolean hasNext() {
return (this.index < this.uri.length());
}

public char charAtIndex() {
return this.uri.charAt(this.index);
}

// Transitions and index updates

public void advanceTo(State state) {
if (logger.isTraceEnabled()) {
logger.trace(this.state + " -> " + state + ", " +
"index=" + this.index + ", componentIndex=" + this.componentIndex);
}
this.state = state;
}

public void advanceTo(State state, int componentIndex) {
this.state = state;
this.componentIndex = componentIndex;
advanceTo(state);
}

public InternalParser componentIndex(int componentIndex) {
Expand All @@ -498,19 +506,19 @@ public InternalParser resolveIfOpaque() {
}

public InternalParser captureScheme() {
this.scheme = captureComponent().toLowerCase();
this.scheme = captureComponent("scheme").toLowerCase();
return this;
}

public InternalParser captureUser() {
this.inPassword = false;
this.user = captureComponent();
this.user = captureComponent("user");
return this;
}

public InternalParser captureHost() {
verify(this.remainingPercentEncodedChars == 0 && !this.inPassword, "Bad authority");
this.host = captureComponent();
verify(this.remainingPercentEncodedChars == 0 && !this.inPassword, this, "Bad authority");
this.host = captureComponent("host");
return this;
}

Expand All @@ -522,29 +530,32 @@ public InternalParser captureHostIfNotEmpty() {
}

public InternalParser capturePort() {
verify(this.openCurlyBracketCount == 0, "Bad authority");
this.port = captureComponent();
verify(this.openCurlyBracketCount == 0, this, "Bad authority");
this.port = captureComponent("port");
return this;
}

public InternalParser capturePath() {
this.path = captureComponent();
this.path = captureComponent("path");
return this;
}

public InternalParser captureQuery() {
this.query = captureComponent();
this.query = captureComponent("query");
return this;
}

public void captureFragmentIfNotEmpty() {
if (this.index > this.componentIndex + 1) {
this.fragment = captureComponent();
this.fragment = captureComponent("fragment");
}
}

public InternalParser switchPortForFullPassword() {
this.user = this.host + ":" + captureComponent();
if (logger.isTraceEnabled()) {
logger.trace("Switching from host/port to user=" + this.user);
}
return this;
}

Expand All @@ -553,16 +564,27 @@ public InternalParser switchPortForPassword() {
if (this.host != null) {
this.componentIndex = (this.componentIndex - this.host.length() - 1);
this.host = null;
if (logger.isTraceEnabled()) {
logger.trace("Switching from host/port to username/password");
}
}
return this;
}

private String captureComponent(String logPrefix) {
String value = captureComponent();
if (logger.isTraceEnabled()) {
logger.trace(logPrefix + " set to '" + value + "'");
}
return value;
}

private String captureComponent() {
return this.uri.substring(this.componentIndex, this.index);
}

public InternalParser markPercentEncoding() {
verify(this.remainingPercentEncodedChars == 0, "Bad encoding");
verify(this.remainingPercentEncodedChars == 0, this, "Bad encoding");
this.remainingPercentEncodedChars = 2;
this.inUtf16Sequence = false;
return this;
Expand All @@ -578,7 +600,7 @@ public boolean countDownPercentEncodingInHost(char c) {
return false;
}
this.remainingPercentEncodedChars--;
verifyIsHexDigit(c, "Bad authority");
verifyIsHexDigit(c, this, "Bad authority");
return true;
}

Expand All @@ -595,7 +617,7 @@ public boolean countDownPercentEncodingInPath(char c) {
return true;
}
this.remainingPercentEncodedChars--;
verifyIsHexDigit(c, "Bad path");
verifyIsHexDigit(c, this, "Bad path");
this.inUtf16Sequence &= (this.remainingPercentEncodedChars > 0);
return true;
}
Expand All @@ -615,6 +637,13 @@ else if (c == '}') {
return (this.openCurlyBracketCount > 0);
}

@Override
public String toString() {
return "[State=" + this.state + ", index=" + this.index + ", componentIndex=" + this.componentIndex +
", uri='" + this.uri + "', scheme='" + this.scheme + "', user='" + this.user +
"', host='" + this.host + "', path='" + this.path + "', port='" + this.port +
"', query='" + this.query + "', fragment='" + this.fragment + "']";
}
}

}

0 comments on commit d6fcad9

Please sign in to comment.