亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

go中使用curl實(shí)現(xiàn)https請(qǐng)求的示例代碼

 更新時(shí)間:2025年06月10日 10:40:27   作者:李遲  
本文主要介紹了go中使用curl實(shí)現(xiàn)https請(qǐng)求的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

之前曾經(jīng)在一個(gè) golang 工程調(diào)用 libcur 實(shí)現(xiàn) https的請(qǐng)求,當(dāng)前自測(cè)是通過(guò)的。后來(lái)遷移到另一個(gè)小系統(tǒng)出現(xiàn)段錯(cuò)誤,于是對(duì)該模塊代碼改造,并再次自測(cè)。

問(wèn)題提出

大約2年前,在某golang項(xiàng)目使用libcurl進(jìn)行https請(qǐng)求(參見(jiàn)容器《Golang實(shí)踐錄:go-curl的使用》),由于使用的docker鏡像不支持glibc,又不想重新制作,且該功能不是核心的,因此,就沒(méi)有上線?,F(xiàn)在,另一個(gè)工程也使用這個(gè)模塊,遷移代碼后自測(cè)出現(xiàn)問(wèn)題。

主要出錯(cuò)信息如下:

fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x80 addr=0x0 pc=0x7fa55160bbf4]

經(jīng)定位,在調(diào)用curl_easy_perform函數(shù)時(shí)出錯(cuò),回顧了curl一般寫法,未發(fā)現(xiàn)問(wèn)題,只好借助AI工具,一邊提問(wèn)一邊搜索。

場(chǎng)景描述

本次重提https請(qǐng)求,主要是因?yàn)槟硞€(gè)測(cè)試工程需要用https向另一個(gè)服務(wù)請(qǐng)求,該服務(wù)的證書(shū)固定了某個(gè)生產(chǎn)環(huán)境的IP,而又需要將該服務(wù)部署在測(cè)試環(huán)境,但測(cè)試環(huán)境無(wú)法使用證書(shū),因此無(wú)法驗(yàn)證一些模塊功能。為保證生產(chǎn)環(huán)境版本的正確,需要在測(cè)試環(huán)境解決證書(shū)請(qǐng)求問(wèn)題。

在此之前,自己沒(méi)有想到解決辦法,問(wèn)了AI,也沒(méi)給出滿意的回答(可能問(wèn)的方式不恰當(dāng))。實(shí)際上,借助docker容器,可以很方便解決上述問(wèn)題。

  • 創(chuàng)建docker網(wǎng)段,網(wǎng)段與生產(chǎn)環(huán)境的服務(wù)相同。
  • 利用容器部署上述測(cè)試工程和服務(wù),兩者在同一網(wǎng)段中,并且將部署服務(wù)的容器IP設(shè)置為生產(chǎn)環(huán)境的IP,這樣使得https證書(shū)可用。
  • 在測(cè)試工程請(qǐng)求時(shí),使用固定IP和固定URL請(qǐng)求。這樣能夠模擬在生產(chǎn)環(huán)境中的請(qǐng)求場(chǎng)景。

重新實(shí)現(xiàn)

核心文件代碼如下:

/*
curlApi_linux.go
使用 curl 庫(kù)封裝的請(qǐng)求接口
為減少cgo開(kāi)銷,在 C 中實(shí)現(xiàn)完整的初始化、請(qǐng)求過(guò)程,使用靜態(tài)變量減少內(nèi)存碎片
編譯、運(yùn)行的系統(tǒng)必須有l(wèi)ibcurl、libssh2等庫(kù)
*/

package mypostservice

