use of io.undertow.util.BadRequestException in project undertow by undertow-io.
the class AjpReadListener method createSourceConduit.
private StreamSourceConduit createSourceConduit(StreamSourceConduit underlyingConduit, AjpServerResponseConduit responseConduit, final HttpServerExchange exchange) throws BadRequestException {
ReadDataStreamSourceConduit conduit = new ReadDataStreamSourceConduit(underlyingConduit, (AbstractServerConnection) exchange.getConnection());
final HeaderMap requestHeaders = exchange.getRequestHeaders();
HttpString transferEncoding = Headers.IDENTITY;
Long length;
final String teHeader = requestHeaders.getLast(Headers.TRANSFER_ENCODING);
boolean hasTransferEncoding = teHeader != null;
if (hasTransferEncoding) {
transferEncoding = new HttpString(teHeader);
}
final String requestContentLength = requestHeaders.getFirst(Headers.CONTENT_LENGTH);
if (hasTransferEncoding && !transferEncoding.equals(Headers.IDENTITY)) {
// unknown length
length = null;
} else if (requestContentLength != null) {
try {
final long contentLength = Long.parseLong(requestContentLength);
if (contentLength == 0L) {
UndertowLogger.REQUEST_LOGGER.trace("No content, starting next request");
// no content - immediately start the next request, returning an empty stream for this one
Connectors.terminateRequest(httpServerExchange);
return new EmptyStreamSourceConduit(conduit.getReadThread());
} else {
length = contentLength;
}
} catch (NumberFormatException e) {
throw new BadRequestException("Invalid Content-Length header", e);
}
} else {
UndertowLogger.REQUEST_LOGGER.trace("No content length or transfer coding, starting next request");
// no content - immediately start the next request, returning an empty stream for this one
Connectors.terminateRequest(exchange);
return new EmptyStreamSourceConduit(conduit.getReadThread());
}
return new AjpServerRequestConduit(conduit, exchange, responseConduit, length, new ConduitListener<AjpServerRequestConduit>() {
@Override
public void handleEvent(AjpServerRequestConduit channel) {
Connectors.terminateRequest(exchange);
}
});
}
use of io.undertow.util.BadRequestException in project undertow by undertow-io.
the class AjpRequestParser method parseString.
protected StringHolder parseString(ByteBuffer buf, AjpRequestParseState state, StringType type) throws UnsupportedEncodingException, BadRequestException {
boolean containsUrlCharacters = state.containsUrlCharacters;
boolean containsUnencodedUrlCharacters = state.containsUnencodedUrlCharacters;
if (!buf.hasRemaining()) {
return new StringHolder(null, false, false, false);
}
int stringLength = state.stringLength;
if (stringLength == -1) {
int number = buf.get() & 0xFF;
if (buf.hasRemaining()) {
final byte b = buf.get();
stringLength = ((0xFF & number) << 8) + (b & 0xFF);
} else {
state.stringLength = number | STRING_LENGTH_MASK;
return new StringHolder(null, false, false, false);
}
} else if ((stringLength & STRING_LENGTH_MASK) != 0) {
int number = stringLength & ~STRING_LENGTH_MASK;
stringLength = ((0xFF & number) << 8) + (buf.get() & 0xFF);
}
if (type == StringType.HEADER && (stringLength & 0xFF00) != 0) {
state.stringLength = -1;
return new StringHolder(headers(stringLength & 0xFF));
}
if (stringLength == 0xFFFF) {
// OxFFFF means null
state.stringLength = -1;
return new StringHolder(null, true, false, false);
}
int length = state.getCurrentStringLength();
while (length < stringLength) {
if (!buf.hasRemaining()) {
state.stringLength = stringLength;
state.containsUrlCharacters = containsUrlCharacters;
state.containsUnencodedUrlCharacters = containsUnencodedUrlCharacters;
return new StringHolder(null, false, false, false);
}
byte c = buf.get();
if (type == StringType.QUERY_STRING && (c == '+' || c == '%' || c < 0)) {
if (c < 0) {
if (!allowUnescapedCharactersInUrl) {
throw new BadRequestException();
} else {
containsUnencodedUrlCharacters = true;
}
}
containsUrlCharacters = true;
} else if (type == StringType.URL && (c == '%' || c < 0)) {
if (c < 0) {
if (!allowUnescapedCharactersInUrl) {
throw new BadRequestException();
} else {
containsUnencodedUrlCharacters = true;
}
}
containsUrlCharacters = true;
}
state.addStringByte(c);
++length;
}
if (buf.hasRemaining()) {
// null terminator
buf.get();
String value = state.getStringAndClear();
state.stringLength = -1;
state.containsUrlCharacters = false;
state.containsUnencodedUrlCharacters = containsUnencodedUrlCharacters;
return new StringHolder(value, true, containsUrlCharacters, containsUnencodedUrlCharacters);
} else {
state.stringLength = stringLength;
state.containsUrlCharacters = containsUrlCharacters;
state.containsUnencodedUrlCharacters = containsUnencodedUrlCharacters;
return new StringHolder(null, false, false, false);
}
}
use of io.undertow.util.BadRequestException in project undertow by undertow-io.
the class AjpRequestParser method parse.
public void parse(final ByteBuffer buf, final AjpRequestParseState state, final HttpServerExchange exchange) throws IOException, BadRequestException {
if (!buf.hasRemaining()) {
return;
}
switch(state.state) {
case AjpRequestParseState.BEGIN:
{
IntegerHolder result = parse16BitInteger(buf, state);
if (!result.readComplete) {
return;
} else {
if (result.value != 0x1234) {
throw new BadRequestException(UndertowMessages.MESSAGES.wrongMagicNumber(result.value));
}
}
}
case AjpRequestParseState.READING_DATA_SIZE:
{
IntegerHolder result = parse16BitInteger(buf, state);
if (!result.readComplete) {
state.state = AjpRequestParseState.READING_DATA_SIZE;
return;
} else {
state.dataSize = result.value;
}
}
case AjpRequestParseState.READING_PREFIX_CODE:
{
if (!buf.hasRemaining()) {
state.state = AjpRequestParseState.READING_PREFIX_CODE;
return;
} else {
final byte prefix = buf.get();
state.prefix = prefix;
if (prefix != 2) {
state.state = AjpRequestParseState.DONE;
return;
}
}
}
case AjpRequestParseState.READING_METHOD:
{
if (!buf.hasRemaining()) {
state.state = AjpRequestParseState.READING_METHOD;
return;
} else {
int method = buf.get();
if (method > 0 && method < 28) {
exchange.setRequestMethod(HTTP_METHODS[method]);
} else if ((method & 0xFF) != 0xFF) {
throw new BadRequestException("Unknown method type " + method);
}
}
}
case AjpRequestParseState.READING_PROTOCOL:
{
StringHolder result = parseString(buf, state, StringType.OTHER);
if (result.readComplete) {
// TODO: more efficient way of doing this
exchange.setProtocol(HttpString.tryFromString(result.value));
} else {
state.state = AjpRequestParseState.READING_PROTOCOL;
return;
}
}
case AjpRequestParseState.READING_REQUEST_URI:
{
StringHolder result = parseString(buf, state, StringType.URL);
if (result.readComplete) {
int colon = result.value.indexOf(';');
if (colon == -1) {
String res = decode(result.value, result.containsUrlCharacters);
if (result.containsUnencodedCharacters) {
// we decode if the URL was non-compliant, and contained incorrectly encoded characters
// there is not really a 'correct' thing to do in this situation, but this seems the least incorrect
exchange.setRequestURI(res);
} else {
exchange.setRequestURI(result.value);
}
exchange.setRequestPath(res);
exchange.setRelativePath(res);
} else {
final StringBuffer resBuffer = new StringBuffer();
int pathParamParsingIndex = 0;
try {
do {
final String url = result.value.substring(pathParamParsingIndex, colon);
resBuffer.append(decode(url, result.containsUrlCharacters));
pathParamParsingIndex = colon + 1 + URLUtils.parsePathParams(result.value.substring(colon + 1), exchange, encoding, doDecode && result.containsUrlCharacters, maxParameters);
colon = result.value.indexOf(';', pathParamParsingIndex + 1);
} while (pathParamParsingIndex < result.value.length() && colon != -1);
} catch (ParameterLimitException e) {
UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(e);
state.badRequest = true;
}
if (pathParamParsingIndex < result.value.length()) {
final String url = result.value.substring(pathParamParsingIndex);
resBuffer.append(decode(url, result.containsUrlCharacters));
}
final String res = resBuffer.toString();
if (result.containsUnencodedCharacters) {
exchange.setRequestURI(res);
} else {
exchange.setRequestURI(result.value);
}
exchange.setRequestPath(res);
exchange.setRelativePath(res);
}
} else {
state.state = AjpRequestParseState.READING_REQUEST_URI;
return;
}
}
case AjpRequestParseState.READING_REMOTE_ADDR:
{
StringHolder result = parseString(buf, state, StringType.OTHER);
if (result.readComplete) {
state.remoteAddress = result.value;
} else {
state.state = AjpRequestParseState.READING_REMOTE_ADDR;
return;
}
}
case AjpRequestParseState.READING_REMOTE_HOST:
{
StringHolder result = parseString(buf, state, StringType.OTHER);
if (!result.readComplete) {
state.state = AjpRequestParseState.READING_REMOTE_HOST;
return;
}
}
case AjpRequestParseState.READING_SERVER_NAME:
{
StringHolder result = parseString(buf, state, StringType.OTHER);
if (result.readComplete) {
state.serverAddress = result.value;
} else {
state.state = AjpRequestParseState.READING_SERVER_NAME;
return;
}
}
case AjpRequestParseState.READING_SERVER_PORT:
{
IntegerHolder result = parse16BitInteger(buf, state);
if (result.readComplete) {
state.serverPort = result.value;
} else {
state.state = AjpRequestParseState.READING_SERVER_PORT;
return;
}
}
case AjpRequestParseState.READING_IS_SSL:
{
if (!buf.hasRemaining()) {
state.state = AjpRequestParseState.READING_IS_SSL;
return;
} else {
final byte isSsl = buf.get();
if (isSsl != 0) {
exchange.setRequestScheme("https");
} else {
exchange.setRequestScheme("http");
}
}
}
case AjpRequestParseState.READING_NUM_HEADERS:
{
IntegerHolder result = parse16BitInteger(buf, state);
if (!result.readComplete) {
state.state = AjpRequestParseState.READING_NUM_HEADERS;
return;
} else {
state.numHeaders = result.value;
if (state.numHeaders > maxHeaders) {
UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(new BadRequestException(UndertowMessages.MESSAGES.tooManyHeaders(maxHeaders)));
state.badRequest = true;
}
}
}
case AjpRequestParseState.READING_HEADERS:
{
int readHeaders = state.readHeaders;
while (readHeaders < state.numHeaders) {
if (state.currentHeader == null) {
StringHolder result = parseString(buf, state, StringType.HEADER);
if (!result.readComplete) {
state.state = AjpRequestParseState.READING_HEADERS;
state.readHeaders = readHeaders;
return;
}
if (result.header != null) {
state.currentHeader = result.header;
} else {
state.currentHeader = HttpString.tryFromString(result.value);
Connectors.verifyToken(state.currentHeader);
}
}
StringHolder result = parseString(buf, state, StringType.OTHER);
if (!result.readComplete) {
state.state = AjpRequestParseState.READING_HEADERS;
state.readHeaders = readHeaders;
return;
}
if (!state.badRequest) {
exchange.getRequestHeaders().add(state.currentHeader, result.value);
}
state.currentHeader = null;
++readHeaders;
}
}
case AjpRequestParseState.READING_ATTRIBUTES:
{
for (; ; ) {
if (state.currentAttribute == null && state.currentIntegerPart == -1) {
if (!buf.hasRemaining()) {
state.state = AjpRequestParseState.READING_ATTRIBUTES;
return;
}
int val = (0xFF & buf.get());
if (val == 0xFF) {
state.state = AjpRequestParseState.DONE;
return;
} else if (val == 0x0A) {
// we need to read the name. We overload currentIntegerPart to avoid adding another state field
state.currentIntegerPart = 1;
} else {
if (val == 0 || val >= ATTRIBUTES.length) {
// ignore unknown codes for compatibility
continue;
}
state.currentAttribute = ATTRIBUTES[val];
}
}
if (state.currentIntegerPart == 1) {
StringHolder result = parseString(buf, state, StringType.OTHER);
if (!result.readComplete) {
state.state = AjpRequestParseState.READING_ATTRIBUTES;
return;
}
state.currentAttribute = result.value;
state.currentIntegerPart = -1;
}
String result;
boolean decodingAlreadyDone = false;
if (state.currentAttribute.equals(SSL_KEY_SIZE)) {
IntegerHolder resultHolder = parse16BitInteger(buf, state);
if (!resultHolder.readComplete) {
state.state = AjpRequestParseState.READING_ATTRIBUTES;
return;
}
result = Integer.toString(resultHolder.value);
} else {
StringHolder resultHolder = parseString(buf, state, state.currentAttribute.equals(QUERY_STRING) ? StringType.QUERY_STRING : StringType.OTHER);
if (!resultHolder.readComplete) {
state.state = AjpRequestParseState.READING_ATTRIBUTES;
return;
}
if (resultHolder.containsUnencodedCharacters) {
result = decode(resultHolder.value, true);
decodingAlreadyDone = true;
} else {
result = resultHolder.value;
}
}
// query string.
if (state.currentAttribute.equals(QUERY_STRING)) {
String resultAsQueryString = result == null ? "" : result;
exchange.setQueryString(resultAsQueryString);
try {
URLUtils.parseQueryString(resultAsQueryString, exchange, encoding, doDecode && !decodingAlreadyDone, maxParameters);
} catch (ParameterLimitException | IllegalArgumentException e) {
UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(e);
state.badRequest = true;
}
} else if (state.currentAttribute.equals(REMOTE_USER)) {
exchange.putAttachment(ExternalAuthenticationMechanism.EXTERNAL_PRINCIPAL, result);
exchange.putAttachment(HttpServerExchange.REMOTE_USER, result);
} else if (state.currentAttribute.equals(AUTH_TYPE)) {
exchange.putAttachment(ExternalAuthenticationMechanism.EXTERNAL_AUTHENTICATION_TYPE, result);
} else if (state.currentAttribute.equals(STORED_METHOD)) {
HttpString requestMethod = new HttpString(result);
Connectors.verifyToken(requestMethod);
exchange.setRequestMethod(requestMethod);
} else if (state.currentAttribute.equals(AJP_REMOTE_PORT)) {
state.remotePort = Integer.parseInt(result);
} else if (state.currentAttribute.equals(SSL_SESSION)) {
state.sslSessionId = result;
} else if (state.currentAttribute.equals(SSL_CIPHER)) {
state.sslCipher = result;
} else if (state.currentAttribute.equals(SSL_CERT)) {
state.sslCert = result;
} else if (state.currentAttribute.equals(SSL_KEY_SIZE)) {
state.sslKeySize = result;
} else {
// other attributes
if (state.attributes == null) {
state.attributes = new TreeMap<>();
}
if (ATTR_SET.contains(state.currentAttribute)) {
// known attirubtes
state.attributes.put(state.currentAttribute, result);
} else if (allowedRequestAttributesPattern != null) {
// custom allowed attributes
Matcher m = allowedRequestAttributesPattern.matcher(state.currentAttribute);
if (m.matches()) {
state.attributes.put(state.currentAttribute, result);
}
}
}
state.currentAttribute = null;
}
}
}
state.state = AjpRequestParseState.DONE;
}
use of io.undertow.util.BadRequestException in project undertow by undertow-io.
the class HttpRequestParser method handleHeaderValueCacheMiss.
private void handleHeaderValueCacheMiss(ByteBuffer buffer, ParseState state, HttpServerExchange builder, HttpString headerName, CacheMap<HttpString, String> headerValuesCache, StringBuilder stringBuilder) throws BadRequestException {
int parseState = state.parseState;
while (buffer.hasRemaining() && parseState == NORMAL) {
final byte next = buffer.get();
if (next == '\r') {
parseState = BEGIN_LINE_END;
} else if (next == '\n') {
parseState = LINE_END;
} else if (next == ' ' || next == '\t') {
parseState = WHITESPACE;
} else {
stringBuilder.append((char) (next & 0xFF));
}
}
while (buffer.hasRemaining()) {
final byte next = buffer.get();
switch(parseState) {
case NORMAL:
{
if (next == '\r') {
parseState = BEGIN_LINE_END;
} else if (next == '\n') {
parseState = LINE_END;
} else if (next == ' ' || next == '\t') {
parseState = WHITESPACE;
} else {
stringBuilder.append((char) (next & 0xFF));
}
break;
}
case WHITESPACE:
{
if (next == '\r') {
parseState = BEGIN_LINE_END;
} else if (next == '\n') {
parseState = LINE_END;
} else if (next == ' ' || next == '\t') {
} else {
if (stringBuilder.length() > 0) {
stringBuilder.append(' ');
}
stringBuilder.append((char) (next & 0xFF));
parseState = NORMAL;
}
break;
}
case LINE_END:
case BEGIN_LINE_END:
{
if (next == '\n' && parseState == BEGIN_LINE_END) {
parseState = LINE_END;
} else if (next == '\t' || next == ' ') {
// this is a continuation
parseState = WHITESPACE;
} else {
// we have a header
String headerValue = stringBuilder.toString();
if (++state.mapCount > maxHeaders) {
throw new BadRequestException(UndertowMessages.MESSAGES.tooManyHeaders(maxHeaders));
}
// TODO: we need to decode this according to RFC-2047 if we have seen a =? symbol
builder.getRequestHeaders().add(headerName, headerValue);
if (headerValuesCache != null && headerName.length() + headerValue.length() < maxCachedHeaderSize) {
headerValuesCache.put(headerName, headerValue);
}
state.nextHeader = null;
state.leftOver = next;
state.stringBuilder.setLength(0);
if (next == '\r') {
parseState = AWAIT_DATA_END;
} else if (next == '\n') {
state.state = ParseState.PARSE_COMPLETE;
return;
} else {
state.state = ParseState.HEADER;
state.parseState = 0;
return;
}
}
break;
}
case AWAIT_DATA_END:
{
state.state = ParseState.PARSE_COMPLETE;
return;
}
}
}
// we only write to the state if we did not finish parsing
state.parseState = parseState;
}
use of io.undertow.util.BadRequestException in project undertow by undertow-io.
the class AjpReadListener method handleEvent.
public void handleEvent(final StreamSourceChannel channel) {
if (connection.getOriginalSinkConduit().isWriteShutdown() || connection.getOriginalSourceConduit().isReadShutdown()) {
safeClose(connection);
channel.suspendReads();
return;
}
PooledByteBuffer existing = connection.getExtraBytes();
final PooledByteBuffer pooled = existing == null ? connection.getByteBufferPool().allocate() : existing;
final ByteBuffer buffer = pooled.getBuffer();
boolean free = true;
boolean bytesRead = false;
try {
int res;
do {
if (existing == null) {
buffer.clear();
res = channel.read(buffer);
} else {
res = buffer.remaining();
}
if (res == 0) {
if (bytesRead && parseTimeoutUpdater != null) {
parseTimeoutUpdater.failedParse();
}
if (!channel.isReadResumed()) {
channel.getReadSetter().set(this);
channel.resumeReads();
}
return;
}
if (res == -1) {
channel.shutdownReads();
final StreamSinkChannel responseChannel = connection.getChannel().getSinkChannel();
responseChannel.shutdownWrites();
safeClose(connection);
return;
}
bytesRead = true;
// TODO: we need to handle parse errors
if (existing != null) {
existing = null;
connection.setExtraBytes(null);
} else {
buffer.flip();
}
int begin = buffer.remaining();
if (httpServerExchange == null) {
httpServerExchange = new HttpServerExchange(connection, maxEntitySize);
}
parser.parse(buffer, state, httpServerExchange);
read += begin - buffer.remaining();
if (buffer.hasRemaining()) {
free = false;
connection.setExtraBytes(pooled);
}
if (read > maxRequestSize) {
UndertowLogger.REQUEST_LOGGER.requestHeaderWasTooLarge(connection.getPeerAddress(), maxRequestSize);
safeClose(connection);
return;
}
} while (!state.isComplete());
if (parseTimeoutUpdater != null) {
parseTimeoutUpdater.requestStarted();
}
if (state.prefix != AjpRequestParser.FORWARD_REQUEST) {
if (state.prefix == AjpRequestParser.CPING) {
UndertowLogger.REQUEST_LOGGER.debug("Received CPING, sending CPONG");
handleCPing();
} else if (state.prefix == AjpRequestParser.CPONG) {
UndertowLogger.REQUEST_LOGGER.debug("Received CPONG, starting next request");
state = new AjpRequestParseState();
channel.getReadSetter().set(this);
channel.resumeReads();
} else {
UndertowLogger.REQUEST_LOGGER.ignoringAjpRequestWithPrefixCode(state.prefix);
safeClose(connection);
}
return;
}
// we remove ourselves as the read listener from the channel;
// if the http handler doesn't set any then reads will suspend, which is the right thing to do
channel.getReadSetter().set(null);
channel.suspendReads();
final HttpServerExchange httpServerExchange = this.httpServerExchange;
final AjpServerResponseConduit responseConduit = new AjpServerResponseConduit(connection.getChannel().getSinkChannel().getConduit(), connection.getByteBufferPool(), httpServerExchange, new ConduitListener<AjpServerResponseConduit>() {
@Override
public void handleEvent(AjpServerResponseConduit channel) {
Connectors.terminateResponse(httpServerExchange);
}
}, httpServerExchange.getRequestMethod().equals(Methods.HEAD));
connection.getChannel().getSinkChannel().setConduit(responseConduit);
connection.getChannel().getSourceChannel().setConduit(createSourceConduit(connection.getChannel().getSourceChannel().getConduit(), responseConduit, httpServerExchange));
// we need to set the write ready handler. This allows the response conduit to wrap it
responseConduit.setWriteReadyHandler(writeReadyHandler);
connection.setSSLSessionInfo(state.createSslSessionInfo());
httpServerExchange.setSourceAddress(state.createPeerAddress());
httpServerExchange.setDestinationAddress(state.createDestinationAddress());
if (scheme != null) {
httpServerExchange.setRequestScheme(scheme);
}
if (state.attributes != null) {
httpServerExchange.putAttachment(HttpServerExchange.REQUEST_ATTRIBUTES, state.attributes);
}
AjpRequestParseState oldState = state;
state = null;
this.httpServerExchange = null;
httpServerExchange.setPersistent(true);
if (recordRequestStartTime) {
Connectors.setRequestStartTime(httpServerExchange);
}
connection.setCurrentExchange(httpServerExchange);
if (connectorStatistics != null) {
connectorStatistics.setup(httpServerExchange);
}
if (!Connectors.areRequestHeadersValid(httpServerExchange.getRequestHeaders())) {
oldState.badRequest = true;
UndertowLogger.REQUEST_IO_LOGGER.debugf("Invalid AJP request from %s, request contained invalid headers", connection.getPeerAddress());
}
if (oldState.badRequest) {
httpServerExchange.setStatusCode(StatusCodes.BAD_REQUEST);
httpServerExchange.endExchange();
handleBadRequest();
safeClose(connection);
} else {
Connectors.executeRootHandler(connection.getRootHandler(), httpServerExchange);
}
} catch (BadRequestException e) {
UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(e);
handleBadRequest();
safeClose(connection);
} catch (IOException e) {
UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
handleInternalServerError();
safeClose(connection);
} catch (Throwable t) {
UndertowLogger.REQUEST_LOGGER.exceptionProcessingRequest(t);
handleInternalServerError();
safeClose(connection);
} finally {
if (free)
pooled.close();
}
}
Aggregations