纯java实现FastCGI协议模拟nginx直接连接php-fpm通讯

java使用socket连接php-fpm,使用FastCGI协议直接通讯

FastCGI交互时序,从上到下,FCGI_BEGIN_REQUEST开始:
在这里插入图片描述
FastCGI协议:

  1. 基于cgi,添加守护进程管理cgi程序,这样就不用像原始cgi那样,每个请求来fork新建cgi进程处理,处理完销毁进程的开销。
  2. FastCGI如上交互时序,然后每条报文都有8byte报文头标识报文类型与长度
  3. FCGI_BEGIN_REQUEST,FCGI_END_REQUEST是固定长度,确定请求的开始于结束
  4. 其他params,stdin,stdout等等都是不定长,每次发最大2byte 65536长度,由报文头的contentLength为2byte决定,可以连续多发,【必须】最后跟一个【同类型】contentLength为0的空报文标识这个类型的消息发送完毕

下面的代码可以远程ip调用,php-fpm配置改为监听0.0.0.0::9000
请求的param报文中确定访问的php文件与路径,确保php有权限,不行就试试777

import java.io.*;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.Map;

public class Test {

    public static void main(String[] args) throws Exception{
        Socket client=new Socket("127.0.0.1",9000);

        InputStream in=client.getInputStream();
        OutputStream out=client.getOutputStream();

        // protocol sequence
        int request_id=5678;
        //1.begin request
        byte[] begin_request_body = new byte[8];
        begin_request_body[0]=0; // roleB1
        begin_request_body[1]= fcgi_role.FCGI_RESPONDER; //roleB0
        begin_request_body[2]=0; // flags
        byte[] begin_request = fcgi.fcgiPacket(fcgi_request_type.FCGI_BEGIN_REQUEST,request_id,begin_request_body);
        System.out.println("FCGI_BEGIN_REQUEST:\n"+ Base64.getEncoder().encodeToString(begin_request));
        //2.params
        Map<String,String> params = new LinkedHashMap<>();
        params.put("GATEWAY_INTERFACE","FastCGI/1.0");
        params.put("REQUEST_METHOD","GET");
        params.put("SCRIPT_FILENAME","/home/qianggetaba/test.php");
        params.put("SCRIPT_NAME","/test.php");
        params.put("QUERY_STRING","");
        params.put("REQUEST_URI","/test.php");
        params.put("DOCUMENT_URI","/test.php");
        params.put("SERVER_SOFTWARE","php/fcgiclient");
        params.put("REMOTE_ADDR","127.0.0.1");
        params.put("REMOTE_PORT","9985");
        params.put("SERVER_ADDR","127.0.0.1");
        params.put("SERVER_PORT","80");
        params.put("SERVER_NAME","DESKTOP-NCL22GF");
        params.put("SERVER_PROTOCOL","HTTP/1.1");
        params.put("CONTENT_TYPE","");
        params.put("CONTENT_LENGTH","0");

        System.out.println("\nparam pair base64:");
        ByteBuffer paramContainer = ByteBuffer.allocate(1024);
        for(String key:params.keySet()){
            byte[] onePair = fcgi.fcgiParam(key,params.get(key));
            paramContainer.put(onePair);

            System.out.println(key+" - "+params.get(key) +": "+Base64.getEncoder().encodeToString(onePair));
        }
        paramContainer.flip();
        byte[] fcgi_param_byte = new byte[paramContainer.remaining()];
        paramContainer.get(fcgi_param_byte);
        byte[] fcgi_params = fcgi.fcgiPacket(fcgi_request_type.FCGI_PARAMS,request_id,fcgi_param_byte);

        System.out.println("\nFCGI_PARAMS:\n"+Base64.getEncoder().encodeToString(fcgi_params));
        byte[] fcgi_params_end = fcgi.fcgiPacket(fcgi_request_type.FCGI_PARAMS,request_id,new byte[0]);
        System.out.println("\nFCGI_PARAMS end with empty content:\n"+Base64.getEncoder().encodeToString(fcgi_params_end));
        //3.stdin
        byte[] fcgi_stdin = fcgi.fcgiPacket(fcgi_request_type.FCGI_STDIN,request_id,new byte[0]);
        System.out.println("\nFCGI_STDIN:\n"+Base64.getEncoder().encodeToString(fcgi_stdin));

        ByteBuffer onePacket = ByteBuffer.allocate(begin_request.length+fcgi_params.length+fcgi_params_end.length+fcgi_stdin.length);
        onePacket.put(begin_request);
        onePacket.put(fcgi_params);
        onePacket.put(fcgi_params_end);
        onePacket.put(fcgi_stdin);

        onePacket.flip();
        System.out.println("\nall:\n" +Base64.getEncoder().encodeToString(onePacket.array()));

        out.write(onePacket.array());

        System.out.println("\noutput:");
        byte[] buf = new byte[1024];
        int len;
        while ((len=in.read(buf))!=-1){
            // decode byte[] packet by yourself
            System.out.println(new String(buf,0,len,"UTF-8"));
        }
    }
}

