static int alias_match_servlet()

in modules/proxy/mod_proxy.c [606:796]


static int alias_match_servlet(apr_pool_t *p,
                               const char **urip,
                               const char *alias)
{
    char *map;
    const char *uri = *urip;
    apr_array_header_t *stack;
    int map_pos, uri_pos, alias_pos, first_pos;
    int alias_depth = 0, depth;

    /* Both uri and alias should start with '/' */
    if (uri[0] != '/' || alias[0] != '/') {
        return 0;
    }

    stack = apr_array_make(p, 5, sizeof(int));
    map = apr_palloc(p, strlen(uri) + 1);
    map[0] = '/';
    map[1] = '\0';

    map_pos = uri_pos = alias_pos = first_pos = 1;
    while (uri[uri_pos] != '\0') {
        /* Remove path parameters ;foo=bar/ from any path segment */
        if (uri[uri_pos] == ';') {
            do {
                uri_pos++;
            } while (uri[uri_pos] != '/' && uri[uri_pos] != '\0');
            continue;
        }

        if (map[map_pos - 1] == '/') {
            /* Collapse ///// sequences to / */
            if (uri[uri_pos] == '/') {
                do {
                    uri_pos++;
                } while (uri[uri_pos] == '/');
                continue;
            }

            if (uri[uri_pos] == '.') {
                /* Remove /./ segments */
                if (uri[uri_pos + 1] == '/'
                        || uri[uri_pos + 1] == ';'
                        || uri[uri_pos + 1] == '\0') {
                    uri_pos++;
                    if (uri[uri_pos] == '/') {
                        uri_pos++;
                    }
                    continue;
                }

                /* Remove /xx/../ segments */
                if (uri[uri_pos + 1] == '.'
                    && (uri[uri_pos + 2] == '/'
                        || uri[uri_pos + 2] == ';'
                        || uri[uri_pos + 2] == '\0')) {
                    /* Wind map segment back the previous one */
                    if (map_pos == 1) {
                        /* Above root */
                        return 0;
                    }
                    do {
                        map_pos--;
                    } while (map[map_pos - 1] != '/');
                    map[map_pos] = '\0';

                    /* Wind alias segment back, unless in deeper segment */
                    if (alias_depth == stack->nelts) {
                        if (alias[alias_pos] == '\0') {
                            alias_pos--;
                        }
                        while (alias_pos > 0 && alias[alias_pos] == '/') {
                            alias_pos--;
                        }
                        while (alias_pos > 0 && alias[alias_pos - 1] != '/') {
                            alias_pos--;
                        }
                        AP_DEBUG_ASSERT(alias_pos > 0);
                        alias_depth--;
                    }
                    apr_array_pop(stack);

                    /* Move uri forward to the next segment */
                    uri_pos += 2;
                    if (uri[uri_pos] == '/') {
                        uri_pos++;
                    }
                    first_pos = 0;
                    continue;
                }
            }
            if (first_pos) {
                while (uri[first_pos] == '/') {
                    first_pos++;
                }
            }

            /* New segment */
            APR_ARRAY_PUSH(stack, int) = first_pos ? first_pos : uri_pos;
            if (alias[alias_pos] != '\0') {
                if (alias[alias_pos - 1] != '/') {
                    /* Remain in pair with uri segments */
                    do {
                        alias_pos++;
                    } while (alias[alias_pos - 1] != '/' && alias[alias_pos]);
                }
                while (alias[alias_pos] == '/') {
                    alias_pos++;
                }
                if (alias[alias_pos] != '\0') {
                    alias_depth++;
                }
            }
        }

        if (alias[alias_pos] != '\0') {
            int *match = &APR_ARRAY_IDX(stack, alias_depth - 1, int);
            if (*match) {
                if (alias[alias_pos] != uri[uri_pos]) {
                    /* Current segment does not match */
                    *match = 0;
                }
                else if (alias[alias_pos + 1] == '\0'
                         && alias[alias_pos] != '/') {
                    if (uri[uri_pos + 1] == ';') {
                        /* We'll preserve the parameters of the last
                         * segment if it does not end with '/', so mark
                         * the match as negative for below handling.
                         */
                        *match = -(uri_pos + 1);
                    }
                    else if (uri[uri_pos + 1] != '/'
                             && uri[uri_pos + 1] != '\0') {
                        /* Last segment does not match all the way */
                        *match = 0;
                    }
                }
            }
            /* Don't go past the segment if the uri isn't there yet */
            if (alias[alias_pos] != '/' || uri[uri_pos] == '/') {
                alias_pos++;
            }
        }

        if (uri[uri_pos] == '/') {
            first_pos = uri_pos + 1;
        }
        map[map_pos++] = uri[uri_pos++];
        map[map_pos] = '\0';
    }

    /* Can't reach the end of uri before the end of the alias,
     * for example if uri is "/" and alias is "/examples"
     */
    if (alias[alias_pos] != '\0') {
        return 0;
    }

    /* Check whether each alias segment matched */
    for (depth = 0; depth < alias_depth; ++depth) {
        if (!APR_ARRAY_IDX(stack, depth, int)) {
            return 0;
        }
    }

    /* If alias_depth == stack->nelts we have a full match, i.e.
     * uri == alias so we can return uri_pos as is (the end of uri)
     */
    if (alias_depth < stack->nelts) {
        /* Return the segment following the alias */
        uri_pos = APR_ARRAY_IDX(stack, alias_depth, int);
        if (alias_depth) {
            /* But if the last segment of the alias does not end with '/'
             * and the corresponding segment of the uri has parameters,
             * we want to forward those parameters (see above for the
             * negative pos trick/mark).
             */
            int pos = APR_ARRAY_IDX(stack, alias_depth - 1, int);
            if (pos < 0) {
                uri_pos = -pos;
            }
        }
    }
    /* If the alias lacks a trailing slash, take it from the uri (if any) */
    if (alias[alias_pos - 1] != '/' && uri[uri_pos - 1] == '/') {
        uri_pos--;
    }

    *urip = map;
    return uri_pos;
}