我如何在Rust中解析一个原始的http请求?

uplii1fm  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(171)

我想做的是解析一个原始的http响应。我从一个PHP文件中得到http响应,例如我的php文件有以下内容:

<?php

echo "Hello";

header("Location: form.php");

?>

它会返回以下响应:

Status: 302 Found\r\nX-Powered-By: PHP/8.1.11\r\nLocation: form.php\r\nContent-type: text/html; charset=UTF-8\r\n\r\nHello

到目前为止,一切都很好。但是如果文件有错误,比如像这样的语法错误:

<?php

echo "Hello"//;

header("Location: form.php");

?>

我得到这样的回应:

PHP message: PHP Parse error:  syntax error, unexpected identifier \"header\", expecting \",\" or \";\" in/var/www/public/index.php on line 5Status: 500 Internal Server Error\r\nX-Powered-By: PHP/8.1.11\r\nContent-type: text/html; charset=UTF-8\r\n\r\n

正如您所看到的,响应明显不同,头的顺序也不同(我想澄清一下,我使用“{:?}"打印了这个,所以一些"后面的\实际上不是响应的一部分)。因此,这导致了一些库(如this)返回错误,所以我决定自己解析响应。
那么,我该如何正确地解析响应呢?我该如何处理它呢?我应该使用正则表达式之类的东西吗?提前感谢。
为了澄清,我使用FastCGI协议获得http响应,所以我不能用其他任何方式获得响应。我还想说,我不是在寻找解决我在库中遇到的错误的方法,我只是想知道如何自己解析http响应。

编辑

你们可以看到我是如何得到响应的我会给你们看我的rust代码,是这样的:

use std::os::unix::net::{UnixStream};
use std::io::{Read, Write};
use std::str;

fn main() {
    const FCGI_VERSION_1: u8    = 1;

    const FCGI_BEGIN_REQUEST:u8 = 1;
    const FCGI_END_REQUEST: u8  = 3;
    const FCGI_STDIN: u8        = 5;
    const FCGI_STDOUT: u8       = 6;
    const FCGI_STDERR: u8       = 7;

    const FCGI_RESPONDER: u16  = 1;

    const FCGI_PARAMS: u8 = 4;

    let socket_path = "/run/php-fpm/php-fpm.sock";

    let mut socket = match UnixStream::connect(socket_path) {
        Ok(sock) => sock,
        Err(e) => {
            println!("Couldn't connect: {e:?}");
            return
        }
    };

    let requestId: u16 = 1;

    let role: u16 = FCGI_RESPONDER;

    let beginRequest = vec![
       // FCGI_Header
       FCGI_VERSION_1, FCGI_BEGIN_REQUEST,
       (requestId >> 8) as u8, (requestId & 0xFF) as u8,
       0x00, 0x08, // This is the size of `FCGI_BeginRequestBody`
       0, 0,
       // FCGI_BeginRequestBody
       (role >> 8) as u8, (role & 0xFF) as u8,
       0, // Flags
       0, 0, 0, 0, 0, // Reserved
    ];

    socket.write_all(&beginRequest).unwrap();

    // write the FCGI_PARAMS

    let param1_name = "SCRIPT_FILENAME".as_bytes();
    let param1_value = "/home/davebook-arch/projects/so/index.php".as_bytes();
    let lengths1 = [ param1_name.len() as u8, param1_value.len() as u8 ];
    let params1_len: u16 = (param1_name.len() + param1_value.len() + lengths1.len()) as u16;

    let param2_name = b"REQUEST_METHOD";
    let param2_value = b"GET";
    let lengths2 = [ param2_name.len() as u8, param2_value.len() as u8 ];
    let params2_len: u16 = (param2_name.len() + param2_value.len() + lengths2.len()) as u16;

    let params_len = params1_len + params2_len;
    let paramsRequest = vec![
       FCGI_VERSION_1, FCGI_PARAMS,
       (requestId >> 8) as u8, (requestId & 0xFF) as u8,
       (params_len >> 8) as u8, (params_len & 0xFF) as u8,
       0, 0,
    ];

    socket.write_all (&paramsRequest).unwrap();
    socket.write_all (&lengths1).unwrap();
    socket.write_all (param1_name).unwrap();
    socket.write_all (param1_value).unwrap();
    socket.write_all (&lengths2).unwrap();
    socket.write_all (param2_name).unwrap();
    socket.write_all (param2_value).unwrap();

    let mut stdout: String = String::new();

    // get the response
    let requestHeader = vec![
        FCGI_VERSION_1, FCGI_STDOUT,
        (requestId >> 8) as u8, (requestId & 0xFF) as u8,
        0, 0,
        0, 0,
    ];

    socket.write_all(&requestHeader).unwrap();

    loop {
        // read the response header
        let mut responseHeader = [0u8; 8];
        socket.read_exact (&mut responseHeader).unwrap();

        if responseHeader[1] != FCGI_STDOUT && responseHeader[1] != FCGI_STDERR{

            if responseHeader[1] == FCGI_END_REQUEST {
                println!("FCGI_END_REQUEST: {:?}", responseHeader);
                break;
            } else {
                println!("NOT FCGI_END_REQUEST: {}", responseHeader[1]);
                break;
            }
        }

        // read the body
        let responseLength = ((responseHeader[4] as usize) << 8) | (responseHeader[5] as usize);

        let mut responseBody = vec![0; responseLength];

        socket.read_exact (&mut responseBody).unwrap();

        stdout.push_str(&String::from_utf8_lossy(&responseBody));

        // read the padding
        let mut pad = vec![0; responseHeader[6] as usize];

        socket.read_exact (&mut pad).unwrap();
    }

    println!("Output: {:?}", stdout);
}