class fcgi {
    static byte VERSION = 1;

    public static byte[] fcgiPacket(byte type, int id, byte[] content) {
        //header
        byte[] header = new byte[8];
        header[0] = fcgi.VERSION;
        header[1] = type;
        header[2] = (byte) ((id >> 8) & 0xff);//requestIdB1
        header[3] = (byte) (id & 0xff); // requestIdB0, big endian
        header[4] = (byte) ((content.length >> 8) & 0xff);//contentLengthB1
        header[5] = (byte) (content.length & 0xff); //contentLengthB0
        header[6] = 0; //padding length
        header[7] = 0; //reserved

        //combine header and content to one byte[]
        byte[] packet = new byte[header.length + content.length];
        System.arraycopy(header, 0, packet, 0, header.length);
        System.arraycopy(content, 0, packet, header.length, content.length);

        return packet;
    }

    public static byte[] fcgiParam(String name, String value) {
        int nameLength = name.length();
        int valueLength = value.length();
        byte[] nameLen, valueLen;
        if (nameLength < 128) {
            nameLen = new byte[1];
            nameLen[0] = (byte) nameLength;
        } else {
            nameLen = new byte[4];
            nameLen[0] = (byte) ((nameLength >> 24) & 0xff);
            nameLen[1] = (byte) ((nameLength >> 16) & 0xff);
            nameLen[2] = (byte) ((nameLength >> 8) & 0xff);
            nameLen[3] = (byte) (nameLength  & 0xff);
        }
        if (valueLength < 128) {
            valueLen = new byte[1];
            valueLen[0] = (byte) valueLength;
        } else {
            valueLen = new byte[4];
            valueLen[0] = (byte) ((valueLength >> 24) & 0xff);
            valueLen[1] = (byte) ((valueLength >> 16) & 0xff);
            valueLen[2] = (byte) ((valueLength >> 8) & 0xff);
            valueLen[3] = (byte) (valueLength  & 0xff);
        }
        byte[] onePair=new byte[nameLen.length+valueLen.length+name.getBytes().length+value.getBytes().length];
        System.arraycopy(nameLen,0,
                onePair,0,nameLen.length);
        System.arraycopy(valueLen,0,
                onePair,nameLen.length,valueLen.length);
        System.arraycopy(name.getBytes(),0,
                onePair,nameLen.length+valueLen.length,name.getBytes().length);
        System.arraycopy(value.getBytes(),0,
                onePair,nameLen.length+valueLen.length+name.getBytes().length,value.getBytes().length);
        return onePair;
    }
}

class fcgi_request_type {
    static byte FCGI_BEGIN_REQUEST = 1;
    static byte FCGI_ABORT_REQUEST = 2;
    static byte FCGI_END_REQUEST = 3;
    static byte FCGI_PARAMS = 4;
    static byte FCGI_STDIN = 5;
    static byte FCGI_STDOUT = 6;
    static byte FCGI_STDERR = 7;
    static byte FCGI_DATA = 8;
    static byte FCGI_GET_VALUES = 9;
    static byte FCGI_GET_VALUES_RESULT = 10;
}

class fcgi_role{
    static byte FCGI_RESPONDER  = 1;
    static byte FCGI_AUTHORIZER = 2;
    static byte FCGI_FILTER     = 3;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值