Package org.gudy.azureus2.core3.tracker.server.impl.tcp

Source Code of org.gudy.azureus2.core3.tracker.server.impl.tcp.TRTrackerServerProcessorTCP

/*
* File    : TRTrackerServerProcessor.java
* Created : 5 Oct. 2003
* By      : Parg
*
* Azureus - a Java Bittorrent client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details ( see the LICENSE file ).
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package org.gudy.azureus2.core3.tracker.server.impl.tcp;


import java.io.*;
import java.net.*;
import java.util.*;
import java.util.zip.GZIPOutputStream;

import org.gudy.azureus2.core3.tracker.server.*;
import org.gudy.azureus2.core3.tracker.server.impl.*;
import org.gudy.azureus2.core3.util.*;

import org.bouncycastle.util.encoders.Base64;

import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPosition;
import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPositionManager;
import com.aelitis.azureus.core.util.HTTPUtils;

public abstract class
TRTrackerServerProcessorTCP
  extends   TRTrackerServerProcessor
{
  protected static final int SOCKET_TIMEOUT        = 5000;

  protected static final char    CR      = '\015';
  protected static final char    FF      = '\012';
  protected static final String  NL      = "\015\012";

  private static final String  lc_azureus_name = Constants.AZUREUS_NAME.toLowerCase();

  protected static final byte[]  HTTP_RESPONSE_START = (
    "HTTP/1.1 200 OK" + NL +
    "Content-Type: text/html" + NL +
    "Server: " + Constants.AZUREUS_NAME + " " + Constants.AZUREUS_VERSION + NL +
    "Connection: close" + NL +
    "Content-Length: ").getBytes();
 
  protected static final byte[]  HTTP_RESPONSE_XML_START = (
      "HTTP/1.1 200 OK" + NL +
      "Content-Type: text/xml; charset=\"utf-8\"" + NL +
      "Server: " + Constants.AZUREUS_NAME + " " + Constants.AZUREUS_VERSION + NL +
      "Connection: close" + NL +
      "Content-Length: ").getBytes();

  protected static final byte[]  HTTP_RESPONSE_END_GZIP     = (NL + "Content-Encoding: gzip" + NL + NL).getBytes();
  protected static final byte[]  HTTP_RESPONSE_END_NOGZIP   = (NL + NL).getBytes();
 
  private TRTrackerServerTCP  server;
  private String        server_url;
 
  private boolean      disable_timeouts   = false;

 
  protected
  TRTrackerServerProcessorTCP(
    TRTrackerServerTCP    _server )
  {
    server  = _server;
   
    server_url = (server.isSSL()?"https":"http") + "://" + UrlUtils.convertIPV6Host(server.getHost()) + ":" + server.getPort();
 

  protected boolean
  areTimeoutsDisabled()
  {
    return( disable_timeouts );
  }
 
  protected void
  setTimeoutsDisabled(
    boolean  d )
  {
    disable_timeouts  = d;
  }
 
  protected TRTrackerServerTCP
  getServer()
  {
    return( server );
  }
 
  protected boolean
  processRequest(
    String        input_header,
    String        lowercase_input_header,
    String        url_path,
    InetSocketAddress  local_address,
    InetSocketAddress  remote_address,
    boolean        announce_and_scrape_only,
    boolean        keep_alive,
    InputStream      is,
    OutputStream    os,
    AsyncController    async )
   
    throws IOException
  {
    String  str = url_path;
     
    int  request_type  = TRTrackerServerRequest.RT_UNKNOWN;

    try{
      Map  root = null;
       
      TRTrackerServerTorrentImpl  specific_torrent  = null;
     
      boolean  gzip_reply = false;
     
      boolean    xml_output    = false;
     
     
      try{
        if ( str.startsWith( "/announce?" )){
         
          request_type  = TRTrackerServerRequest.RT_ANNOUNCE;
         
          str = str.substring(10);
         
        }else if ( str.startsWith( "/scrape?" )){
         
          request_type  = TRTrackerServerRequest.RT_SCRAPE;
         
          str = str.substring(8);
       
        }else if ( str.equals( "/scrape" )){
         
          request_type  = TRTrackerServerRequest.RT_FULL_SCRAPE;
         
          str = "";
       
        }else if ( str.startsWith( "/query?" )){

          request_type  = TRTrackerServerRequest.RT_QUERY;
         
          str = str.substring(7);

        }else{
         
          String  redirect = TRTrackerServerImpl.redirect_on_not_found;
         
          if ( announce_and_scrape_only ){
           
            if ( redirect.length() == 0 ){
             
              throw( new Exception( "Tracker only supports announce and scrape functions" ));
            }
          }else{
           
            setTaskState( "external request" );
 
            disable_timeouts  = true;
           
              // check non-tracker authentication
             
            String user = doAuthentication( remote_address, url_path, input_header, os, false );
           
            if ( user == null ){
             
              return( false );
            }
           
            boolean[] ka = new boolean[]{ keep_alive };
           
            if ( handleExternalRequest( local_address, remote_address, user, str, input_header, is, os, async, ka )){
           
              return( ka[0] );
            }
          }
         
          if ( redirect.length() > 0 ){
           
            os.write( ("HTTP/1.1 301 Moved Permanently" + NL + "Location: " + redirect + NL + "Connection: close" + NL +  "Content-Length: 0" + NL + NL).getBytes() );
           
          }else{
           
            os.write( ("HTTP/1.1 404 Not Found" + NL + "Connection: close" + NL + "Content-Length: 0" + NL + NL ).getBytes() );
          }
         
          os.flush();

          return( false ); // throw( new Exception( "Unsupported Request Type"));
        }
       
          // OK, here its an announce, scrape or full scrape
       
          // check tracker authentication
         
        if ( doAuthentication( remote_address, url_path, input_header, os, true ) == null ){
         
          return ( false );
        }
       
       
        int  enc_pos = lowercase_input_header.indexOf( "accept-encoding:");
       
        if ( enc_pos != -1 ){
         
          int  e_pos = input_header.indexOf( NL, enc_pos );
         
          if ( e_pos != -1 ){
           
              // check we've not found X-Accept-Encoding (for example)
           
            if ( enc_pos > 0 ){
             
              char  c = lowercase_input_header.charAt(enc_pos-1);
             
              if ( c != FF && c != ' ' ){
               
                enc_pos  = -1;
              }
            }
           
            if ( enc_pos != -1 ){
             
              String  accept_encoding = lowercase_input_header.substring(enc_pos+16,e_pos);
           
              gzip_reply = HTTPUtils.canGZIP( accept_encoding );
            }
          }
        }
               
        setTaskState( "decoding announce/scrape" );

        int  pos = 0;
         
        byte[]    hash    = null;
        List    hash_list  = null;
        String    link    = null;
       
        HashWrapper  peer_id    = null;
        int      tcp_port  = 0;
        String    event    = null;
         
        long    uploaded    = 0;
        long    downloaded    = 0;
        long    left      = 0;
        int      num_want    = -1;
        boolean    no_peer_id    = false;
        byte    compact_mode  = TRTrackerServerTorrentImpl.COMPACT_MODE_NONE;
        String    key        = null;
        byte    crypto_level   = TRTrackerServerPeer.CRYPTO_NONE;
        int      crypto_port    = 0;
        int      udp_port    = 0;
        int      http_port    = 0;
        int      az_ver      = 0;
        boolean    stop_to_queue  = false;
        String    scrape_flags  = null;
        int      up_speed    = 0;
        boolean    hide      = false;
       
        DHTNetworkPosition  network_position = null;
       
        String    real_ip_address    = remote_address.getAddress().getHostAddress();
        String    client_ip_address  = real_ip_address;
       
        while(pos < str.length()){
           
          int  p1 = str.indexOf( '&', pos );
           
          String  token;
           
          if ( p1 == -1 ){
             
            token = str.substring( pos );
             
          }else{
             
            token = str.substring( pos, p1 );
             
            pos = p1+1;
          }
         
          int  p2 = token.indexOf('=');
           
          if ( p2 == -1 ){
             
            throw( new Exception( "format invalid" ));
          }
           
          String  lhs = token.substring( 0, p2 ).toLowerCase();
          String  rhs = URLDecoder.decode(token.substring( p2+1 ), Constants.BYTE_ENCODING );
           
          // System.out.println( "param:" + lhs + " = " + rhs );
           
          if ( lhs.equals( "info_hash" )){
             
            byte[] b = rhs.getBytes(Constants.BYTE_ENCODING);
           
            if ( hash == null ){
             
              hash = b;
             
            }else{
             
              if ( hash_list == null ){
               
                hash_list = new ArrayList();
               
                hash_list.add( hash );
              }
             
              hash_list.add( b );
            }
             
          }else if ( lhs.equals( "peer_id" )){
           
            peer_id  = new HashWrapper(rhs.getBytes(Constants.BYTE_ENCODING));
           
          }else if ( lhs.equals( "no_peer_id" )){
           
            no_peer_id = rhs.equals("1");
           
          }else if ( lhs.equals( "compact" )){
           
            if ( server.isCompactEnabled()){
             
              if ( rhs.equals("1") && compact_mode == TRTrackerServerTorrentImpl.COMPACT_MODE_NONE ){
               
                compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_NORMAL;
              }
            }
          }else if ( lhs.equals( "key" )){
           
            if ( server.isKeyEnabled()){
             
              key = rhs;
            }
           
          }else if ( lhs.equals( "port" )){
             
            tcp_port = Integer.parseInt( rhs );
           
          }else if ( lhs.equals( "event" )){
             
            event = rhs;
             
          }else if ( lhs.equals( "ip" )){
             
            // System.out.println( "override: " + real_ip_address + " -> " + rhs + " [" + input_header + "]" );
           
            if ( !HostNameToIPResolver.isNonDNSName( rhs )){
             
              for (int i=0;i<rhs.length();i++){
               
                char  c = rhs.charAt(i);
               
                if ( c != '.' && c != ':' && !Character.isDigit( c )){
                 
                  throw( new Exception( "IP override address must be resolved by the client" ));
               
              }
             
              try{
                rhs  = HostNameToIPResolver.syncResolve( rhs ).getHostAddress();
             
              }catch( UnknownHostException e ){
               
                throw( new Exception( "IP override address must be resolved by the client" ));
              }
            }
           
            client_ip_address = rhs;
           
          }else if ( lhs.equals( "uploaded" )){
             
            uploaded = Long.parseLong( rhs );
           
          }else if ( lhs.equals( "downloaded" )){
             
            downloaded = Long.parseLong( rhs );
           
          }else if ( lhs.equals( "left" )){
             
            left = Long.parseLong( rhs );
                       
          }else if ( lhs.equals( "numwant" )){
           
            num_want = Integer.parseInt( rhs );
           
          }else if ( lhs.equals( "azudp" )){
           
            udp_port   = Integer.parseInt( rhs );
           
              // implicit compact mode for 2500 indicated by presence of udp port
           
            compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_AZ;
           
          }else if ( lhs.equals( "azhttp" )){
           
            http_port   = Integer.parseInt( rhs );

          }else if ( lhs.equals( "azver" )){
           
            az_ver   = Integer.parseInt( rhs );
                       
          }else if ( lhs.equals( "supportcrypto" )){
           
            if ( crypto_level == TRTrackerServerPeer.CRYPTO_NONE ){
           
              crypto_level  = TRTrackerServerPeer.CRYPTO_SUPPORTED;
            }
           
          }else if ( lhs.equals( "requirecrypto" )){
                       
            crypto_level  = TRTrackerServerPeer.CRYPTO_REQUIRED;
         
          }else if ( lhs.equals( "cryptoport" )){

            crypto_port = Integer.parseInt( rhs );
           
          }else if ( lhs.equals( "azq" )){
         
            stop_to_queue  = true;
                       
          }else if ( lhs.equals( "azsf" )){
           
            scrape_flags = rhs;
           
          }else if ( lhs.equals( "link" )){
           
            link = rhs;
         
          }else if ( lhs.equals( "outform" )){

            if ( rhs.equals( "xml" )){
             
              xml_output  = true;
            }
           
          }else if ( lhs.equals( "hide" )){
           
            hide   = Integer.parseInt( rhs ) == 1;

          }else if ( TRTrackerServerImpl.supportsExtensions()){
           
            if ( lhs.equals( "aznp" )){

              try{
                network_position = DHTNetworkPositionManager.deserialisePosition( remote_address.getAddress(), Base32.decode( rhs ));
                               
              }catch( Throwable e ){
               
              }
            }else if ( lhs.equals( "azup" )){
 
              up_speed = Integer.parseInt( rhs );
            }
          }
         
          if ( p1 == -1 ){
             
            break;
          }
        }
       
          // let them hide!
          // this is also useful if an az client wants to just hide themselves on
          // particular torrents (to prevent inward connections) as they can just
          // add a tracker-extension to append this option
       
        if ( hide ){
         
          tcp_port   = 0;
          crypto_port  = 0;
          http_port  = 0;
          udp_port  = 0;
        }
       
        if ( crypto_level == TRTrackerServerPeer.CRYPTO_REQUIRED ){
         
          if ( crypto_port != 0 ){
           
            tcp_port = crypto_port;
          }
        }
       
        byte[][]  hashes = null;
       
        if ( hash_list != null ){
           
          hashes = new byte[hash_list.size()][];
           
          hash_list.toArray( hashes );
           
        }else if ( hash != null ){
         
          hashes = new byte[][]{ hash };
        }
       
          // >= so that if this tracker is "old" and sees a version 3+ it replies with the
          // best it can - version 2
       
        if ( xml_output ){
         
          compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_XML;
         
        }else if ( az_ver >= 2 ){
         
          compact_mode = TRTrackerServerTorrentImpl.COMPACT_MODE_AZ_2;
        }
       
        Map[]            root_out = new Map[1];
        TRTrackerServerPeerImpl[]  peer_out = new TRTrackerServerPeerImpl[1];
       
        specific_torrent =
            processTrackerRequest(
              server, str,
              root_out, peer_out,
              request_type,
              hashes, link, scrape_flags,
              peer_id, no_peer_id, compact_mode, key,
              event, stop_to_queue,
              tcp_port&0xffff, udp_port&0xffff, http_port&0xffff,
              real_ip_address,
              client_ip_address,
              downloaded, uploaded, left,
              num_want,
              crypto_level,
              (byte)az_ver,
              up_speed,
              network_position );
       
        root  = root_out[0];

        if ( request_type == TRTrackerServerRequest.RT_SCRAPE ){
         
            // add in tracker type for az clients so they know this is an AZ tracker
         
          if ( lowercase_input_header.indexOf( lc_azureus_name ) != -1 ){
       
            root.put( "aztracker", new Long(1));
          }
        }

          // only post-process if this isn't a cached entry
       
        if ( root.get( "_data" ) == null ){
 
          TRTrackerServerPeer  post_process_peer = peer_out[0];
         
          if ( post_process_peer == null ){
           
            post_process_peer = new lightweightPeer( client_ip_address, tcp_port, peer_id );
          }
         
          server.postProcess( post_process_peer, specific_torrent, request_type, str, root );
        }
       
      }catch( Exception e ){
       
        String  warning_message = null;
       
        Map  error_entries    = null;
       
        if ( e instanceof TRTrackerServerException ){
         
          TRTrackerServerException  tr_excep = (TRTrackerServerException)e;
         
          int  reason = tr_excep.getResponseCode();
         
          error_entries = tr_excep.getErrorEntries();
         
          if ( reason != -1 ){
           
            String  resp = "HTTP/1.1 " + reason + " " + tr_excep.getResponseText() + NL;
           
            Map  headers = tr_excep.getResponseHeaders();
           
            Iterator  it = headers.entrySet().iterator();
           
            while( it.hasNext()){
             
              Map.Entry  entry = (Map.Entry)it.next();
             
              String  key   = (String)entry.getKey();
              String  value   = (String)entry.getValue();
             
              if ( key.equalsIgnoreCase( "connection" )){
               
                if ( !value.equalsIgnoreCase( "close" )){
                 
                  Debug.out( "Ignoring 'Connection' header" );
                 
                  continue;
                }
              }
              resp += key + ": " + value + NL;
            }

            resp += "Connection: close" + NL;

            byte[]  payload = null;
           
            if ( error_entries != null ){
             
              payload = BEncoder.encode( error_entries );
             
              resp += "Content-Length: " + payload.length + NL;
            }else{
             
              resp += "Content-Length: 0" + NL;
            }
                       
            resp += NL;

            os.write( resp.getBytes());
           
            if ( payload != null ){
             
              os.write( payload );
            }
           
            os.flush();

            return( false );
          }
         
          if ( tr_excep.isUserMessage()){
           
            warning_message = tr_excep.getMessage();
          }         
        }else if ( e instanceof NullPointerException ){
         
          e.printStackTrace();
        }
       
        String  message = e.getMessage();
       
        // e.printStackTrace();
       
        if ( message == null || message.length() == 0 ){

          // e.printStackTrace();
               
          message = e.toString();
        }
         
        root  = new HashMap();
               
        root.put( "failure reason", message );
       
        if ( warning_message != null ){
         
          root.put( "warning message", warning_message );
        }
       
        if ( error_entries != null ){
         
          root.putAll( error_entries );
        }
      }
   
      setTaskState( "writing response" );

      byte[]  data;
      byte[]  header_start;
     
      if ( xml_output ){
       
        StringBuffer  xml = new StringBuffer( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" );

        xml.append( "<RESULT>" );
       
        if ( specific_torrent != null ){
         
          xml.append( "<BTIH>" );
          xml.append( ByteFormatter.encodeString( specific_torrent.getHash().getBytes()));
          xml.append( "</BTIH>" );
         
          xml.append( BEncoder.encodeToXML( root, true ));
        }
       
        xml.append( "</RESULT>" );
       
        data      = xml.toString().getBytes("UTF-8" );
       
        header_start = HTTP_RESPONSE_XML_START;

      }else{
          // cache both plain and gzip encoded data for possible reuse
       
        data     = (byte[])root.get( "_data" );
           
        if ( data == null ){
         
          data = BEncoder.encode( root );
         
          if ( data.length > 1000000 ){
           
            File  dump = new File( "bdecoder.dump" );
           
            synchronized( TRTrackerServerProcessorTCP.class ){
             
              try{
                Debug.out( "Output is too large, saving diagnostics to " + dump.toString());
               
                PrintWriter  pw = new PrintWriter( new FileWriter( dump ));
               
                BDecoder.print( pw, root );
               
                pw.close();
               
              }catch( Throwable e ){
               
              }
            }
          }
         
          root.put( "_data", data );
        }
       
        header_start = HTTP_RESPONSE_START;
     
     
      if ( gzip_reply ){
       
        byte[]  gzip_data = (byte[])root.get( "_gzipdata");
       
        if ( gzip_data == null ){
           
          ByteArrayOutputStream tos = new ByteArrayOutputStream(data.length);
         
          GZIPOutputStream gos = new GZIPOutputStream( tos );
         
          gos.write( data );
         
          gos.close();
         
          gzip_data = tos.toByteArray();
         
          root.put( "_gzipdata", gzip_data );
        }
       
        data  = gzip_data;
      }
     
        // System.out.println( "TRTrackerServerProcessor::reply: sending " + new String(data));
             
        // write the response
     
      setTaskState( "writing header" );

      os.write( header_start );
     
      byte[]  length_bytes = String.valueOf(data.length).getBytes();
     
      os.write( length_bytes );

      int  header_len = header_start.length + length_bytes.length;
     
      setTaskState( "writing content" );

      if ( gzip_reply ){
       
        os.write( HTTP_RESPONSE_END_GZIP );
     
        header_len += HTTP_RESPONSE_END_GZIP.length;
      }else{
       
        os.write( HTTP_RESPONSE_END_NOGZIP );

        header_len += HTTP_RESPONSE_END_NOGZIP.length;
      }
         
      os.write( data );
     
      server.updateStats( request_type, specific_torrent, input_header.length(), header_len+data.length );
             
    }finally{
     
      setTaskState( "final os flush" );

      os.flush();
    }
   
    return( false );
  }
 
  protected String
  doAuthentication(
    InetSocketAddress  remote_ip,
    String        url_path,
    String        header,
    OutputStream    os,
    boolean        tracker )
   
    throws IOException
  {
    // System.out.println( "doAuth: " + server.isTrackerPasswordEnabled() + "/" + server.isWebPasswordEnabled());
   
    boolean  apply_web_password     = (!tracker) && server.isWebPasswordEnabled();
    boolean apply_torrent_password  = tracker && server.isTrackerPasswordEnabled();
   
    if (   apply_web_password &&
        server.isWebPasswordHTTPSOnly() &&
        !server.isSSL()){
     
      os.write( ("HTTP/1.1 403 BAD\r\n\r\nAccess Denied\r\n").getBytes() );
     
      os.flush();
       
      return( null );

    }else if apply_torrent_password ||
          apply_web_password ){
     
      int  x = header.indexOf( "Authorization:" );
     
      if ( x == -1 ){
       
          // auth missing. however, if we have external auth defined
          // and external auth is happy with junk then allow it through
       
        if ( server.hasExternalAuthorisation()){
         
          try{
            String  resource_str =
              ( server.isSSL()?"https":"http" ) + "://" +
                UrlUtils.convertIPV6Host(server.getHost()) + ":" + server.getPort() + url_path;
           
            URL  resource = new URL( resource_str );
         
            if ( server.performExternalAuthorisation( remote_ip, header, resource, "", "" )){
             
              return( "" );
            }
          }catch( MalformedURLException e ){
           
            Debug.printStackTrace( e );
          }
        }
      }else{
                             
          //      Authorization: Basic dG9tY2F0OnRvbWNhdA==
   
        int  p1 = header.indexOf(' ', x );
        int p2 = header.indexOf(' ', p1+1 );
       
        String  body = header.substring( p2, header.indexOf( '\r', p2 )).trim();
       
        String decoded=new String( Base64.decode(body));

          // username:password
                 
        int  cp = decoded.indexOf(':');
       
        String  user = decoded.substring(0,cp);
        String  pw   = decoded.substring(cp+1);
       
        boolean  auth_failed  = false;
       
        if ( server.hasExternalAuthorisation()){
         
          try{
            String  resource_str =
              ( server.isSSL()?"https":"http" ) + "://" +
              UrlUtils.convertIPV6Host(server.getHost()) + ":" + server.getPort() + url_path;
           
            URL  resource = new URL( resource_str );
         
            if ( server.performExternalAuthorisation( remote_ip, header, resource, user, pw )){
             
              return( user );
            }
          }catch( MalformedURLException e ){
           
            Debug.printStackTrace( e );
          }
         
          auth_failed  = true;
        }
       
        if ( server.hasInternalAuthorisation() && !auth_failed ){
         
          try{
       
            SHA1Hasher hasher = new SHA1Hasher();
           
            byte[] password = pw.getBytes();
           
            byte[] encoded;
           
            if( password.length > 0){
           
              encoded = hasher.calculateHash(password);
             
            }else{
             
              encoded = new byte[0];
            }
           
            if ( user.equals( "<internal>")){
             
              byte[] internal_pw = Base64.decode(pw);
 
              if ( Arrays.equals( internal_pw, server.getPassword())){
               
                return( user );
              }
            }else if (   user.equalsIgnoreCase(server.getUsername()) &&
                  Arrays.equals(encoded, server.getPassword())){
                
               return( user );        
            }
          }catch( Exception e ){
           
            Debug.printStackTrace( e );
          }
        }
      }
     
      os.write( ("HTTP/1.1 401 BAD\r\nWWW-Authenticate: Basic realm=\"" + server.getName() + "\"\r\n\r\nAccess Denied\r\n").getBytes() );
     
      os.flush();
       
      return( null );

    }else{
   
      return( "" );
    }
  }
   
  protected boolean
  handleExternalRequest(
    InetSocketAddress  local_address,
    InetSocketAddress  remote_address,
    String        user,
    String        url,
    String        header,
    InputStream      is,
    OutputStream    os,
    AsyncController    async,
    boolean[]      keep_alive )
   
    throws IOException
  {
    URL  absolute_url = new URL( server_url + (url.startsWith("/")?url:("/"+url)));
     
    return( server.handleExternalRequest( local_address, remote_address, user,url,absolute_url,header, is, os, async, keep_alive ));
  }
}
TOP

Related Classes of org.gudy.azureus2.core3.tracker.server.impl.tcp.TRTrackerServerProcessorTCP

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.