完整和详细的上下文在this问题中(在我对那个问题的回答中有更多的细节)。在我看来,最好向你展示这个问题,这样你就可以看到我是如何详细解释那里的一切的。

编辑2

我已经执行了这个问题答案中的代码。它运行得很好,但只是在第一种情况下,在第二种情况下,我该怎么办?我是收集错误数据的那个人吗?或者我该如何解析http响应?

ee7vknir

ee7vknir1#

我从来没有在Rust做过这个,我做Rust的时间也不长,不要把这个当作权威的回答什么的,但是做

fn main(){
    let raw="Status: 302 Found\r\nX-Powered-By: PHP/8.1.11\r\nLocation: form.php\r\nContent-type: text/html; charset=UTF-8\r\n\r\nHello\r\n\r\nboody contains separator, account for that";
    let raw_split = raw.splitn(2, "\r\n\r\n").collect::<Vec<&str>>();
    let headers = raw_split[0].split("\r\n").collect::<Vec<&str>>();
    let body = raw_split[1];
    let mut headers_map = std::collections::HashMap::new();
    for header in headers {
        let header = header.splitn(2,": ").collect::<Vec<&str>>();
        headers_map.insert(header[0], header[1]);
    }
    println!("{:?}", headers_map);
    println!("{}", body);
}

给了我

{"X-Powered-By": "PHP/8.1.11", "Status": "302 Found", "Location": "form.php", "Content-type": "text/html; charset=UTF-8"}
Hello

boody contains separator, account for that

并将原始数据转换为

let raw = "PHP message: PHP Parse error:  syntax error, unexpected identifier \"header\", expecting \",\" or \";\" in/var/www/public/index.php on line 5\r\nStatus: 500 Internal Server Error\r\nX-Powered-By: PHP/8.1.11\r\nContent-type: text/html; charset=UTF-8\r\n\r\n";

给出:

{"PHP message": "PHP Parse error:  syntax error, unexpected identifier \"header\", expecting \",\" or \";\" in/var/www/public/index.php on line 5", "X-Powered-By": "PHP/8.1.11", "Content-type": "text/html; charset=UTF-8", "Status": "500 Internal Server Error"}

因此您可以使用

if headers_map.contains_key("PHP message"){
        what to do if its a PHP message?
    }

检查它是否是“PHP消息”,在本例中,PHP消息是PHP Parse error: syntax error, unexpected identifier \"header\", expecting \",\" or \";\" in/var/www/public/index.php on line 5

相关问题