/*
#cgo linux LDFLAGS: -lcurl
#cgo darwin LDFLAGS: -lcurl
#cgo windows LDFLAGS: -lcurl
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

static void GetDateTimeStr(char *buf, int len)
{
	int Year = 0;
	int Month = 0;
	int Day = 0;
	int Hour = 0;
	int Minute = 0;
	int Second = 0;
	long mSecond = 0;

	struct timeval theTime;
	gettimeofday(&theTime, NULL);
	struct tm * timeinfo = localtime(&(theTime.tv_sec));

	Year   = 1900 + timeinfo->tm_year;
	Month  = 1 + timeinfo->tm_mon;
	Day    = timeinfo->tm_mday;

	Hour   = timeinfo->tm_hour;
	Minute = timeinfo->tm_min;
	Second = timeinfo->tm_sec;
	mSecond = theTime.tv_usec / 1000;

	snprintf(buf, len, "%04d%02d%02d%02d%02d%02d%03ld",
		Year, Month, Day, Hour, Minute, Second, mSecond);
}

typedef struct {
    char *url;
    char *postfile;
	char *cafile;
    char *clifile;
    char *keyfile;
    int timeout;
    char *jsonStr;
    int jsonLen;
} CRequestParams;

typedef struct {
    char *data;
    size_t len;
} CResponseData;

typedef struct {
    char *respBody;       // 響應(yīng)結(jié)果
    char *filename;     // 響應(yīng)文件名
    int retcode;        // 是否成功標(biāo)志
} CReturnData;

static size_t write_callback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    CResponseData *mem = (CResponseData *)userp;

    char *ptr = realloc(mem->data, mem->len + realsize + 1);
    if(!ptr) return 0;

    mem->data = ptr;
    memcpy(&(mem->data[mem->len]), contents, realsize);
    mem->len += realsize;
    mem->data[mem->len] = 0;

    return realsize;
}

// 頭部回調(diào)函數(shù)用于獲取文件名
static size_t header_callback(void *ptr, size_t size, size_t nmemb, void *userdata) {
    char *header = strndup(ptr, size * nmemb);
    char *filename = (char *)userdata;

    // 從Content-Disposition頭部提取文件名
    if(strstr(header, "Content-Disposition") != NULL) {
        char *start = strstr(header, "filename=");
        if(start) {
            start += 9; // 跳過(guò)"filename="
            char *end = strchr(start, ';');
            if(!end) end = start + strlen(start);

            // 去除可能的引號(hào)
            if(*start == '"') start++;
            if(*(end-1) == '"') end--;

            strncpy(filename, start, end - start);
            filename[end - start] = '\0';
        }
    }

    free(header);
    return size * nmemb;
}

static CReturnData perform_request(CRequestParams *params) {
    CURL *curl;
    CURLcode res;
    CResponseData chunk = {0};
	CReturnData ret = {0};
    char resp_filename[128] = {0};  // 存儲(chǔ)文件名

    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if(!curl) {
        ret.respBody = strdup("curl_easy_init failed");
        ret.retcode = -1;
        goto cleanup;
    }

    // 設(shè)置基本選項(xiàng)
    curl_easy_setopt(curl, CURLOPT_URL, params->url); // 服務(wù)器URL
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);  // // 設(shè)置線程安全選項(xiàng)
    curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, (long)params->timeout); // 超時(shí)時(shí)間,單位為毫秒
    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, (long)params->timeout);

    // HTTPS設(shè)置
    if(strncmp(params->url, "https://", 8) == 0) {
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L);
        curl_easy_setopt(curl, CURLOPT_CAINFO, params->cafile);
        curl_easy_setopt(curl, CURLOPT_SSLCERT, params->clifile);
        curl_easy_setopt(curl, CURLOPT_SSLCERTPASSWD, "123456");
        curl_easy_setopt(curl, CURLOPT_SSLKEY, params->keyfile);
        curl_easy_setopt(curl, CURLOPT_SSLKEYPASSWD, "123456");
    }
	// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); // 調(diào)試信息
	curl_easy_setopt(curl, CURLOPT_SSLVERSION, 4);

    // 設(shè)置回調(diào)
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);

    // 構(gòu)建表單
    struct curl_httppost *formpost = NULL;
    struct curl_httppost *lastptr = NULL;

    curl_formadd(&formpost, &lastptr,
        CURLFORM_COPYNAME, "file",
        CURLFORM_BUFFER, params->postfile,
        CURLFORM_BUFFERPTR, params->jsonStr,
        CURLFORM_BUFFERLENGTH, (long)params->jsonLen,
        CURLFORM_CONTENTTYPE, "application/json",
        CURLFORM_END);

    curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);

	// 設(shè)置頭部回調(diào)以獲取文件名
    curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
    curl_easy_setopt(curl, CURLOPT_HEADERDATA, resp_filename);

    // 執(zhí)行請(qǐng)求
    res = curl_easy_perform(curl);

    if(res != CURLE_OK) {
        const char *err = curl_easy_strerror(res);
        ret.respBody = malloc(strlen(err) + 32);
        sprintf(ret.respBody, "curl_easy_perform failed: %s", err);
        ret.retcode = -2;
        goto cleanup;
    }

	// printf("debug %s %d  resp data len: \n", __func__, __LINE__, chunk.len);
    // 成功則復(fù)制結(jié)果
    if(chunk.data) {
        ret.respBody = strdup(chunk.data);
        ret.filename = strdup(resp_filename);
        ret.retcode = 0;
    } else {
        ret.respBody = strdup("No data received");
        ret.retcode = -3;
    }

cleanup:
    if(chunk.data) free(chunk.data);
    if(formpost) curl_formfree(formpost);
    if(curl) curl_easy_cleanup(curl);
    curl_global_cleanup();

    return ret;
}
*/
import "C"
import (
	"unsafe"
)

