C EXE调用GO DLL实现HTTPS Server

·

这是一个HTTPS Server的例子,CEXE调用GODLL的函数启动HTTPS Server,Server收到数据后会调用CEXE中的回调函数将HTTP请求数据发送给CEXE并从CEXE处获得HTTP响应数据。

需要注意的是在C和GO之间进行变量类型转换的时候,如果使用C.CString和C.CBytes转换字符串或数据,则需要调用C.free释放内存,使用C.free的需要在import “C”前的C代码中引入头文件stdlib.h,否则会报错。

package main

/*
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef void (*callback)(const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len);
static inline void call_callback(callback p,const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len)
{
	p(req_head,req_body,req_body_len,res_body_buffer,res_body_buffer_len,res_body_len);
}
*/
import "C"
import (
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"net/http"
	"runtime/cgo"
	"unsafe"
)

type HttpServer struct {
	http.Server
	remark   string
	cb       C.callback
	port     uint16
	crt_file string
	key_file string
}

func (ser *HttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	header := fmt.Sprintf("%s %s %s\r\n", r.Method, r.RequestURI, r.Proto)
	header += fmt.Sprintf("Host: %s\r\n", r.Host)
	for k, vs := range r.Header {
		header += fmt.Sprintf("%s: ", k)
		for _, v := range vs {
			header += v
		}
		header += "\r\n"
	}
	header += "\r\n"
	body, err := io.ReadAll(io.Reader(r.Body))
	if err != nil {
		http.Error(w, "Error reading request body", http.StatusInternalServerError)
		return
	}
	cHeader := C.CString(header)
	cReqBody := C.CBytes(body)
	res_body_buffer := make([]byte, 1024*1024)
	var res_body_len C.uint
	C.call_callback(ser.cb, cHeader, (*C.uchar)(cReqBody), C.uint(binary.Size(body)), (*C.uchar)(unsafe.Pointer(&res_body_buffer[0])), C.uint(binary.Size(res_body_buffer)), &res_body_len)
	slice := res_body_buffer[0:res_body_len]
	w.Write(slice)
	C.free(unsafe.Pointer(cHeader))
	C.free(unsafe.Pointer(cReqBody))
}

//export ListenAndServe
func ListenAndServe(handle C.uintptr_t) {
	h := cgo.Handle(handle)
	server := h.Value().(*HttpServer)
	server.ListenAndServeTLS(server.crt_file, server.key_file)
}

//export CreateServer
func CreateServer(remark *C.char, port C.ushort, crt_file *C.char, key_file *C.char, p C.callback) (handle C.uintptr_t) {
	server := HttpServer{}
	server.remark = C.GoString(remark)
	server.cb = p
	server.port = uint16(port)
	server.crt_file = C.GoString(crt_file)
	server.key_file = C.GoString(key_file)
	server.Addr = fmt.Sprintf(":%d", server.port)
	server.Handler = &server
	handle = C.uintptr_t(cgo.NewHandle(&server))
	return handle
}

//export ShutdownServer
func ShutdownServer(handle C.uintptr_t) {
	h := cgo.Handle(handle)
	server := h.Value().(*HttpServer)
	server.Shutdown(context.Background())
	h.Delete()
}

// //export main
func main() {

}
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include "https_server.h"

void test_callback(const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len)
{
    printf("%s\r\n",req_head);
    printf("body length: %u\r\n",req_body_len);
    printf("res_body_buffer_len: %u\r\n",res_body_buffer_len);
    strcpy(res_body_buffer,"0123456789!111111111111111111111111111111111");
    *res_body_len=10;
}

void test_callback2(const char *req_head,unsigned char *req_body,unsigned int req_body_len,unsigned char *res_body_buffer,unsigned int res_body_buffer_len,unsigned int *res_body_len)
{
    printf("%s\r\n",req_head);
    printf("body length: %u\r\n",req_body_len);
    printf("res_body_buffer_len: %u\r\n",res_body_buffer_len);
    strcpy(res_body_buffer,"9876543210!111111111111111111111111111111111");
    *res_body_len=10;
}

VOID WINAPI ListenAndServeThread(uintptr_t handle)
{
    ListenAndServe(handle);
    printf("exit ListenAndServe thread\r\n");
}


int main(int argc,char *argv[]){
    uintptr_t handle;
    uintptr_t handle2;
    handle=CreateServer("test_8000",8000,"server.crt","server.key",test_callback);
    handle2=CreateServer("test_8001",8000,"server.crt","server.key",test_callback2);
    CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ListenAndServeThread,(LPVOID)handle,0,NULL);
    //CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ListenAndServeThread,(LPVOID)handle2,0,NULL);
    getchar();
    ShutdownServer(handle);
    return 0;
}

编译命令

go build -o https_server.dll -buildmode=c-shared server.go
gcc -o main.exe main.c https_server.dll

这里也可以将GO代码编译成C能用的静态库文件

go build -o https_server_static.lib -buildmode=c-archive server.go

生成自签名证书

openssl genrsa -out server.key 2048
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注