std :: io :: TcpStream :: read_as_string返回一个空字符串

时间:2014-07-11 03:25:12

标签: http macros tcp rust

我想在Rust中创建一个类似curl的函数。

到目前为止,这是我使用的代码:

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)
};

问题是,如果HTTP请求格式正确,

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

不会显示任何内容,包含任何网址。

如果格式不正确,相同的代码将显示403或400错误。这应该意味着套接字var是否填充良好?

问题出在哪里?服务器或请求?

顺便说一下,代码有点太长了,我想用宏试试!但是在做的时候会引发错误,例如:

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

1 个答案:

答案 0 :(得分:3)

看起来您的请求确实 不正确,但服务器无法告诉您。

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

你知道,HTTP请求应该在两个换行符中结束,即两个\r\n序列。此外,GET请求不能具有正文(尽管大多数Web服务器可能会忽略它):

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

此请求应该有效。

对于try!,您似乎试图在main()函数或类似的东西中使用它,对吧? try!旨在在出错时从函数返回,即:

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

被重写为

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

如果您的功能未返回Result,则无效。所以你的代码看起来会更好:

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)
    }
}
顺便说一句,你也不需要明确地删除套接字或其他东西。它们超出范围时会自动销毁。我还将read_to_string()更改为read_to_end(),因为二进制文件在通过String后不正确。