Package com.alibaba.citrus.service.requestcontext.rewrite.impl

Source Code of com.alibaba.citrus.service.requestcontext.rewrite.impl.RewriteRequestContextImpl$RewriteSubstitutionContextImpl

/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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 com.alibaba.citrus.service.requestcontext.rewrite.impl;

import static com.alibaba.citrus.service.requestcontext.rewrite.impl.RewriteUtil.*;
import static com.alibaba.citrus.service.requestcontext.util.RequestContextUtil.*;
import static com.alibaba.citrus.util.ArrayUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.BasicConstant.*;
import static com.alibaba.citrus.util.ObjectUtil.*;
import static com.alibaba.citrus.util.ServletUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;

import java.io.IOException;
import java.util.regex.MatchResult;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.citrus.service.requestcontext.RequestContext;
import com.alibaba.citrus.service.requestcontext.parser.ParameterParser;
import com.alibaba.citrus.service.requestcontext.parser.ParserRequestContext;
import com.alibaba.citrus.service.requestcontext.rewrite.RewriteRequestContext;
import com.alibaba.citrus.service.requestcontext.rewrite.RewriteSubstitutionContext;
import com.alibaba.citrus.service.requestcontext.rewrite.RewriteSubstitutionHandler;
import com.alibaba.citrus.service.requestcontext.support.AbstractRequestContextWrapper;
import com.alibaba.citrus.service.requestcontext.support.AbstractRequestWrapper;
import com.alibaba.citrus.util.FileUtil;
import com.alibaba.citrus.util.ServletUtil;
import com.alibaba.citrus.util.StringEscapeUtil;
import com.alibaba.citrus.util.regex.MatchResultSubstitution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** 重写URL及参数的request context,类似于apache的mod_rewrite模块。 */
public class RewriteRequestContextImpl extends AbstractRequestContextWrapper implements RewriteRequestContext {
    private final static Logger log                 = LoggerFactory.getLogger(RewriteRequestContext.class);
    public static final  String SERVER_SCHEME_HTTP  = "http";
    public static final  String SERVER_SCHEME_HTTPS = "https";
    public static final  int    SERVER_PORT_HTTP    = 80;
    public static final  int    SERVER_PORT_HTTPS   = 443;
    private final RewriteRule[]        rules;
    private       ParserRequestContext parserRequestContext;
    private       HttpServletRequest   wrappedRequest;

    /**
     * 包装一个<code>RequestContext</code>对象。
     *
     * @param wrappedContext 被包装的<code>RequestContext</code>
     * @param rewriteConfig  rewrite的配置文件信息
     */
    public RewriteRequestContextImpl(RequestContext wrappedContext, RewriteRule[] rules) {
        super(wrappedContext);

        this.rules = defaultIfEmptyArray(rules, null);

        // 取得parser request context,以便修改参数
        this.parserRequestContext = assertNotNull(findRequestContext(wrappedContext, ParserRequestContext.class),
                                                  "Could not find ParserRequestContext in request context chain");

        // 保存上一层的request对象,以便取得原来的servletPath、pathInfo之类的信息
        this.wrappedRequest = wrappedContext.getRequest();
    }

    /** 开始一个请求。 */
    @Override
    public void prepare() {
        if (rules == null) {
            return;
        }

        // 取得servletPath+pathInfo,忽略contextPath
        String originalPath = wrappedRequest.getServletPath()
                              + defaultIfNull(wrappedRequest.getPathInfo(), EMPTY_STRING);
        String path = originalPath;
        boolean parameterSubstituted = false;

        if (log.isDebugEnabled()) {
            log.debug("Starting rewrite engine: path=\"{}\"", StringEscapeUtil.escapeJava(path));
        }

        // 开始匹配
        int redirectCode = 0;

        for (RewriteRule rule : rules) {
            MatchResult ruleMatchResult = rule.match(path);
            MatchResult conditionMatchResult = null;
            RewriteSubstitution subs = rule.getSubstitution();

            // 如果匹配,则查看conditions
            if (ruleMatchResult != null) {
                conditionMatchResult = rule.matchConditions(ruleMatchResult, wrappedRequest);
            }

            // 如果C标志被指定,则除非匹配,否则不去判断余下的规则
            boolean chainRule = subs.getFlags().hasC();

            if (conditionMatchResult == null) {
                if (chainRule) {
                    break;
                } else {
                    continue;
                }
            }

            // 用rule和condition的匹配结果来替换变量
            MatchResultSubstitution resultSubs = getMatchResultSubstitution(ruleMatchResult, conditionMatchResult);

            // 替换path
            log.debug("Rule conditions have been satisfied, starting substitution to uri");

            path = subs.substitute(path, resultSubs);

            if (!isFullURL(path)) {
                path = FileUtil.normalizeAbsolutePath(path);
            }

            // 处理parameters
            parameterSubstituted |= subs.substituteParameters(parserRequestContext.getParameters(), resultSubs);

            // post substitution处理
            path = firePostSubstitutionEvent(rule, path, parserRequestContext, resultSubs);

            // 查看重定向标志
            redirectCode = subs.getFlags().getRedirectCode();

            // 如果L标志被指定,则立即结束
            boolean lastRule = subs.getFlags().hasL();

            if (lastRule) {
                break;
            }
        }

        // 如果path被改变了,则替换request或重定向
        if (!isEquals(originalPath, path)) {
            // 如果是重定向,则组合出新的URL
            if (redirectCode > 0) {
                StringBuffer uri = new StringBuffer();
                HttpServletRequest request = getRequest();

                if (!isFullURL(path)) {
                    uri.append(request.getScheme()).append("://").append(request.getServerName());

                    boolean isDefaultPort = false;

                    // http和80
                    isDefaultPort |= SERVER_SCHEME_HTTP.equals(request.getScheme())
                                     && request.getServerPort() == SERVER_PORT_HTTP;

                    // https和443
                    isDefaultPort |= SERVER_SCHEME_HTTPS.equals(request.getScheme())
                                     && request.getServerPort() == SERVER_PORT_HTTPS;

                    if (!isDefaultPort) {
                        uri.append(":");
                        uri.append(request.getServerPort());
                    }

                    uri.append(request.getContextPath());
                }

                uri.append(path);

                String queryString = parserRequestContext.getParameters().toQueryString();

                if (!isEmpty(queryString)) {
                    uri.append("?").append(queryString);
                }

                String uriLocation = uri.toString();

                try {
                    if (redirectCode == HttpServletResponse.SC_MOVED_TEMPORARILY) {
                        getResponse().sendRedirect(uriLocation);
                    } else {
                        getResponse().setHeader("Location", uriLocation);
                        getResponse().setStatus(redirectCode);
                    }
                } catch (IOException e) {
                    log.warn("Redirect to location \"" + uriLocation + "\" failed", e);
                }
            } else {
                RequestWrapper requestWrapper = new RequestWrapper(wrappedRequest);

                requestWrapper.setPath(path);

                setRequest(requestWrapper);
            }
        } else {
            if (!parameterSubstituted) {
                log.trace("No rewrite substitution happend!");
            }
        }
    }

