All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.undertow.server.handlers.form.FormEncodedDataDefinition Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed 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.
 */

package io.undertow.server.handlers.form;

import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.UndertowOptions;
import io.undertow.connector.PooledByteBuffer;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.UrlDecodeException;
import io.undertow.util.Headers;
import io.undertow.util.SameThreadExecutor;
import io.undertow.util.URLUtils;
import org.xnio.ChannelListener;
import org.xnio.IoUtils;
import org.xnio.channels.StreamSourceChannel;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Parser definition for form encoded data. This handler takes effect for any request that has a mime type
 * of application/x-www-form-urlencoded. The handler attaches a {@link FormDataParser} to the chain
 * that can parse the underlying form data asynchronously.
 *
 * @author Stuart Douglas
 */
public class FormEncodedDataDefinition implements FormParserFactory.ParserDefinition {

    public static final String APPLICATION_X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded";
    private static boolean parseExceptionLogAsDebug = false;
    private String defaultEncoding = "ISO-8859-1";
    private boolean forceCreation = false; //if the parser should be created even if the correct headers are missing

    public FormEncodedDataDefinition() {
    }

    @Override
    public FormDataParser create(final HttpServerExchange exchange)  {
        String mimeType = exchange.getRequestHeaders().getFirst(Headers.CONTENT_TYPE);
        if (forceCreation || (mimeType != null && mimeType.startsWith(APPLICATION_X_WWW_FORM_URLENCODED))) {

            String charset = defaultEncoding;
            String contentType = exchange.getRequestHeaders().getFirst(Headers.CONTENT_TYPE);
            if (contentType != null) {
                String cs = Headers.extractQuotedValueFromHeader(contentType, "charset");
                if (cs != null) {
                    charset = cs;
                }
            }
            UndertowLogger.REQUEST_LOGGER.tracef("Created form encoded parser for %s", exchange);
            return new FormEncodedDataParser(charset, exchange);
        }
        return null;
    }

    public String getDefaultEncoding() {
        return defaultEncoding;
    }

    public boolean isForceCreation() {
        return forceCreation;
    }

    public FormEncodedDataDefinition setForceCreation(boolean forceCreation) {
        this.forceCreation = forceCreation;
        return this;
    }

    public FormEncodedDataDefinition setDefaultEncoding(final String defaultEncoding) {
        this.defaultEncoding = defaultEncoding;
        return this;
    }

    private static final class FormEncodedDataParser implements ChannelListener, FormDataParser {

        private final HttpServerExchange exchange;
        private final FormData data;
        private final StringBuilder builder = new StringBuilder();
        private String name = null;
        private String charset;
        private HttpHandler handler;

        //0= parsing name
        //1=parsing name, decode required
        //2=parsing value
        //3=parsing value, decode required
        //4=finished
        private int state = 0;

        private FormEncodedDataParser(final String charset, final HttpServerExchange exchange) {
            this.exchange = exchange;
            this.charset = charset;
            this.data = new FormData(exchange.getConnection().getUndertowOptions().get(UndertowOptions.MAX_PARAMETERS, 1000));
        }

        @Override
        public void handleEvent(final StreamSourceChannel channel) {
            try {
                doParse(channel);
                if (state == 4) {
                    exchange.dispatch(SameThreadExecutor.INSTANCE, handler);
                }
            } catch (IOException e) {
                IoUtils.safeClose(channel);
                UndertowLogger.REQUEST_IO_LOGGER.ioExceptionReadingFromChannel(e);
                exchange.endExchange();

            }
        }

