Home > other >  How to pass data to php-fpm socket in rust using sockets
How to pass data to php-fpm socket in rust using sockets

Time:10-02

I would like to ask you for help because I have a question about unix sockets in rust. I want to try to connect to the php-fpm service in rust (I'm on a Linux machine), and I think I can connect, but how can I interact with it? Let me explain what I mean, this is my code:

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

fn main() {
    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
        }
    };
}

And the code above works perfectly. But how can I start using it? I mean maybe execute php files from the socket, get their output or the headers that the program returns and stuff like that. I've been looking for ways to do it for days, but I can't find how, I've found libraries to do it but they haven't worked for me, like this library (Just as an example, with that library I didn't know how to get the "headers" of the program). Do you know any way to start using that socket and start working with php files from the same socket? Thanks in advance.

Edit

I have been reading the link that Jmb passed me and I think I understand more how it works, but now I have a question very similar to the main one. How do I pass the data? For example, how do I tell the socket that I want to send a FCGI_BEGIN_REQUEST because, if I understand correctly, the web server is the one that has to send that, not the socket, and I think that with that I could start with the rest of the request. I'm asking the "how" to know how it should be done, because, for example, I don't think it should be done like this:

socket.write(b"FCGI_BEGIN_REQUEST")

That's just a very vague example, but I think it reflects what I'm talking about. I hope you can help me and tell me how I should write the data through the socket. And finally, if I am misunderstanding something about how they work, I would appreciate it if you also tell me so I can correct my question.

CodePudding user response:

The fastcgi protocol is specified here. If you don't want to use an existing library, you will need to implement it yourself through your socket. FastCGI is a binary protocol, in which the messages are specified in section 8 as if they were C structs. For example for FCGI_BEGIN_REQUEST, the relevant parts of the spec are:

typedef struct {
    unsigned char version;
    unsigned char type;
    unsigned char requestIdB1;
    unsigned char requestIdB0;
    unsigned char contentLengthB1;
    unsigned char contentLengthB0;
    unsigned char paddingLength;
    unsigned char reserved;
} FCGI_Header;

#define FCGI_VERSION_1           1

#define FCGI_BEGIN_REQUEST       1

typedef struct {
    unsigned char roleB1;
    unsigned char roleB0;
    unsigned char flags;
    unsigned char reserved[5];
} FCGI_BeginRequestBody;

typedef struct {
    FCGI_Header header;
    FCGI_BeginRequestBody body;
} FCGI_BeginRequestRecord;

#define FCGI_RESPONDER  1
#define FCGI_AUTHORIZER 2
#define FCGI_FILTER     3

This means that an FCGI_BEGIN_REQUEST message should be sent as a 16-byte block of data like:

const FCGI_VERSION_1: u8    = 1;
const FCGI_BEGIN_REQUEST:u8 = 1;

const FCGI_RESPONDER: u16  = 1;
const FCGI_AUTHORIZER: u16 = 2;
const FCGI_FILTER: u16     = 3;

let requestId = 0x1234;    // Change for each request
let role = 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();

The body of the request is typically sent using one or several FCGI_STDIN messages, i.e.:

let contentLength = data.len();
assert!(contentLength <= u16::MAX); // TODO: split the content if it is too large
let requestHeader = vec![
   FCGI_VERSION_1, FCGI_BEGIN_REQUEST, 
   (requestId >> 8) as u8, (requestId & 0xFF) as u8,
   (contentLength >> 8) as u8, (contentLength & 0xFF) as u8,
   0, 0,
];
socket.write_all (&requestHeader).unwrap();
socket.write_all (&data).unwrap();

The fastcgi crate implements the application side of the protocol. You can look at its source code for inspiration on how to implement the webserver side.

  • Related