    private String firePostSubstitutionEvent(RewriteRule rule, String path, ParserRequestContext parser,
                                             MatchResultSubstitution resultSubs) {
        for (Object handler : rule.handlers()) {
            RewriteSubstitutionContext context = null;

            if (handler instanceof RewriteSubstitutionHandler) {
                if (context == null) {
                    context = new RewriteSubstitutionContextImpl(path, parser, resultSubs);
                }

                if (log.isTraceEnabled()) {
                    log.trace("Processing post-substitution event for \"{}\" with handler: {}",
                              StringEscapeUtil.escapeJava(path), handler);
                }

                ((RewriteSubstitutionHandler) handler).postSubstitution(context);

                // path可以被改变
                String newPath = context.getPath();

                if (newPath != null && !isEquals(path, newPath)) {
                    if (log.isDebugEnabled()) {
                        log.debug("Rewriting \"{}\" to \"{}\"", StringEscapeUtil.escapeJava(path),
                                  StringEscapeUtil.escapeJava(newPath));
                    }
                }

                path = newPath;
            }
        }

        return path;
    }

    /** 实现<code>RewriteSubstitutionContext</code>。 */
    private class RewriteSubstitutionContextImpl implements RewriteSubstitutionContext {
        private String                  path;
        private ParserRequestContext    parser;
        private MatchResultSubstitution resultSubs;

        public RewriteSubstitutionContextImpl(String path, ParserRequestContext parser,
                                              MatchResultSubstitution resultSubs) {
            this.path = path;
            this.parser = parser;
            this.resultSubs = resultSubs;
        }

        public String getPath() {
            return path;
        }

        public void setPath(String path) {
            this.path = path;
        }

        public ParserRequestContext getParserRequestContext() {
            return parser;
        }

        public ParameterParser getParameters() {
            return parser.getParameters();
        }

        public MatchResultSubstitution getMatchResultSubstitution() {
            return resultSubs;
        }
    }

    /** 包装request。 */
    private class RequestWrapper extends AbstractRequestWrapper {
        private       String  path;
        private final boolean prefixMapping;
        private final String  originalServletPath;

        public RequestWrapper(HttpServletRequest request) {
            super(RewriteRequestContextImpl.this, request);

            // Servlet mapping有两种匹配方式:前缀匹配和后缀匹配。
            // 对于前缀匹配,例如:/turbine/aaa/bbb,servlet path为/turbine,path info为/aaa/bbb
            // 对于后缀匹配,例如:/aaa/bbb.html,servlet path为/aaa/bbb.html,path info为null
            this.prefixMapping = ServletUtil.isPrefixServletMapping(request);
            this.originalServletPath = request.getServletPath();
        }

        public void setPath(String path) {
            this.path = trimToNull(path);
        }

        @Override
        public String getServletPath() {
            if (path == null) {
                return super.getServletPath();
            } else {
                if (prefixMapping) {
                    if (startsWithPath(originalServletPath, path)) {
                        return originalServletPath; // 保持原有的servletPath
                    } else {
                        return "";
                    }
                } else {
                    return path;
                }
            }
        }

        @Override
        public String getPathInfo() {
            if (path == null) {
                return super.getPathInfo();
            } else {
                if (prefixMapping) {
                    if (startsWithPath(originalServletPath, path)) {
                        return path.substring(originalServletPath.length()); // 除去servletPath后剩下的部分
                    } else {
                        return path;
                    }
                } else {
                    return null;
                }
            }
        }
    }
}
TOP

Related Classes of com.alibaba.citrus.service.requestcontext.rewrite.impl.RewriteRequestContextImpl$RewriteSubstitutionContextImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.