        private void doParse(final StreamSourceChannel channel) throws IOException {
            int c = 0;
            final PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
            try {
                final ByteBuffer buffer = pooled.getBuffer();
                do {
                    buffer.clear();
                    c = channel.read(buffer);
                    if (c > 0) {
                        buffer.flip();
                        while (buffer.hasRemaining()) {
                            byte n = buffer.get();
                            switch (state) {
                                case 0: {
                                    if (n == '=') {
                                        name = builder.toString();
                                        builder.setLength(0);
                                        state = 2;
                                    } else if (n == '&') {
                                        addPair(builder.toString(), "");
                                        builder.setLength(0);
                                        state = 0;
                                    } else if (n == '%' || n == '+' || n < 0) {
                                        state = 1;
                                        builder.append((char) (n & 0xFF));
                                    } else {
                                        builder.append((char) n);
                                    }
                                    break;
                                }
                                case 1: {
                                    if (n == '=') {
                                        name = decodeParameterName(builder.toString(), charset, true, new StringBuilder());
                                        builder.setLength(0);
                                        state = 2;
                                    } else if (n == '&') {
                                        addPair(decodeParameterName(builder.toString(), charset, true, new StringBuilder()), "");
                                        builder.setLength(0);
                                        state = 0;
                                    } else {
                                        builder.append((char) (n & 0xFF));
                                    }
                                    break;
                                }
                                case 2: {
                                    if (n == '&') {
                                        addPair(name, builder.toString());
                                        builder.setLength(0);
                                        state = 0;
                                    } else if (n == '%' || n == '+' || n < 0) {
                                        state = 3;
                                        builder.append((char) (n & 0xFF));
                                    } else {
                                        builder.append((char) n);
                                    }
                                    break;
                                }
                                case 3: {
                                    if (n == '&') {
                                        addPair(name, decodeParameterValue(name, builder.toString(), charset, true, new StringBuilder()));
                                        builder.setLength(0);
                                        state = 0;
                                    } else {
                                        builder.append((char) (n & 0xFF));
                                    }
                                    break;
                                }
                            }
                        }
                    }
                } while (c > 0);
                if (c == -1) {
                    if (state == 2) {
                        addPair(name, builder.toString());
                    } else if (state == 3) {
                        addPair(name, decodeParameterValue(name, builder.toString(), charset, true, new StringBuilder()));
                    } else if(builder.length() > 0) {
                        if(state == 1) {
                            addPair(decodeParameterName(builder.toString(), charset, true, new StringBuilder()), "");
                        } else {
                            addPair(builder.toString(), "");
                        }
                    }
                    state = 4;
                    exchange.putAttachment(FORM_DATA, data);
                }
            } finally {
                pooled.close();
            }
        }

        private void addPair(String name, String value) {
            //if there was exception during decoding ignore the parameter [UNDERTOW-1554]
            if(name != null && value != null) {
                data.add(name, value);
            }
        }

        private String decodeParameterValue(String name, String value, String charset, boolean decodeSlash, StringBuilder stringBuilder) {
            String decodedValue = null;

            try {
                decodedValue = URLUtils.decode(value, charset, decodeSlash, stringBuilder);
            } catch (UrlDecodeException e) {
                if (!parseExceptionLogAsDebug) {
                    UndertowLogger.REQUEST_LOGGER.errorf(UndertowMessages.MESSAGES.failedToDecodeParameterValue(name, value, e));
                    parseExceptionLogAsDebug = true;
                } else {
                    UndertowLogger.REQUEST_LOGGER.debugf(UndertowMessages.MESSAGES.failedToDecodeParameterValue(name, value, e));
                }
            }

            return decodedValue;
        }

        private String decodeParameterName(String name, String charset, boolean decodeSlash, StringBuilder stringBuilder) {
            String decodedName = null;

            try {
                decodedName = URLUtils.decode(name, charset, decodeSlash, stringBuilder);
            } catch (UrlDecodeException e) {
                if (!parseExceptionLogAsDebug) {
                    UndertowLogger.REQUEST_LOGGER.errorf(UndertowMessages.MESSAGES.failedToDecodeParameterName(name, e));
                    parseExceptionLogAsDebug = true;
                } else {
                    UndertowLogger.REQUEST_LOGGER.debugf(UndertowMessages.MESSAGES.failedToDecodeParameterName(name, e));
                }
            }

            return decodedName;
        }

        @Override
        public void parse(HttpHandler handler) throws Exception {
            if (exchange.getAttachment(FORM_DATA) != null) {
                handler.handleRequest(exchange);
                return;
            }
            this.handler = handler;
            StreamSourceChannel channel = exchange.getRequestChannel();
            if (channel == null) {
                throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided());
            } else {
                doParse(channel);
                if (state != 4) {
                    channel.getReadSetter().set(this);
                    channel.resumeReads();
                } else {
                    exchange.dispatch(SameThreadExecutor.INSTANCE, handler);
                }
            }
        }

        @Override
        public FormData parseBlocking() throws IOException {
            final FormData existing = exchange.getAttachment(FORM_DATA);
            if (existing != null) {
                return existing;
            }

            StreamSourceChannel channel = exchange.getRequestChannel();
            if (channel == null) {
                throw new IOException(UndertowMessages.MESSAGES.requestChannelAlreadyProvided());
            } else {
                while (state != 4) {
                    doParse(channel);
                    if (state != 4) {
                        channel.awaitReadable();
                    }
                }
            }
            return data;
        }

        @Override
        public void close() throws IOException {

        }

        @Override
        public void setCharacterEncoding(final String encoding) {
            this.charset = encoding;
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy