2014-07-11 7 views
3

Je voudrais créer une fonction de type curl dans Rust.std :: io :: TcpStream :: read_as_string renvoie une chaîne vide

Jusqu'à présent, voici le code que j'utilise:

match Url::parse(url) { 
    Ok(u) => { 
     match TcpStream::connect(u.host.as_slice(), 80) { 
      Ok(mut socket) => { 
       let req = format!(
        "GET {:s} HTTP/1.1\r\nHost: {:s}\r\nAccept: */*\r\nContent-Length: 0\r\nContent-Type: aplication/x-www-form-urlencoded\r\n", 
        u.path.path.as_slice(), u.host 
       ); 
       socket.write(req.as_bytes()); 
       match socket.read_to_string() { 
        Ok(res) => println!("{}", res.as_slice()), 
        Err(e) => fail!("Error: {}", e) 
       }; 
       drop(socket); 
      }, 
      Err(e) => fail!("Error: {}", e) 
     }; 
    }, 
    Err(e) => fail!("Error: {}", e) 
}; 

Le problème est, si la requête HTTP est correctement formaté,

println!("{}", res.as_slice()) 

n'affichera rien, avec une URL.

Si le format n'est pas correct, le même code affichera 403 ou 400 erreurs. Cela signifie-t-il que la socket var est bien remplie?

D'où le problème pourrait-il provenir? Le serveur ou la requête? Par ailleurs, le code étant un peu trop long, j'aimerais utiliser la macro essayer! mais elle soulève une erreur en faisant, par exemple:

try!(TcpStream::connect(u.host.as_slice(), 80) 
+1

Revendiquer HTTP/1.0 pour une chose si simple est probablement OK. Réclamer HTTP/1.1 ne demande que des problèmes, à moins que vous n'ayez implémenté un certain nombre de choses supplémentaires (par exemple l'encodage de transfert deflate et chunked). Les serveurs sont entièrement libres de vous envoyer des choses que vous ne pourrez pas comprendre. –

+0

veuillez également signaler l'erreur que vous obtenez en utilisant l'essai! macro –

Répondre

3

Il ressemble à votre demande vraiment est incorrecte, mais pas d'une façon dont le serveur pourrait vous dire.

GET {:s} HTTP/1.1\r\nHost: {:s}\r\nAccept: */*\r\nContent-Length: 0\r\nContent-Type: aplication/x-www-form-urlencoded\r\n 

Vous voyez, une requête HTTP est censé être terminé dans deux nouvelles lignes, soit deux séquences \r\n. Aussi GET demande ne peut pas avoir le corps (bien que la plupart des serveurs Web ignorent sans doute que):

GET {:s} HTTP/1.1\r\nHost: {:s}\r\nAccept: */*\r\n\r\n 

Cette demande devrait fonctionner.

Comme pour try!, il semble que vous essayez de l'utiliser dans une fonction main() ou quelque chose comme ça, non? try! est destiné à revenir de la fonction en cas d'erreur, c'est ceci:

try!(TcpStream::connect(u.host.as_slice(), 80)) 

est réécrite en

match TcpStream::connect(u.host.as_slice(), 80) { 
    Ok(s) => s, 
    Err(e) => return Err(e) 
} 

Si votre fonction ne retourne pas Result, il ne fonctionnera pas. Donc, votre code sera mieux comme ceci:

extern crate url; 

use std::io::{IoResult, File, TcpStream}; 

use url::Url; 

fn download_file(url: &Url, file: &Path) -> IoResult<()> { 
    let mut socket = try!(TcpStream::connect(url.host.as_slice(), 80)); 
    let req = format!(
     "GET {:s} HTTP/1.1\r\nHost: {:s}\r\nAccept: */*\r\n\r\n", 
     url.path.path.as_slice(), url.host 
    ); 

    try!(socket.write(req.as_bytes())); 

    let res = try!(socket.read_to_end()); 

    let mut file = File::create(file); 
    try!(file.write(res.as_slice())); 

    Ok(()) 
} 

fn main() { 
    let url = "http://kitten.jpg.to/"; 
    match Url::parse(url) { 
     Ok(url) => match download_file(&url, &Path::new("/tmp/kitten_link.http")) { 
      Ok(_) => println!("Download successful"), 
      Err(e) => fail!("Error: {}", e) 
     }, 
     Err(e) => fail!("Error: {}", e) 
    } 
} 

BTW, vous pouvez aussi ne pas besoin de déposer des prises ou d'autres choses explicitement. Ils sont détruits automatiquement lorsqu'ils sortent du champ d'application. J'ai également changé read_to_string() en read_to_end() parce que les fichiers binaires ne seront pas corrects après avoir passé un String.

+0

Ca fonctionne comme un charme, merci! –

Questions connexes