1 16 17 package org.apache.jk.common; 18 19 import java.io.File ; 20 import java.io.FileOutputStream ; 21 import java.io.IOException ; 22 import java.io.CharConversionException ; 23 import java.net.InetAddress ; 24 import java.util.Properties ; 25 26 import org.apache.coyote.Request; 27 import org.apache.coyote.RequestInfo; 28 import org.apache.coyote.Response; 29 import org.apache.coyote.Constants; 30 import org.apache.jk.core.JkHandler; 31 import org.apache.jk.core.Msg; 32 import org.apache.jk.core.MsgContext; 33 import org.apache.jk.core.WorkerEnv; 34 import org.apache.jk.core.JkChannel; 35 import org.apache.tomcat.util.buf.ByteChunk; 36 import org.apache.tomcat.util.buf.CharChunk; 37 import org.apache.tomcat.util.buf.HexUtils; 38 import org.apache.tomcat.util.buf.MessageBytes; 39 import org.apache.tomcat.util.http.MimeHeaders; 40 import org.apache.tomcat.util.net.SSLSupport; 41 import org.apache.tomcat.util.threads.ThreadWithAttributes; 42 43 63 public class HandlerRequest extends JkHandler 64 { 65 private static org.apache.commons.logging.Log log= 66 org.apache.commons.logging.LogFactory.getLog( HandlerRequest.class ); 67 68 70 public static final byte JK_AJP13_FORWARD_REQUEST = 2; 72 public static final byte JK_AJP13_SHUTDOWN = 7; 73 public static final byte JK_AJP13_PING_REQUEST = 8; 74 public static final byte JK_AJP13_CPING_REQUEST = 10; 75 76 public static final byte JK_AJP13_SEND_BODY_CHUNK = 3; 78 public static final byte JK_AJP13_SEND_HEADERS = 4; 79 public static final byte JK_AJP13_END_RESPONSE = 5; 80 public static final byte JK_AJP13_GET_BODY_CHUNK = 6; 81 public static final byte JK_AJP13_CPONG_REPLY = 9; 82 83 public static final int SC_RESP_CONTENT_TYPE = 0xA001; 85 public static final int SC_RESP_CONTENT_LANGUAGE = 0xA002; 86 public static final int SC_RESP_CONTENT_LENGTH = 0xA003; 87 public static final int SC_RESP_DATE = 0xA004; 88 public static final int SC_RESP_LAST_MODIFIED = 0xA005; 89 public static final int SC_RESP_LOCATION = 0xA006; 90 public static final int SC_RESP_SET_COOKIE = 0xA007; 91 public static final int SC_RESP_SET_COOKIE2 = 0xA008; 92 public static final int SC_RESP_SERVLET_ENGINE = 0xA009; 93 public static final int SC_RESP_STATUS = 0xA00A; 94 public static final int SC_RESP_WWW_AUTHENTICATE = 0xA00B; 95 96 public static final byte SC_A_CONTEXT = 1; public static final byte SC_A_SERVLET_PATH = 2; public static final byte SC_A_REMOTE_USER = 3; 100 public static final byte SC_A_AUTH_TYPE = 4; 101 public static final byte SC_A_QUERY_STRING = 5; 102 public static final byte SC_A_JVM_ROUTE = 6; 103 public static final byte SC_A_SSL_CERT = 7; 104 public static final byte SC_A_SSL_CIPHER = 8; 105 public static final byte SC_A_SSL_SESSION = 9; 106 public static final byte SC_A_SSL_KEYSIZE = 11; 107 public static final byte SC_A_SECRET = 12; 108 public static final byte SC_A_STORED_METHOD = 13; 109 110 public static final byte SC_A_REQ_ATTRIBUTE = 10; 112 113 public static final byte SC_A_ARE_DONE = (byte)0xFF; 115 116 public static final String []methodTransArray = { 118 "OPTIONS", 119 "GET", 120 "HEAD", 121 "POST", 122 "PUT", 123 "DELETE", 124 "TRACE", 125 "PROPFIND", 126 "PROPPATCH", 127 "MKCOL", 128 "COPY", 129 "MOVE", 130 "LOCK", 131 "UNLOCK", 132 "ACL", 133 "REPORT", 134 "VERSION-CONTROL", 135 "CHECKIN", 136 "CHECKOUT", 137 "UNCHECKOUT", 138 "SEARCH", 139 "MKWORKSPACE", 140 "UPDATE", 141 "LABEL", 142 "MERGE", 143 "BASELINE-CONTROL", 144 "MKACTIVITY" 145 }; 146 public static final int SC_M_JK_STORED = (byte) 0xFF; 147 148 public static final int SC_REQ_ACCEPT = 1; 150 public static final int SC_REQ_ACCEPT_CHARSET = 2; 151 public static final int SC_REQ_ACCEPT_ENCODING = 3; 152 public static final int SC_REQ_ACCEPT_LANGUAGE = 4; 153 public static final int SC_REQ_AUTHORIZATION = 5; 154 public static final int SC_REQ_CONNECTION = 6; 155 public static final int SC_REQ_CONTENT_TYPE = 7; 156 public static final int SC_REQ_CONTENT_LENGTH = 8; 157 public static final int SC_REQ_COOKIE = 9; 158 public static final int SC_REQ_COOKIE2 = 10; 159 public static final int SC_REQ_HOST = 11; 160 public static final int SC_REQ_PRAGMA = 12; 161 public static final int SC_REQ_REFERER = 13; 162 public static final int SC_REQ_USER_AGENT = 14; 163 public static final byte SC_A_SSL_KEY_SIZE = 11; 166 public static final String []headerTransArray = { 168 "accept", 169 "accept-charset", 170 "accept-encoding", 171 "accept-language", 172 "authorization", 173 "connection", 174 "content-type", 175 "content-length", 176 "cookie", 177 "cookie2", 178 "host", 179 "pragma", 180 "referer", 181 "user-agent" 182 }; 183 184 187 public static final int HOSTBUFFER = 10; 188 189 192 private static Object lock = new Object (); 193 194 HandlerDispatch dispatch; 195 String ajpidDir="conf"; 196 197 198 public HandlerRequest() 199 { 200 } 201 202 public void init() { 203 dispatch=(HandlerDispatch)wEnv.getHandler( "dispatch" ); 204 if( dispatch != null ) { 205 dispatch.registerMessageType( JK_AJP13_FORWARD_REQUEST, 207 "JK_AJP13_FORWARD_REQUEST", 208 this, null); 210 dispatch.registerMessageType( JK_AJP13_SHUTDOWN, 211 "JK_AJP13_SHUTDOWN", 212 this, null); 214 dispatch.registerMessageType( JK_AJP13_CPING_REQUEST, 215 "JK_AJP13_CPING_REQUEST", 216 this, null); dispatch.registerMessageType( HANDLE_THREAD_END, 218 "HANDLE_THREAD_END", 219 this, null); 220 dispatch.registerMessageType( JK_AJP13_SEND_BODY_CHUNK, "JK_AJP13_SEND_BODY_CHUNK", 223 this,null ); 224 } 225 226 bodyNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "jkInputStream" ); 227 tmpBufNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "tmpBuf" ); 228 secretNote=wEnv.getNoteId( WorkerEnv.ENDPOINT_NOTE, "secret" ); 229 230 if( next==null ) 231 next=wEnv.getHandler( "container" ); 232 if( log.isDebugEnabled() ) 233 log.debug( "Container handler " + next + " " + next.getName() + 234 " " + next.getClass().getName()); 235 236 generateAjp13Id(); 238 } 239 240 public void setSecret( String s ) { 241 requiredSecret=s; 242 } 243 244 public void setUseSecret( boolean b ) { 245 requiredSecret=Double.toString(Math.random()); 246 } 247 248 public void setDecodedUri( boolean b ) { 249 decoded=b; 250 } 251 252 public boolean isTomcatAuthentication() { 253 return tomcatAuthentication; 254 } 255 256 public void setShutdownEnabled(boolean se) { 257 shutdownEnabled = se; 258 } 259 260 public boolean getShutdownEnabled() { 261 return shutdownEnabled; 262 } 263 264 public void setTomcatAuthentication(boolean newTomcatAuthentication) { 265 tomcatAuthentication = newTomcatAuthentication; 266 } 267 268 public void setAjpidDir( String path ) { 269 if( "".equals( path ) ) path=null; 270 ajpidDir=path; 271 } 272 273 276 public void setRegisterRequests(boolean srr) { 277 registerRequests = srr; 278 } 279 280 283 public boolean getRegisterRequests() { 284 return registerRequests; 285 } 286 287 289 private void generateAjp13Id() { 290 int portInt=8009; InetAddress address=null; 293 if( requiredSecret == null ) 294 return; 295 296 File f1=new File ( wEnv.getJkHome() ); 297 File f2=new File ( f1, "conf" ); 298 299 if( ! f2.exists() ) { 300 log.error( "No conf dir for ajp13.id " + f2 ); 301 return; 302 } 303 304 File sf=new File ( f2, "ajp13.id"); 305 306 if( log.isDebugEnabled()) 307 log.debug( "Using stop file: "+sf); 308 309 try { 310 Properties props=new Properties (); 311 312 props.put( "port", Integer.toString( portInt )); 313 if( address!=null ) { 314 props.put( "address", address.getHostAddress() ); 315 } 316 if( requiredSecret !=null ) { 317 props.put( "secret", requiredSecret ); 318 } 319 320 FileOutputStream stopF=new FileOutputStream ( sf ); 321 props.store( stopF, "Automatically generated, don't edit" ); 322 } catch( IOException ex ) { 323 if(log.isDebugEnabled()) 324 log.debug( "Can't create stop file: "+sf,ex ); 325 } 326 } 327 328 String requiredSecret=null; 330 int bodyNote; 331 int tmpBufNote; 332 int secretNote; 333 334 boolean decoded=true; 335 boolean tomcatAuthentication=true; 336 boolean registerRequests=true; 337 boolean shutdownEnabled=false; 338 339 public int invoke(Msg msg, MsgContext ep ) 340 throws IOException 341 { 342 int type=msg.getByte(); 343 ThreadWithAttributes twa = null; 344 if (Thread.currentThread() instanceof ThreadWithAttributes) { 345 twa = (ThreadWithAttributes) Thread.currentThread(); 346 } 347 Object control=ep.getControl(); 348 349 MessageBytes tmpMB=(MessageBytes)ep.getNote( tmpBufNote ); 350 if( tmpMB==null ) { 351 tmpMB= MessageBytes.newInstance(); 352 ep.setNote( tmpBufNote, tmpMB); 353 } 354 if( log.isDebugEnabled() ) 355 log.debug( "Handling " + type ); 356 357 switch( type ) { 358 case JK_AJP13_FORWARD_REQUEST: 359 try { 360 if (twa != null) { 361 twa.setCurrentStage(control, "JkDecode"); 362 } 363 decodeRequest( msg, ep, tmpMB ); 364 if (twa != null) { 365 twa.setCurrentStage(control, "JkService"); 366 twa.setParam(control, 367 ((Request)ep.getRequest()).unparsedURI()); 368 } 369 } catch( Exception ex ) { 370 log.error( "Error decoding request ", ex ); 371 msg.dump( "Incomming message"); 372 return ERROR; 373 } 374 375 if( requiredSecret != null ) { 376 String epSecret=(String )ep.getNote( secretNote ); 377 if( epSecret==null || ! requiredSecret.equals( epSecret ) ) 378 return ERROR; 379 } 380 381 if(log.isDebugEnabled() ) 382 log.debug("Calling next " + next.getName() + " " + 383 next.getClass().getName()); 384 385 int err= next.invoke( msg, ep ); 386 if (twa != null) { 387 twa.setCurrentStage(control, "JkDone"); 388 } 389 390 if( log.isDebugEnabled() ) 391 log.debug( "Invoke returned " + err ); 392 return err; 393 case JK_AJP13_SHUTDOWN: 394 String epSecret=null; 395 if( msg.getLen() > 3 ) { 396 msg.getBytes( tmpMB ); 398 epSecret=tmpMB.toString(); 399 } 400 401 if( requiredSecret != null && 402 requiredSecret.equals( epSecret ) ) { 403 if( log.isDebugEnabled() ) 404 log.debug("Received wrong secret, no shutdown "); 405 return ERROR; 406 } 407 408 JkChannel ch=ep.getSource(); 410 if( !ch.isSameAddress(ep) ) { 411 log.error("Shutdown request not from 'same address' "); 412 return ERROR; 413 } 414 415 if( !shutdownEnabled ) { 416 log.warn("Ignoring shutdown request: shutdown not enabled"); 417 return ERROR; 418 } 419 checkRequest(ep); 421 next.invoke( msg, ep ); 422 423 if(log.isInfoEnabled()) 424 log.info("Exiting"); 425 System.exit(0); 426 427 return OK; 428 429 case JK_AJP13_CPING_REQUEST: 431 msg.reset(); 432 msg.appendByte(JK_AJP13_CPONG_REPLY); 433 ep.setType( JkHandler.HANDLE_SEND_PACKET ); 434 ep.getSource().send( msg, ep ); 435 return OK; 436 437 case HANDLE_THREAD_END: 438 return OK; 439 440 default: 441 if(log.isInfoEnabled()) 442 log.info("Unknown message " + type); 443 } 444 445 return OK; 446 } 447 448 static int count = 0; 449 450 private Request checkRequest(MsgContext ep) { 451 Request req=(Request)ep.getRequest(); 452 if( req==null ) { 453 req=new Request(); 454 Response res=new Response(); 455 req.setResponse(res); 456 ep.setRequest( req ); 457 if( registerRequests ) { 458 synchronized(lock) { 459 ep.getSource().registerRequest(req, ep, count++); 460 } 461 } 462 } 463 return req; 464 } 465 466 private int decodeRequest( Msg msg, MsgContext ep, MessageBytes tmpMB ) 467 throws IOException 468 { 469 Request req = checkRequest(ep); 471 472 RequestInfo rp = req.getRequestProcessor(); 473 rp.setStage(Constants.STAGE_PARSE); 474 MessageBytes tmpMB2 = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE); 475 if(tmpMB2 != null) { 476 tmpMB2.recycle(); 477 } 478 req.setStartTime(System.currentTimeMillis()); 479 JkInputStream jkBody=(JkInputStream)ep.getNote( bodyNote ); 480 if( jkBody==null ) { 481 jkBody=new JkInputStream(); 482 jkBody.setMsgContext( ep ); 483 484 ep.setNote( bodyNote, jkBody ); 485 } 486 487 jkBody.recycle(); 488 489 byte methodCode = msg.getByte(); 491 if (methodCode != SC_M_JK_STORED) { 492 String mName=methodTransArray[(int)methodCode - 1]; 493 req.method().setString(mName); 494 } 495 496 msg.getBytes(req.protocol()); 497 msg.getBytes(req.requestURI()); 498 499 msg.getBytes(req.remoteAddr()); 500 msg.getBytes(req.remoteHost()); 501 msg.getBytes(req.localName()); 502 req.setLocalPort(msg.getInt()); 503 504 boolean isSSL = msg.getByte() != 0; 505 if( isSSL ) { 506 req.scheme().setString("https"); 508 } 509 510 decodeHeaders( ep, msg, req, tmpMB ); 511 512 decodeAttributes( ep, msg, req, tmpMB ); 513 514 rp.setStage(Constants.STAGE_PREPARE); 515 MessageBytes valueMB = req.getMimeHeaders().getValue("host"); 516 parseHost(valueMB, req); 517 req.getCookies().setHeaders(req.getMimeHeaders()); 519 520 int cl=req.getContentLength(); 523 if(cl > 0) { 524 jkBody.setContentLength( cl ); 525 jkBody.receive(); 526 } 527 528 if (log.isTraceEnabled()) { 529 log.trace(req.toString()); 530 } 531 532 return OK; 533 } 534 535 private int decodeAttributes( MsgContext ep, Msg msg, Request req, 536 MessageBytes tmpMB) { 537 boolean moreAttr=true; 538 539 while( moreAttr ) { 540 byte attributeCode=msg.getByte(); 541 if( attributeCode == SC_A_ARE_DONE ) 542 return 200; 543 544 546 if( attributeCode == SC_A_SSL_KEY_SIZE ) { 547 req.setAttribute(SSLSupport.KEY_SIZE_KEY, 549 new Integer ( msg.getInt())); 550 } 552 553 if( attributeCode == SC_A_REQ_ATTRIBUTE ) { 554 msg.getBytes( tmpMB ); 556 String n=tmpMB.toString(); 557 msg.getBytes( tmpMB ); 558 String v=tmpMB.toString(); 559 req.setAttribute(n, v ); 560 } 561 562 563 switch(attributeCode) { 565 case SC_A_CONTEXT : 566 msg.getBytes( tmpMB ); 567 break; 569 570 case SC_A_SERVLET_PATH : 571 msg.getBytes( tmpMB ); 572 break; 574 575 case SC_A_REMOTE_USER : 576 if( tomcatAuthentication ) { 577 msg.getBytes( tmpMB ); 579 } else { 580 msg.getBytes(req.getRemoteUser()); 581 } 582 break; 583 584 case SC_A_AUTH_TYPE : 585 if( tomcatAuthentication ) { 586 msg.getBytes( tmpMB ); 588 } else { 589 msg.getBytes(req.getAuthType()); 590 } 591 break; 592 593 case SC_A_QUERY_STRING : 594 msg.getBytes(req.queryString()); 595 break; 596 597 case SC_A_JVM_ROUTE : 598 msg.getBytes(req.instanceId()); 599 break; 600 601 case SC_A_SSL_CERT : 602 req.scheme().setString( "https" ); 603 MessageBytes tmpMB2 = (MessageBytes)req.getNote(WorkerEnv.SSL_CERT_NOTE); 605 if(tmpMB2 == null) { 606 tmpMB2 = MessageBytes.newInstance(); 607 req.setNote(WorkerEnv.SSL_CERT_NOTE, tmpMB2); 608 } 609 msg.getBytes(tmpMB2); 611 break; 612 613 case SC_A_SSL_CIPHER : 614 req.scheme().setString( "https" ); 615 msg.getBytes(tmpMB); 616 req.setAttribute(SSLSupport.CIPHER_SUITE_KEY, 617 tmpMB.toString()); 618 break; 619 620 case SC_A_SSL_SESSION : 621 req.scheme().setString( "https" ); 622 msg.getBytes(tmpMB); 623 req.setAttribute(SSLSupport.SESSION_ID_KEY, 624 tmpMB.toString()); 625 break; 626 627 case SC_A_SECRET : 628 msg.getBytes(tmpMB); 629 String secret=tmpMB.toString(); 630 log.info("Secret: " + secret ); 631 ep.setNote( secretNote, secret ); 633 break; 634 635 case SC_A_STORED_METHOD: 636 msg.getBytes(req.method()); 637 break; 638 639 default: 640 break; } 642 } 643 return 200; 644 } 645 646 private void decodeHeaders( MsgContext ep, Msg msg, Request req, 647 MessageBytes tmpMB ) { 648 MimeHeaders headers = req.getMimeHeaders(); 650 651 int hCount = msg.getInt(); 652 for(int i = 0 ; i < hCount ; i++) { 653 String hName = null; 654 655 int isc = msg.peekInt(); 659 int hId = isc & 0xFF; 660 661 MessageBytes vMB=null; 662 isc &= 0xFF00; 663 if(0xA000 == isc) { 664 msg.getInt(); hName = headerTransArray[hId - 1]; 666 vMB=headers.addValue( hName ); 667 } else { 668 hId = -1; 675 msg.getBytes( tmpMB ); 676 ByteChunk bc=tmpMB.getByteChunk(); 677 vMB=headers.addValue( bc.getBuffer(), 678 bc.getStart(), bc.getLength() ); 679 } 680 681 msg.getBytes(vMB); 682 683 if (hId == SC_REQ_CONTENT_LENGTH || 684 (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) { 685 req.setContentLength( vMB.getInt() ); 687 } else if (hId == SC_REQ_CONTENT_TYPE || 688 (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) { 689 ByteChunk bchunk = vMB.getByteChunk(); 691 req.contentType().setBytes(bchunk.getBytes(), 692 bchunk.getOffset(), 693 bchunk.getLength()); 694 } 695 } 696 } 697 698 701 private void parseHost(MessageBytes valueMB, Request request) 702 throws IOException { 703 704 if (valueMB == null || valueMB.isNull()) { 705 request.setServerPort(request.getLocalPort()); 709 request.serverName().duplicate(request.localName()); 710 return; 711 } 712 713 ByteChunk valueBC = valueMB.getByteChunk(); 714 byte[] valueB = valueBC.getBytes(); 715 int valueL = valueBC.getLength(); 716 int valueS = valueBC.getStart(); 717 int colonPos = -1; 718 CharChunk hostNameC = (CharChunk)request.getNote(HOSTBUFFER); 719 if(hostNameC == null) { 720 hostNameC = new CharChunk(valueL); 721 request.setNote(HOSTBUFFER, hostNameC); 722 } 723 hostNameC.recycle(); 724 725 boolean ipv6 = (valueB[valueS] == '['); 726 boolean bracketClosed = false; 727 for (int i = 0; i < valueL; i++) { 728 char b = (char) valueB[i + valueS]; 729 hostNameC.append(b); 730 if (b == ']') { 731 bracketClosed = true; 732 } else if (b == ':') { 733 if (!ipv6 || bracketClosed) { 734 colonPos = i; 735 break; 736 } 737 } 738 } 739 740 if (colonPos < 0) { 741 if (request.scheme().equalsIgnoreCase("https")) { 742 request.setServerPort(443); 744 } else { 745 request.setServerPort(80); 747 } 748 request.serverName().setChars(hostNameC.getChars(), 749 hostNameC.getStart(), 750 hostNameC.getLength()); 751 } else { 752 753 request.serverName().setChars(hostNameC.getChars(), 754 hostNameC.getStart(), colonPos); 755 756 int port = 0; 757 int mult = 1; 758 for (int i = valueL - 1; i > colonPos; i--) { 759 int charValue = HexUtils.DEC[(int) valueB[i + valueS]]; 760 if (charValue == -1) { 761 throw new CharConversionException ("Invalid char in port: " + valueB[i + valueS]); 763 } 764 port = port + (charValue * mult); 765 mult = 10 * mult; 766 } 767 request.setServerPort(port); 768 769 } 770 771 } 772 773 } 774 | Popular Tags |