type CurlResponse struct {
	respBody string
	filename string
	retcode  int
}

type MyCURL struct {
	url, postfile, cafile, clifile, keyfile string
	timeout                                 int
}

func NewCurl() *MyCURL {
	return &MyCURL{
		timeout: 5000, // 默認(rèn)超時(shí)
	}
}

func (c *MyCURL) SetOpt(url, postfile, cafile, clientfile, keyfile string, timeout int) {
	c.url = url
	c.postfile = postfile
	c.cafile = cafile
	c.clifile = clientfile
	c.keyfile = keyfile
	c.timeout = timeout
}

func (c *MyCURL) PostFiledata(jsonStr []byte) CurlResponse {
	// 將 Go的json數(shù)據(jù)復(fù)制到C的內(nèi)存中
	cJsonStr := C.CBytes(jsonStr)
	defer C.free(cJsonStr)

	params := C.CRequestParams{
		url:      C.CString(c.url),
		postfile: C.CString(c.postfile),
		cafile:   C.CString(c.cafile),
		clifile:  C.CString(c.clifile),
		keyfile:  C.CString(c.keyfile),
		timeout:  C.int(c.timeout),
		jsonStr:  (*C.char)(cJsonStr), // 使用C分配的內(nèi)存
		jsonLen:  C.int(len(jsonStr)),
	}

	defer func() {
		C.free(unsafe.Pointer(params.url))
		C.free(unsafe.Pointer(params.cafile))
		C.free(unsafe.Pointer(params.clifile))
		C.free(unsafe.Pointer(params.keyfile))
	}()

	// 調(diào)用C函數(shù)并獲取返回結(jié)構(gòu)體
	cRet := C.perform_request(&params)
	defer func() {
		C.free(unsafe.Pointer(cRet.respBody))
		C.free(unsafe.Pointer(cRet.filename))
	}()

	// 轉(zhuǎn)換為Go結(jié)構(gòu)體
	return CurlResponse{
		respBody: C.GoString(cRet.respBody),
		filename: C.GoString(cRet.filename),
		retcode:  int(cRet.retcode),
	}
}

與上一版本對(duì)比,有如下調(diào)整:

  • #cgo linux pkg-config: libcurl改為#cgo linux LDFLAGS: -lcurl,對(duì)編譯環(huán)境較友好一些。
  • 將全局變量改為局域變量,防止多線程情況下出現(xiàn)問(wèn)題。
  • 上版本返回值使用換行符進(jìn)行解析,現(xiàn)改為返回多個(gè)值(go語(yǔ)言本身支持),代碼較友好。

測(cè)試

