1 22 23 24 package org.jboss.web.rewrite; 25 26 import java.io.BufferedReader ; 27 import java.io.File ; 28 import java.io.FileInputStream ; 29 import java.io.IOException ; 30 import java.io.InputStream ; 31 import java.io.InputStreamReader ; 32 import java.io.StringReader ; 33 import java.util.ArrayList ; 34 import java.util.Hashtable ; 35 import java.util.Iterator ; 36 import java.util.Map ; 37 import java.util.StringTokenizer ; 38 39 import javax.servlet.ServletException ; 40 import javax.servlet.http.Cookie ; 41 import javax.servlet.http.HttpServletResponse ; 42 43 import org.apache.catalina.Container; 44 import org.apache.catalina.Context; 45 import org.apache.catalina.Engine; 46 import org.apache.catalina.Host; 47 import org.apache.catalina.Lifecycle; 48 import org.apache.catalina.LifecycleException; 49 import org.apache.catalina.LifecycleListener; 50 import org.apache.catalina.connector.Request; 51 import org.apache.catalina.connector.Response; 52 import org.apache.catalina.util.LifecycleSupport; 53 import org.apache.catalina.valves.ValveBase; 54 import org.apache.tomcat.util.buf.CharChunk; 55 import org.apache.tomcat.util.buf.MessageBytes; 56 import org.apache.tomcat.util.net.URL; 57 58 public class RewriteValve extends ValveBase 59 implements Lifecycle { 60 61 64 protected LifecycleSupport lifecycle = new LifecycleSupport(this); 65 66 67 70 protected RewriteRule[] rules = null; 71 72 73 76 protected ThreadLocal invoked = new ThreadLocal (); 77 78 79 84 protected String resourcePath = "rewrite.properties"; 85 86 87 90 protected boolean context = false; 91 92 93 96 protected Map maps = new Hashtable (); 97 98 99 public void addLifecycleListener(LifecycleListener listener) { 100 lifecycle.addLifecycleListener(listener); 101 } 102 103 public LifecycleListener[] findLifecycleListeners() { 104 return lifecycle.findLifecycleListeners(); 105 } 106 107 public void removeLifecycleListener(LifecycleListener listener) { 108 lifecycle.removeLifecycleListener(listener); 109 } 110 111 public void start() throws LifecycleException { 112 113 InputStream is = null; 114 115 if (getContainer() instanceof Context) { 117 context = true; 118 is = ((Context) getContainer()).getServletContext() 119 .getResourceAsStream("/WEB-INF/" + resourcePath); 120 if ((is == null) && (container.getLogger().isInfoEnabled())) { 121 container.getLogger().info("No configuration resource found: /WEB-INF/" + resourcePath); 122 } 123 } else { 124 String resourceName = getHostConfigPath(resourcePath); 125 File file = new File (getConfigBase(), resourceName); 126 try { 127 if (!file.exists()) { 128 if (resourceName != null) { 129 is = getClass().getClassLoader() 131 .getResourceAsStream(resourceName); 132 if (is != null && container.getLogger().isDebugEnabled()) { 133 container.getLogger().debug("Read configuration from CL at " + resourceName); 134 } 135 } 136 } else { 137 if (container.getLogger().isDebugEnabled()) { 138 container.getLogger().debug("Read configuration from " + file.getAbsolutePath()); 139 } 140 is = new FileInputStream (file); 141 } 142 if ((is == null) && (container.getLogger().isInfoEnabled())) { 143 container.getLogger().info("No configuration resource found: " + resourceName + 144 " in " + getConfigBase().getAbsolutePath() + " or in the classloader"); 145 } 146 } catch (Exception e) { 147 container.getLogger().error("Error opening configuration", e); 148 } 149 } 150 151 if (is == null) { 152 return; 154 } 155 156 BufferedReader reader = new BufferedReader (new InputStreamReader (is)); 157 158 try { 159 parse(reader); 160 } finally { 161 try { 162 reader.close(); 163 } catch (IOException e) { 164 container.getLogger().error("Error closing configuration", e); 165 } 166 try { 167 if (is != null) { 168 is.close(); 169 } 170 } catch (IOException e) { 171 container.getLogger().error("Error closing configuration", e); 172 } 173 } 174 175 } 176 177 public void setConfiguration(String configuration) 178 throws Exception { 179 maps.clear(); 180 parse(new BufferedReader (new StringReader (configuration))); 181 } 182 183 public String getConfiguration() { 184 StringBuffer buffer = new StringBuffer (); 185 for (int i = 0; i < rules.length; i++) { 187 for (int j = 0; j < rules[i].getConditions().length; j++) { 188 buffer.append(rules[i].getConditions()[j].toString()).append("\r\n"); 189 } 190 buffer.append(rules[i].toString()).append("\r\n").append("\r\n"); 191 } 192 return buffer.toString(); 193 } 194 195 protected void parse(BufferedReader reader) throws LifecycleException { 196 ArrayList rules = new ArrayList (); 197 ArrayList conditions = new ArrayList (); 198 while (true) { 199 try { 200 String line = reader.readLine(); 201 if (line == null) { 202 break; 203 } 204 Object result = parse(line); 205 if (result instanceof RewriteRule) { 206 RewriteRule rule = (RewriteRule) result; 207 if (container.getLogger().isDebugEnabled()) { 208 container.getLogger().debug("Add rule with pattern " + rule.getPatternString() 209 + " and substitution " + rule.getSubstitutionString()); 210 } 211 for (int i = 0; i < conditions.size(); i++) { 212 if (container.getLogger().isDebugEnabled()) { 213 RewriteCond cond = (RewriteCond) conditions.get(i); 214 container.getLogger().debug("Add condition " + cond.getCondPattern() 215 + " test " + cond.getTestString() + " to rule with pattern " 216 + rule.getPatternString() + " and substitution " 217 + rule.getSubstitutionString()); 218 } 219 rule.addCondition((RewriteCond) conditions.get(i)); 220 } 221 conditions.clear(); 222 rules.add(rule); 223 } else if (result instanceof RewriteCond) { 224 conditions.add(result); 225 } else if (result instanceof Object []) { 226 String mapName = (String ) ((Object []) result)[0]; 227 RewriteMap map = (RewriteMap) ((Object []) result)[1]; 228 maps.put(mapName, map); 229 if (map instanceof Lifecycle) { 230 ((Lifecycle) map).start(); 231 } 232 } 233 } catch (IOException e) { 234 container.getLogger().error("Error reading configuration", e); 235 } 236 } 237 this.rules = (RewriteRule[]) rules.toArray(new RewriteRule[0]); 238 239 for (int i = 0; i < this.rules.length; i++) { 241 this.rules[i].parse(maps); 242 } 243 } 244 245 public void stop() throws LifecycleException { 246 Iterator values = maps.values().iterator(); 247 while (values.hasNext()) { 248 RewriteMap map = (RewriteMap) values.next(); 249 if (map instanceof Lifecycle) { 250 ((Lifecycle) map).stop(); 251 } 252 } 253 maps.clear(); 254 rules = null; 255 } 256 257 258 public void invoke(Request request, Response response) 259 throws IOException , ServletException { 260 261 if (rules == null || rules.length == 0) { 262 getNext().invoke(request, response); 263 return; 264 } 265 266 if (invoked.get() == Boolean.TRUE) { 267 getNext().invoke(request, response); 268 invoked.set(null); 269 return; 270 } 271 272 TomcatResolver resolver = new TomcatResolver(request); 273 274 invoked.set(Boolean.TRUE); 275 276 MessageBytes urlMB = context ? request.getRequestPathMB() : request.getDecodedRequestURIMB(); 279 urlMB.toChars(); 280 CharSequence url = urlMB.getCharChunk(); 281 CharSequence host = request.getServerName(); 282 boolean rewritten = false; 283 boolean done = false; 284 for (int i = 0; i < rules.length; i++) { 285 CharSequence test = (rules[i].isHost()) ? host : url; 286 CharSequence newtest = rules[i].evaluate(test, resolver); 287 if (newtest != null && !test.equals(newtest.toString())) { 288 if (container.getLogger().isDebugEnabled()) { 289 container.getLogger().debug("Rewrote " + test + " as " + newtest 290 + " with rule pattern " + rules[i].getPatternString()); 291 } 292 if (rules[i].isHost()) { 293 host = newtest; 294 } else { 295 url = newtest; 296 } 297 rewritten = true; 298 } 299 300 302 if (rules[i].isForbidden() && newtest != null) { 304 response.sendError(HttpServletResponse.SC_FORBIDDEN); 305 done = true; 306 break; 307 } 308 if (rules[i].isGone() && newtest != null) { 310 response.sendError(HttpServletResponse.SC_GONE); 311 done = true; 312 break; 313 } 314 if (rules[i].isRedirect() && newtest != null) { 316 String queryString = request.getQueryString(); 318 StringBuffer urlString = new StringBuffer (url); 319 if (queryString != null && queryString.length() > 0) { 320 int index = urlString.indexOf("?"); 321 if (index != -1) { 322 if (rules[i].isQsappend()) { 324 urlString.append('&'); 325 urlString.append(queryString); 326 } 327 else if (index == urlString.length() - 1) { 330 urlString.deleteCharAt(index); 331 } 332 } else { 333 urlString.append('?'); 334 urlString.append(queryString); 335 } 336 } 337 if (context && urlString.charAt(0) == '/' && !hasScheme(urlString)) { 342 urlString.insert(0, request.getContext().getEncodedPath()); 343 } 344 response.sendRedirect(urlString.toString()); 345 done = true; 346 break; 347 } 348 349 351 if (rules[i].isCookie() && newtest != null) { 353 response.addCookie(new Cookie (rules[i].getCookieName(), 354 rules[i].getCookieValue())); 355 } 357 if (rules[i].isEnv() && newtest != null) { 359 System.setProperty(rules[i].getEnvName(), rules[i].getEnvValue()); 360 } 361 if (rules[i].isType() && newtest != null) { 364 request.setContentType(rules[i].getTypeValue()); 365 } 366 if (rules[i].isQsappend() && newtest != null) { 368 String queryString = request.getQueryString(); 369 String urlString = url.toString(); 370 if (urlString.indexOf('?') != -1 && queryString != null) { 371 url = urlString + "&" + queryString; 372 } 373 } 374 375 377 if (rules[i].isChain() && newtest == null) { 379 for (int j = i; j < rules.length; j++) { 380 if (!rules[j].isChain()) { 381 i = j; 382 break; 383 } 384 } 385 continue; 386 } 387 if (rules[i].isLast() && newtest != null) { 389 break; 390 } 391 if (rules[i].isNext() && newtest != null) { 393 i = 0; 394 continue; 395 } 396 if (newtest != null) { 398 i += rules[i].getSkip(); 399 } 400 401 } 402 403 if (rewritten) { 404 if (!done) { 405 String urlString = url.toString(); 407 String queryString = null; 408 int queryIndex = urlString.indexOf('?'); 409 if (queryIndex != -1) { 410 queryString = urlString.substring(queryIndex+1); 411 urlString = urlString.substring(0, queryIndex); 412 } 413 CharChunk chunk = request.getCoyoteRequest().requestURI().getCharChunk(); 415 chunk.recycle(); 416 if (context) { 417 chunk.append(request.getContextPath()); 418 } 419 chunk.append(urlString); 420 request.getCoyoteRequest().requestURI().toChars(); 421 if (queryString != null) { 423 chunk = request.getCoyoteRequest().queryString().getCharChunk(); 424 chunk.recycle(); 425 chunk.append(queryString); 426 request.getCoyoteRequest().queryString().toChars(); 427 } 428 if (!host.equals(request.getServerName())) { 430 chunk = request.getCoyoteRequest().serverName().getCharChunk(); 431 chunk.recycle(); 432 chunk.append(host.toString()); 433 request.getCoyoteRequest().serverName().toChars(); 434 } 435 request.getMappingData().recycle(); 436 try { 438 request.getConnector().getProtocolHandler().getAdapter().service 439 (request.getCoyoteRequest(), response.getCoyoteResponse()); 440 } catch (Exception e) { 441 } 443 } 444 } else { 445 getNext().invoke(request, response); 446 } 447 448 invoked.set(null); 449 450 } 451 452 453 456 protected File getConfigBase() { 457 File configBase = 458 new File (System.getProperty("catalina.base"), "conf"); 459 if (!configBase.exists()) { 460 return null; 461 } else { 462 return configBase; 463 } 464 } 465 466 467 474 protected String getHostConfigPath(String resourceName) { 475 StringBuffer result = new StringBuffer (); 476 Container container = getContainer(); 477 Container host = null; 478 Container engine = null; 479 while (container != null) { 480 if (container instanceof Host) 481 host = container; 482 if (container instanceof Engine) 483 engine = container; 484 container = container.getParent(); 485 } 486 if (engine != null) { 487 result.append(engine.getName()).append('/'); 488 } 489 if (host != null) { 490 result.append(host.getName()).append('/'); 491 } 492 result.append(resourceName); 493 return result.toString(); 494 } 495 496 497 506 public static Object parse(String line) { 507 StringTokenizer tokenizer = new StringTokenizer (line); 508 if (tokenizer.hasMoreTokens()) { 509 String token = tokenizer.nextToken(); 510 if (token.equals("RewriteCond")) { 511 RewriteCond condition = new RewriteCond(); 513 if (tokenizer.countTokens() < 2) { 514 throw new IllegalArgumentException ("Ivalid line: " + line); 515 } 516 condition.setTestString(tokenizer.nextToken()); 517 condition.setCondPattern(tokenizer.nextToken()); 518 if (tokenizer.hasMoreTokens()) { 519 String flags = tokenizer.nextToken(); 520 if (flags.startsWith("[") && flags.endsWith("]")) { 521 flags = flags.substring(1, flags.length() - 1); 522 } 523 StringTokenizer flagsTokenizer = new StringTokenizer (flags, ","); 524 while (flagsTokenizer.hasMoreElements()) { 525 parseCondFlag(line, condition, flagsTokenizer.nextToken()); 526 } 527 } 528 return condition; 529 } else if (token.equals("RewriteRule")) { 530 RewriteRule rule = new RewriteRule(); 532 if (tokenizer.countTokens() < 2) { 533 throw new IllegalArgumentException ("Ivalid line: " + line); 534 } 535 rule.setPatternString(tokenizer.nextToken()); 536 rule.setSubstitutionString(tokenizer.nextToken()); 537 if (tokenizer.hasMoreTokens()) { 538 String flags = tokenizer.nextToken(); 539 if (flags.startsWith("[") && flags.endsWith("]")) { 540 flags = flags.substring(1, flags.length() - 1); 541 } 542 StringTokenizer flagsTokenizer = new StringTokenizer (flags, ","); 543 while (flagsTokenizer.hasMoreElements()) { 544 parseRuleFlag(line, rule, flagsTokenizer.nextToken()); 545 } 546 } 547 return rule; 548 } else if (token.equals("RewriteMap")) { 549 if (tokenizer.countTokens() < 2) { 551 throw new IllegalArgumentException ("Ivalid line: " + line); 552 } 553 String name = tokenizer.nextToken(); 554 String rewriteMapClassName = tokenizer.nextToken(); 555 RewriteMap map = null; 556 try { 557 map = (RewriteMap) (Class.forName(rewriteMapClassName).newInstance()); 558 } catch (Exception e) { 559 throw new IllegalArgumentException ("Ivalid map className: " + line); 560 } 561 if (tokenizer.hasMoreTokens()) { 562 map.setParameters(tokenizer.nextToken()); 563 } 564 Object [] result = new Object [2]; 565 result[0] = name; 566 result[1] = map; 567 return result; 568 } else if (token.startsWith("#")) { 569 } else { 571 throw new IllegalArgumentException ("Invalid line: " + line); 572 } 573 } 574 return null; 575 } 576 577 578 584 protected static void parseCondFlag(String line, RewriteCond condition, String flag) { 585 if (flag.equals("NC") || flag.equals("nocase")) { 586 condition.setNocase(true); 587 } else if (flag.equals("OR") || flag.equals("ornext")) { 588 condition.setOrnext(true); 589 } else { 590 throw new IllegalArgumentException ("Ivalid flag in: " + line + " flags: " + flag); 591 } 592 } 593 594 595 601 protected static void parseRuleFlag(String line, RewriteRule rule, String flag) { 602 if (flag.equals("chain") || flag.equals("C")) { 603 rule.setChain(true); 604 } else if (flag.startsWith("cookie=") || flag.startsWith("C=")) { 605 rule.setCookie(true); 606 if (flag.startsWith("cookie")) { 607 flag = flag.substring("cookie=".length()); 608 } else if (flag.startsWith("C=")) { 609 flag = flag.substring("C=".length()); 610 } 611 int pos = flag.indexOf(':'); 612 if (pos == -1 || (pos + 1) == flag.length()) { 613 throw new IllegalArgumentException ("Ivalid flag in: " + line); 614 } 615 rule.setCookieName(flag.substring(0, pos)); 616 rule.setCookieValue(flag.substring(pos + 1)); 617 } else if (flag.startsWith("env=") || flag.startsWith("E=")) { 618 rule.setEnv(true); 619 if (flag.startsWith("env=")) { 620 flag = flag.substring("env=".length()); 621 } else if (flag.startsWith("E=")) { 622 flag = flag.substring("E=".length()); 623 } 624 int pos = flag.indexOf(':'); 625 if (pos == -1 || (pos + 1) == flag.length()) { 626 throw new IllegalArgumentException ("Ivalid flag in: " + line); 627 } 628 rule.setEnvName(flag.substring(0, pos)); 629 rule.setEnvValue(flag.substring(pos + 1)); 630 } else if (flag.startsWith("forbidden") || flag.startsWith("F")) { 631 rule.setForbidden(true); 632 } else if (flag.startsWith("gone") || flag.startsWith("G")) { 633 rule.setGone(true); 634 } else if (flag.startsWith("host") || flag.startsWith("H")) { 635 rule.setHost(true); 636 } else if (flag.startsWith("last") || flag.startsWith("L")) { 637 rule.setLast(true); 638 } else if (flag.startsWith("next") || flag.startsWith("N")) { 639 rule.setNext(true); 640 } else if (flag.startsWith("nocase") || flag.startsWith("NC")) { 641 rule.setNocase(true); 642 } else if (flag.startsWith("noescape") || flag.startsWith("NE")) { 643 rule.setNoescape(true); 644 } else if (flag.startsWith("proxy") || flag.startsWith("P")) { 645 } else if (flag.startsWith("qsappend") || flag.startsWith("QSA")) { 649 rule.setQsappend(true); 650 } else if (flag.startsWith("redirect") || flag.startsWith("R")) { 651 if (flag.startsWith("redirect=")) { 652 flag = flag.substring("redirect=".length()); 653 rule.setRedirect(true); 654 rule.setRedirectCode(Integer.parseInt(flag)); 655 } else if (flag.startsWith("R=")) { 656 flag = flag.substring("R=".length()); 657 rule.setRedirect(true); 658 rule.setRedirectCode(Integer.parseInt(flag)); 659 } else { 660 rule.setRedirect(true); 661 rule.setRedirectCode(HttpServletResponse.SC_FOUND); 662 } 663 } else if (flag.startsWith("skip") || flag.startsWith("S")) { 664 if (flag.startsWith("skip=")) { 665 flag = flag.substring("skip=".length()); 666 } else if (flag.startsWith("S=")) { 667 flag = flag.substring("S=".length()); 668 } 669 rule.setSkip(Integer.parseInt(flag)); 670 } else if (flag.startsWith("type") || flag.startsWith("T")) { 671 if (flag.startsWith("type=")) { 672 flag = flag.substring("type=".length()); 673 } else if (flag.startsWith("T=")) { 674 flag = flag.substring("T=".length()); 675 } 676 rule.setType(true); 677 rule.setTypeValue(flag); 678 } else { 679 throw new IllegalArgumentException ("Invalid flag in: " + line + " flag: " + flag); 680 } 681 } 682 683 684 687 protected static boolean hasScheme(StringBuffer uri) { 688 int len = uri.length(); 689 for(int i=0; i < len ; i++) { 690 char c = uri.charAt(i); 691 if(c == ':') { 692 return i > 0; 693 } else if(!URL.isSchemeChar(c)) { 694 return false; 695 } 696 } 697 return false; 698 } 699 700 } 701 | Popular Tags |