1 19 20 package org.apache.excalibur.instrument.manager.http.server; 21 22 import java.io.BufferedReader ; 23 import java.io.ByteArrayOutputStream ; 24 import java.io.File ; 25 import java.io.FileNotFoundException ; 26 import java.io.FileWriter ; 27 import java.io.InputStream ; 28 import java.io.InputStreamReader ; 29 import java.io.IOException ; 30 import java.io.OutputStream ; 31 import java.io.PrintWriter ; 32 import java.io.UnsupportedEncodingException ; 33 import java.net.InetAddress ; 34 import java.net.Socket ; 35 import java.text.SimpleDateFormat ; 36 import java.util.ArrayList ; 37 import java.util.Date ; 38 import java.util.HashMap ; 39 import java.util.List ; 40 import java.util.Locale ; 41 import java.util.Map ; 42 import java.util.StringTokenizer ; 43 44 import org.apache.excalibur.instrument.CounterInstrument; 45 46 52 public class HTTPServer 53 extends AbstractSocketServer 54 { 55 56 private List m_handlers = new ArrayList (); 57 58 59 private HTTPURLHandler[] m_handlerArray; 60 61 62 private String m_accessLogFile; 63 64 65 private File m_currentLogFile; 66 67 68 private PrintWriter m_currentLogWriter; 69 70 71 private SimpleDateFormat m_dayFormat = new SimpleDateFormat ( "yyyy-MM-dd" ); 72 73 75 private SimpleDateFormat m_logTimeFormat = 76 new SimpleDateFormat ( "dd/MMM/yyyy:HH:mm:ss Z", Locale.US ); 77 78 79 private CounterInstrument m_instrumentRequests; 80 81 83 private CounterInstrument m_instrumentResponseBytes; 84 85 86 private CounterInstrument m_instrumentRequestBytes; 87 88 91 98 public HTTPServer( int port, InetAddress bindAddress ) 99 { 100 super( port, bindAddress ); 101 102 addInstrument( m_instrumentRequests = new CounterInstrument( "requests" ) ); 103 addInstrument( m_instrumentResponseBytes = new CounterInstrument( "response-bytes" ) ); 104 addInstrument( m_instrumentRequestBytes = new CounterInstrument( "request-bytes" ) ); 105 } 106 107 110 111 116 public void stop() 117 throws Exception 118 { 119 super.stop(); 120 121 synchronized( this ) 123 { 124 if ( m_currentLogWriter != null ) 125 { 126 m_currentLogWriter.close(); 127 m_currentLogWriter = null; 128 m_currentLogFile = null; 129 } 130 } 131 } 132 133 139 protected void handleSocket( Socket socket ) 140 { 141 if ( getLogger().isDebugEnabled() ) 142 { 143 getLogger().debug( "handleSocket( " + socket + " ) BEGIN : " 144 + Thread.currentThread().getName() ); 145 } 146 147 String ip = socket.getInetAddress().getHostAddress(); 148 149 try 150 { 151 while ( handleRequest( socket.getInputStream(), socket.getOutputStream(), ip ) 153 && !isStopping() ) 154 { 155 } 156 } 157 catch ( java.io.InterruptedIOException e ) { 159 } 161 catch ( java.net.SocketException e ) 162 { 163 } 165 catch ( Throwable e ) 166 { 167 getLogger().debug( "Encountered an error processing the request.", e ); 168 } 169 170 if ( getLogger().isDebugEnabled() ) 171 { 172 getLogger().debug( "handleSocket( " + socket + " ) END : " 173 + Thread.currentThread().getName() ); 174 } 175 } 176 177 180 181 189 public void setAccessLogFile( String accessLogFile ) 190 { 191 m_accessLogFile = accessLogFile; 192 } 193 194 199 public void registerHandler( HTTPURLHandler handler ) 200 { 201 synchronized( m_handlers ) 202 { 203 m_handlers.add( handler ); 204 m_handlerArray = null; 205 } 206 } 207 208 private void logAccessEvent( String ip, 209 String method, 210 String url, 211 int errorCode, 212 int contentLength, 213 String referrer, 214 String userAgent ) 215 { 216 if ( m_accessLogFile == null ) 217 { 218 return; 219 } 220 221 Date now = new Date (); 222 223 synchronized( this ) 224 { 225 File file; 226 int datePos = m_accessLogFile.indexOf( "yyyy_mm_dd" ); 227 if ( datePos >= 0 ) 228 { 229 StringBuffer sb = new StringBuffer (); 230 if ( datePos > 0 ) 231 { 232 sb.append( m_accessLogFile.substring( 0, datePos ) ); 233 } 234 sb.append( m_dayFormat.format( now ) ); 235 if ( datePos + 10 < m_accessLogFile.length() ) 236 { 237 sb.append( m_accessLogFile.substring( datePos + 10 ) ); 238 } 239 240 file = new File ( sb.toString() ); 241 } 242 else 243 { 244 file = new File ( m_accessLogFile ); 245 } 246 247 if ( ( m_currentLogFile == null ) || ( !m_currentLogFile.equals( file ) ) ) 248 { 249 if ( m_currentLogWriter != null ) 251 { 252 m_currentLogWriter.close(); 253 } 254 try 255 { 256 m_currentLogWriter = new PrintWriter ( new FileWriter ( file ) ); 257 m_currentLogFile = file; 258 } 259 catch ( IOException e ) 260 { 261 getLogger().warn( "Unable to open: " + m_currentLogFile ); 262 m_currentLogWriter = null; 263 m_currentLogFile = null; 264 return; 265 } 266 } 267 268 StringBuffer sb = new StringBuffer (); 270 sb.append( ip ); 271 sb.append( " - - [" ); 272 sb.append( m_logTimeFormat.format( now ) ); 273 sb.append( "] \"" ); 274 sb.append( method ); 275 sb.append( " " ); 276 sb.append( url ); 277 sb.append( "\" " ); 278 sb.append( errorCode ); 279 sb.append( " " ); 280 sb.append( contentLength ); 281 sb.append( " \"" ); 282 sb.append( referrer ); 283 sb.append( "\" \"" ); 284 sb.append( userAgent ); 285 sb.append( "\"" ); 286 287 m_currentLogWriter.println( sb.toString() ); 288 m_currentLogWriter.flush(); 289 } 290 } 291 292 private boolean handleRequest( InputStream is, OutputStream os, String ip ) 293 throws IOException 294 { 295 300 BufferedReader r = new BufferedReader ( new InputStreamReader ( is ) ); 302 String request = r.readLine(); 303 if ( request == null ) 304 { 305 return false; 307 } 308 309 String referrer = "-"; 310 String userAgent = "-"; 311 String host = null; 312 int requestBytes = request.getBytes().length + 1; 313 try 314 { 315 String header; 317 do 318 { 319 header = r.readLine(); 320 getLogger().debug( "Header: " + header ); 321 if ( header != null ) 322 { 323 if ( header.startsWith( "User-Agent: " ) ) 324 { 325 userAgent = header.substring( 12 ); 326 } 327 else if ( header.startsWith( "Referer: " ) ) 328 { 329 referrer = header.substring( 9 ); 330 } 331 else if ( header.startsWith( "Host: " ) ) 332 { 333 host = header.substring( 6 ); 334 } 335 336 requestBytes += header.getBytes().length + 1; 337 } 338 } 339 while ( ( header != null ) && ( header.length() > 0 ) ); 340 } 341 finally 342 { 343 if ( requestBytes > 0 ) 344 { 345 m_instrumentRequestBytes.increment( requestBytes ); 346 } 347 } 348 349 if ( getLogger().isDebugEnabled() ) 350 { 351 getLogger().debug( "got request: " + request + " : " 352 + Thread.currentThread().getName() ); 353 } 354 355 Throwable error = null; 356 357 ByteArrayOutputStream hbos = new ByteArrayOutputStream (); 359 PrintWriter out = new PrintWriter ( hbos ); 360 361 String method = "ERROR"; 362 String url = ""; 363 364 StringTokenizer st = new StringTokenizer ( request, " " ); 366 if ( st.countTokens() == 3 ) 367 { 368 method = st.nextToken(); 369 url = st.nextToken(); 370 String version = st.nextToken(); 371 372 if ( method.equals( "GET" ) && version.startsWith( "HTTP/" ) ) 373 { 374 String path; 376 String query = null; 377 int pos = url.indexOf( '?' ); 378 if ( pos > 0 ) 379 { 380 path = url.substring( 0, pos ); 381 382 if ( pos < url.length() - 1 ) 383 { 384 query = url.substring( pos + 1 ); 385 } 386 } 387 else 388 { 389 path = url; 390 } 391 392 396 HTTPURLHandler[] handlers = getHandlers(); 397 for ( int i = 0; i < handlers.length; i++ ) 398 { 399 HTTPURLHandler handler = handlers[i]; 400 401 if ( path.startsWith( handler.getPath() ) ) 403 { 404 407 Map params = new HashMap (); 409 if ( query != null ) 410 { 411 decodeQuery( params, query, handler.getEncoding() ); 413 } 414 415 if ( getLogger().isDebugEnabled() ) 416 { 417 getLogger().debug( "Request Path: " + path ); 418 getLogger().debug( " Parameters: " + params.toString() ); 419 } 420 421 m_instrumentRequests.increment(); 422 423 ByteArrayOutputStream bos = new ByteArrayOutputStream (); 427 boolean ok; 428 try 430 { 431 handler.handleRequest( path, params, bos ); 432 433 ok = true; 434 } 435 catch ( HTTPRedirect e ) 436 { 437 String redirectPath = e.getPath(); 441 if ( ( host != null ) && ( redirectPath.indexOf( "://" ) < 0 ) ) 442 { 443 StringBuffer sb = new StringBuffer (); 445 sb.append( "http://" ); 446 sb.append( host ); 447 if ( redirectPath.startsWith( "." ) ) 448 { 449 int slashPos = path.lastIndexOf( '/' ); 451 String subpath; 452 if ( slashPos > 0 ) 453 { 454 subpath = path.substring( 0, slashPos + 1 ); 455 } 456 else 457 { 458 subpath = "/"; 459 } 460 sb.append( subpath ); 461 } 462 else if ( !redirectPath.startsWith( "/" ) ) 463 { 464 sb.append( "/" ); 465 } 466 sb.append( redirectPath ); 467 468 redirectPath = sb.toString(); 469 } 470 471 if ( getLogger().isDebugEnabled() ) 472 { 473 if ( redirectPath.equals( e.getPath() ) ) 474 { 475 getLogger().debug( "Redirect to: " + redirectPath ); 476 } 477 else 478 { 479 getLogger().debug( 480 "Redirect to: " + e.getPath() + " -> " + redirectPath ); 481 } 482 } 483 484 byte[] contents = ( "<html><head><title>302 Found</title></head><body>" 485 + "The document has moved <a HREF='" + redirectPath + "'>here</a>" 486 + "</body></html>" ).getBytes( handler.getEncoding() ); 487 488 out.println( "HTTP/1.1 302 Found" ); out.println( "Date: " + new Date () ); 491 out.println( "Server: Avalon Instrument Manager HTTP Connector" ); 492 out.println( "Content-Length: " + contents.length ); 493 out.println( "Location: " + redirectPath ); 494 out.println( "Keep-Alive: timeout=" + ( getSoTimeout() / 1000 ) ); 495 out.println( "Connection: Keep-Alive" ); 496 out.println( "Content-Type: " + handler.getContentType() ); 497 out.println( "Pragma: no-cache" ); 499 out.println( "Expires: Thu, 01 Jan 1970 00:00:00 GMT" ); 500 out.println( "Cache-Control: no-cache" ); 501 502 out.println( "" ); 504 505 out.flush(); 507 byte[] responseBytes = hbos.toByteArray(); 508 os.write( responseBytes ); 509 510 os.write( contents, 0, contents.length ); 512 513 os.flush(); 515 516 m_instrumentResponseBytes.increment( 518 responseBytes.length + contents.length ); 519 520 logAccessEvent( 522 ip, method, url, 302, contents.length, referrer, userAgent ); 523 524 526 return true; 527 } 528 catch ( Throwable t ) 529 { 530 error = t; 532 ok = false; 533 } 534 535 if ( ok ) 536 { 537 byte[] contents = bos.toByteArray(); 538 539 out.println( "HTTP/1.1 200 OK" ); 541 out.println( "Date: " + new Date () ); 542 out.println( "Server: Avalon Instrument Manager HTTP Connector" ); 543 out.println( "Content-Length: " + contents.length ); 544 out.println( "Keep-Alive: timeout=" + ( getSoTimeout() / 1000 ) ); 545 out.println( "Connection: Keep-Alive" ); 546 out.println( "Content-Type: " + handler.getContentType() ); 547 out.println( "Pragma: no-cache" ); 549 out.println( "Expires: Thu, 01 Jan 1970 00:00:00 GMT" ); 550 out.println( "Cache-Control: no-cache" ); 551 552 out.println( "" ); 554 555 out.flush(); 557 byte[] responseBytes = hbos.toByteArray(); 558 os.write( responseBytes ); 559 560 os.write( contents, 0, contents.length ); 562 563 os.flush(); 565 566 m_instrumentResponseBytes.increment( 568 responseBytes.length + contents.length ); 569 570 logAccessEvent( ip, method, url, 200, contents.length, referrer, userAgent ); 572 573 575 return true; 576 } 577 else 578 { 579 break; 581 } 582 } 583 } 584 } 585 } 586 587 out.println( "HTTP/1.1 404 Not Found" ); 590 out.println( "Date: " + new Date () ); 591 out.println( "Server: Avalon Instrument Manager HTTP Connector" ); 592 out.println( "Content-Type: text/plain; charset=UTF-8" ); 593 out.println( "" ); 594 out.println( "The Requested page does not exist" ); 595 if ( error != null ) 596 { 597 out.println( "---" ); 598 if ( error instanceof FileNotFoundException ) 599 { 600 out.println( error.getMessage() ); 601 } 602 else 603 { 604 getLogger().error( "Error servicing request.", error ); 605 error.printStackTrace( out ); 606 } 607 } 608 609 out.flush(); 611 byte[] responseBytes = hbos.toByteArray(); 612 os.write( responseBytes ); 613 os.flush(); 614 615 m_instrumentResponseBytes.increment( responseBytes.length ); 617 618 logAccessEvent( ip, method, url, 404, 0, referrer, userAgent ); 620 621 return false; 622 } 623 624 public void setParameter( Map params, String param, String value ) 625 { 626 Object old = params.get( param ); 627 if ( old == null ) 628 { 629 params.put( param, value ); 630 } 631 else 632 { 633 if ( old instanceof String ) 634 { 635 List list = new ArrayList (); 636 list.add( old ); 637 list.add( value ); 638 params.put( param, list ); 639 } 640 else 641 { 642 List list = (List )old; 643 list.add( value ); 644 } 645 } 646 } 647 648 private void decodeParameter( Map params, String pair, String encoding ) 649 { 650 int pos = pair.indexOf( '=' ); 651 if ( pos > 0 ) 652 { 653 try 654 { 655 String param = URLCoder.decode( pair.substring( 0, pos ), encoding ); 658 659 String value; 660 if ( pos < pair.length() - 1 ) 661 { 662 value = URLCoder.decode( pair.substring( pos + 1 ), encoding ); 663 } 664 else 665 { 666 value = ""; 667 } 668 669 setParameter( params, param, value ); 670 } 671 catch ( UnsupportedEncodingException e ) 672 { 673 throw new IllegalArgumentException ( "Unknown encoding: " + e.toString() ); 674 } 675 } 676 } 677 678 private void decodeQuery( Map params, String query, String encoding ) 679 { 680 StringTokenizer st = new StringTokenizer ( query, "&" ); 681 while ( st.hasMoreTokens() ) 682 { 683 decodeParameter( params, st.nextToken(), encoding ); 684 } 685 } 686 687 private HTTPURLHandler[] getHandlers() 688 { 689 HTTPURLHandler[] handlers = m_handlerArray; 690 if ( handlers == null ) 691 { 692 synchronized( m_handlers ) 693 { 694 handlers = new HTTPURLHandler[ m_handlers.size() ]; 695 m_handlers.toArray( handlers ); 696 m_handlerArray = handlers; 697 } 698 } 699 700 return handlers; 701 } 702 } 703 704 | Popular Tags |