2010-11-08 3 views
0

désolé pour ce titre mais il est très difficile à expliquer en quelques mots.Proxy Web personnalisé: est-il préférable d'effectuer un fork ou de faire des requêtes http?

J'ai écrit un petit proxy web - pas apache ou n'importe quel serveur web - dont le rôle est d'exécuter du code PHP.

Il y a deux façons de le faire:

1) FOURCHETTE un nouveau php -f file.php

2) appeler http://localhost/file.php à partir du proxy Web.

Je pense qu'il y aurait beaucoup de demandes simultanées à ce proxy, et chaque demande restera en vie pendant au moins 20-30 secondes.

Ma question est: quel est le meilleur entre le forking et la demande via http?

Merci pour tout conseil!

Dario

+0

Ajout d'un code exemple loooooong. –

Répondre

0

J'ai aussi fait un proxy, récemment. Et - il y a une troisième option. N'a même pas besoin de s'appeler ou un autre script, il est complètement autonome, et multiplate-forme ...

Donc - première chose est que j'ai fait cela en utilisant des sockets. Je suppose que vous l'avez fait aussi, mais juste en l'écrivant ici au cas où vous ne l'auriez pas fait. J'ai placé le proxy dans le navigateur à un port spécifique, qui est autorisé à travers le pare-feu dans le script PHP écoute. Pour le faire commencer à écouter, je dois "exécuter" le script sur le port 80, donc j'ai aussi une console en temps réel. Donc, le script écoute sur le socket, et il est réglé sur NON-BLOCKING. Cela fonctionne avec une boucle (infinie, expire en utilisant break si nécessaire) - * @ socket_accept() * est utilisé pour voir s'il y a une nouvelle connexion, vérifiée si! == false. Enfin, la boucle boucle (: D) à travers toutes les sockets enfant engendrées, qui sont stockées dans un tableau. C'est un proxy HTTP, donc ils ont tous quelques étapes spécifiques (envoi des en-têtes du client, tentative d'atteindre le serveur distant, envoi des en-têtes du client, réception des données du serveur distant). Des choses comme la lecture qui serait (sur la socket distante ouverte avec fsockopen()) normalement être bloquées, et il n'y a aucun moyen de régler cela à non-bloquant sur celles-ci, "émulant" le mode non-bloquant par un délai d'expiration très - comme 5 microsecondes, et max. les caractères lus sont 128.

tl; dr; Essentiellement, c'est comme un processeur multi-threading.

TOUTEFOIS !!! vous avez besoin de prises si c'est fait comme ça.

EDIT: l'ajout d'un code exemple dépouillé de toutes les actions personnalisées: (oui, il est temps)

set_time_limit(0); // so we don't get a timeout from PHP 
// - can lead to sockets left open... 

$config_port = 85; // port 
$config_address = "192.168.0.199"; // IP address, most likely local with router 
// if there's not a router between server and wan, use the wan IP 
$config_to = 30; // global timeout in seconds 
$config_connect_to = 2; // timeout for connecting to remote server 
$config_client_to = 0.1; // timeout for reading client data 
$config_remote_to = 10; // timeout for reading remote data (microseconds) 
$config_remote_stage_to = 15; // timeout for last stage (seconds) 
$config_backlog = 5000; // max backlogs, the more the better (usually) 

$parent_sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // parent socket 
$tmp_res = @socket_bind($parent_sock, $config_address, $config_port); 
if ($tmp_res === false){ 
    echo "Can't bind socket."; 
    exit; 
    // address or port in use, for example by Apache 
} 

$tmp_res = @socket_listen($parent_sock, $config_backlog); 
if ($tmp_res === false){ 
    echo "Can't start socket listener."; 
    exit; 
    // hard to tell what can cause this 
} 

socket_set_nonblock($parent_sock); // non-blocking mode 

$sockets = array(); // children sockets 
$la = time(); // last activity 
while (time() - $la < $config_to){ 
    $spawn = @socket_accept($parent_sock); // check for new connection 
    if ($spawn !== false){ 
     $la = time(); 

     $ni = count($sockets); 
     $sockets[$ni] = array(); 
     $sockets[$ni]["handle"] = $spawn; 
     $sockets[$ni]["stage"] = 1; 
     $sockets[$ni]["la"] = time(); // for some stages 
     $sockets[$ni]["client_data"] = ""; 
     $sockets[$ni]["headers"] = array(); 
     $sockets[$ni]["remote"] = false; 
    } 

    foreach ($sockets as &$sock){ // &$sock because we're gonna edit the var 
     switch ($sock["stage"]){ 
     case 1: // receive client data 
     $read_data = @socket_read($sock["handle"], 2048); 
     if ($read_data !== false && $read_data !== ""){ 
      $la = time(); 
      $sock["la"] = microtime(true); 
      $sock["client_data"] .= $read_data; 
     } else if (microtime(true) - $sock["la"] > $config_client_to) { 
      // client data received (or too slow :D) 
      $sock["stage"] = 2; // go on 
     } 
     break; 
     case 2: // connect to remote 
     $headers = explode("\r\n", $sock["client_data"]); 
     foreach ($headers as $hdr){ 
      $h_pos = strpos($hdr, ":"); 
      if ($h_pos !== false){ 
       $nhid = count($sock["headers"]); 
       $sock["headers"][strtolower(substr($hdr, 0, $h_pos))] = ltrim(substr($hdr, $h_pos + 1)); 
      } 
     } 

     // we'll have to use the "Host" header to know target server 
     $sock["remote"] = @fsockopen($sock["headers"]["host"], 80, $sock["errno"], $sock["errstr"], $config_connect_to); 
     if ($sock["remote"] === false){ 
      // error, echo it and close client socket, set stage to 0 
      echo "Socket error: #".$sock["errno"].": ".$sock["errstr"]."<br />\n"; 
      flush(); // immediately show the error 
      @socket_close($sock["handle"]); 
      $sock["handle"] = 0; 
     } else { 
      $la = time(); 
      // okay - connected 
      $sock["stage"] = 3; 
     } 
     break; 
     case 3: // send client data 
     $tmp_res = @fwrite($sock["remote"], $sock["client_data"]); 
     // this currently supports just client data up to 8192 bytes long 
     if ($tmp_res === false){ 
      // error 
      echo "Couldn't send client data to remote server!<br />\n"; 
      flush(); 
      @socket_close($sock["handle"]); 
      @fclose($sock["remote"]); 
      $sock["stage"] = 0; 
     } else { 
      // client data sent 
      $la = time(); 
      stream_set_timeout($sock["remote"], $config_remote_to); 
      $sock["la"] = time(); // we'll need this in stage 4 
      $sock["stage"] = 4; 
     } 
     break; 
     case 4: 
     $remote_read = @fread($sock["remote"], 128); 
     if ($remote_read !== false && $remote_read !== ""){ 
      $la = time(); 
      $sock["la"] = time(); 
      @socket_write($sock["handle"], $remote_read); 
     } else { 
      if (time() - $sock["la"] >= $config_remote_stage_to){ 
       echo "Timeout.<br />\n"; 
       flush(); 
       @socket_close($sock["handle"]); 
       @fclose($sock["remote"]); 
       $sock["stage"] = 0; 
      } 
     } 
     break; 
     } 
    } 
} 

foreach($sockets as $sock){ 
    @socket_close($sock["handle"]); 
    @fclose($sock["remote"]); 
} 
@socket_close($parent_sock); 
+0

ce n'est pas exactement ce à quoi je pensais, mais vous m'avez suggéré une bonne alternative sur apache et forking: écrire un autre serveur web dont le rôle sera d'exécuter ce fichier. Php. Je vous remercie! – n0cturnal

+0

Il y a un problème très difficile: si un proxy à une connexion hôte distant dure un certain temps, cela bloquerait l'ensemble du serveur. Il est nécessaire d'avoir un vrai serveur multi-threading. toute suggestion ? – n0cturnal

+0

C'est un serveur multithread - en PHP, pas avec des processus.Il ne le bloque pas vraiment - comme vous pouvez le voir, il y a un délai de 10 microsecondes à chaque opération de lecture de l'hôte distant. C'est très rapide. Aussi, même si la connexion réseau est très rapide, max. 128 caractères sont lus pour accélérer le tout. Cependant, certaines parties pourraient être améliorées, comme l'écriture sur le serveur distant - elle n'est actuellement pas "threadée". –