與curl請(qǐng)求有關(guān)的輸出信息如下:

 * About to connect() to 172.18.18.10 port 86 (#4)
 *   Trying 172.18.18.10...
 * Connected to 172.18.18.10 (172.18.18.10) port 86 (#4)
 * Initializing NSS with certpath: sql:/etc/pki/nssdb
 *   CAfile: ../../../cert/all.pem
   CApath: none
 * SSL connection using ECDHE-RSA-AES256-GCM-SHA384
 * Server certificate:
 *        subject: CN=172.18.18.10
 *        start date: 2023-02-16 08:19:00 GMT
 *        expire date: 2033-02-16 08:19:00 GMT
 > POST /mypost/foobar HTTP/1.1
 Host: 172.18.18.10:86
 Content-Length: 799
 Expect: 100-continue
 Content-Type: multipart/form-data; boundary=----------------------------258acabf1379

 < HTTP/1.1 100 Continue
 < HTTP/1.1 200 OK
 < Server: nginx/1.16.1
 < Date: Sun, 14 May 2025 18:20:48 GMT
 < Content-Type: application/json
 < Content-Length: 1083
 < Connection: keep-alive
 < Content-Disposition: form-data;filename=bar.json
 <
 * Connection #4 to host 172.18.18.10 left intact

小結(jié)

上述代碼目前只在測(cè)試環(huán)境測(cè)試,后續(xù)擇機(jī)在生產(chǎn)環(huán)境中使用。就測(cè)試結(jié)果看,應(yīng)該是沒(méi)有大問(wèn)題的。

到此這篇關(guān)于go中使用curl實(shí)現(xiàn)https請(qǐng)求的示例代碼的文章就介紹到這了,更多相關(guān)go實(shí)現(xiàn)https請(qǐng)求內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang errors包快速上手

    Golang errors包快速上手

    errors 包是用于處理錯(cuò)誤的標(biāo)準(zhǔn)庫(kù), errors 包提供的功能比較簡(jiǎn)單,使用起來(lái)非常方便,下面就來(lái)介紹一下,感興趣的可以了解一下
    2025-05-05
  • golang如何去除字符串的換行符

    golang如何去除字符串的換行符

    這篇文章主要介紹了golang如何去除字符串的換行符問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • Golang使用Gin實(shí)現(xiàn)文件上傳的示例代碼

    Golang使用Gin實(shí)現(xiàn)文件上傳的示例代碼

    本文我們主要介紹了Golang如何使用Gin實(shí)現(xiàn)文件上傳,Go標(biāo)準(zhǔn)庫(kù)net/http對(duì)文件上傳已經(jīng)提供了非常完善的支持,而Gin框架在其基礎(chǔ)上進(jìn)一步封裝,因此使用Gin開(kāi)發(fā)文件上傳功能時(shí),只需要簡(jiǎn)單幾行代碼便可以實(shí)現(xiàn),需要的朋友可以參考下
    2024-02-02
  • Golang爬蟲(chóng)及正則表達(dá)式的實(shí)現(xiàn)示例

    Golang爬蟲(chóng)及正則表達(dá)式的實(shí)現(xiàn)示例

    本文主要介紹了Golang爬蟲(chóng)及正則表達(dá)式的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • golang flag包的使用教程

    golang flag包的使用教程

    golang 的 flag 包是用于處理命令行參數(shù)的工具包,我們可以基于這個(gè)包來(lái)開(kāi)發(fā)自定義的命令行工具,下面小編就來(lái)為大家介紹一下flag包的具體使用吧
    2023-09-09
  • 圖文詳解Go程序如何編譯并運(yùn)行起來(lái)的

    圖文詳解Go程序如何編譯并運(yùn)行起來(lái)的

    Go語(yǔ)言這兩年在語(yǔ)言排行榜上的上升勢(shì)頭非常猛,Go語(yǔ)言雖然是靜態(tài)編譯型語(yǔ)言,但是它卻擁有腳本化的語(yǔ)法,下面這篇文章主要給大家介紹了關(guān)于Go程序如何編譯并運(yùn)行起來(lái)的相關(guān)資料,需要的朋友可以參考下
    2024-05-05
  • Go語(yǔ)言實(shí)現(xiàn)讀取文件的方式總結(jié)

    Go語(yǔ)言實(shí)現(xiàn)讀取文件的方式總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言實(shí)現(xiàn)讀取文件的幾個(gè)方式,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定的幫助,感興趣的小伙伴可以收藏一下
    2023-04-04
  • 詳解Go程序添加遠(yuǎn)程調(diào)用tcpdump功能

    詳解Go程序添加遠(yuǎn)程調(diào)用tcpdump功能

    這篇文章主要介紹了go程序添加遠(yuǎn)程調(diào)用tcpdump功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • Golang學(xué)習(xí)之map的用法詳解

    Golang學(xué)習(xí)之map的用法詳解

    在Golang(又稱Go語(yǔ)言)中,map是一種非常有用的數(shù)據(jù)結(jié)構(gòu),所以這篇文章小編就來(lái)帶大家一起深入了解一下map的用法,感興趣的小伙伴可以了解一下
    2023-06-06
  • 從入門到精通:Go語(yǔ)言XML數(shù)據(jù)解析指南

    從入門到精通:Go語(yǔ)言XML數(shù)據(jù)解析指南

    Go語(yǔ)言的XML包提供了強(qiáng)大的數(shù)據(jù)解析功能,讓你輕松處理各種XML格式的數(shù)據(jù),這個(gè)指南將帶你深入了解如何使用Go語(yǔ)言的XML包,快速上手XML數(shù)據(jù)解析,準(zhǔn)備好開(kāi)啟XML解析之旅了嗎?Let's?Go!
    2024-03-03

最新評(píng)論