golang HTTP2 https测试POST变GET问题小记

概述

因为工作需要协助修改某个golang程序,添加双向认证。但是在调整的过程遇到一个HTTP POST请求变成GET诡异的问题,最后各种搜索,总算解决,博文记录,用于备忘。

代码

服务端

因工作内容,代码有删减,以下样例

package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"mime"
	"mime/multipart"
	"net/http"
	"os"
	"strings"
	"time"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/...", handleToken)
	mux.HandleFunc("/...", handleHandshake)
	mux.HandleFunc("/...", handleData)


	caCert, err := os.ReadFile("certs/root_cert.pem")
	if err != nil {
		log.Fatalf("Reading server certificate: %s", err)
	}

	caCertPool := x509.NewCertPool()
	if !caCertPool.AppendCertsFromPEM(caCert) {
		fmt.Print("AppendCertsFromPEM failured!!!")
	}

	// Create TLS configuration with the certificate of the server
	tlsConfig := &tls.Config{
		ClientCAs:  caCertPool, //载入验证客户端证书的根证书
		ClientAuth: tls.RequireAndVerifyClientCert, //设置需要客户端证书
	}

	h1s := &http.Server{
		Addr:      ":8008",
		Handler:   mux,
		TLSConfig: tlsConfig,
	}
	log.Fatal(h1s.ListenAndServeTLS("certs/server_cert.pem", "certs/server_key.pem"))
}

...
...
...

func handleToken(w http.ResponseWriter, r *http.Request) {
	log.Printf("Token......%s", r.Method)

	switch r.Method {
	case http.MethodPost:
		...
	case http.MethodDelete:
		...
	default:
		http.Error(w, "400 Unsupport Method", http.StatusBadRequest)
	}

}

func handleHandshake(w http.ResponseWriter, r *http.Request) {
	.....
}

客户端



func GetToken(client http.Client) error {
	authData := ...
	jsonStr, _ := json.Marshal(authData)

	resp, err := client.Post(srv+url, "application/json", bytes.NewBuffer(jsonStr))
	if err != nil {
		log.Printf("Failed get token: err:%s \n", err)
		return err
	}
	defer resp.Body.Close()
	....
	
	return nil
}

func main() {
	... 
	
	client := http.Client{}

	// Create a pool with the server certificate since it is not signed
	// by a known CA
	caCert, err := os.ReadFile("certs/root_cert.pem")
	if err != nil {
		log.Fatalf("Reading server certificate: %s", err)
	}

	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	clientCert, err := tls.LoadX509KeyPair("certs/client_cert.pem", "certs/client_key.pem")
	if err != nil {
		panic(err)
	}

	// Create TLS configuration with the certificate of the server
	tlsConfig := &tls.Config{
		RootCAs:            caCertPool,
		Certificates:       []tls.Certificate{clientCert},
		InsecureSkipVerify: true, //真实证书的情况下,需要删除该行,自签名可以保留
	}

	client.Transport = &http2.Transport{
		TLSClientConfig: tlsConfig}
		
	
	GetToken(client)
}

Q&A

Q:GetToken发起请求Post请求在服务端收到变成了GET
A:后面经过排查,根源是URL拼接的时候,中间多了一个“/”字符串,原本URL https://127.0.0.1:8008/api/token错误拼接成https://127.0.0.1:8008//api/token,从而在服务器端触发了301响应,go的http client在处理301响应的时候,将POST方法改成GET重新提交,所以导致服务端收到的请求方法是GET而不是POST

对应代码位于client.go

// redirectBehavior describes what should happen when the
// client encounters a 3xx status code from the server.
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
	switch resp.StatusCode {
	case 301, 302, 303:
		redirectMethod = reqMethod
		shouldRedirect = true
		includeBody = false

		// RFC 2616 allowed automatic redirection only with GET and
		// HEAD requests. RFC 7231 lifts this restriction, but we still
		// restrict other methods to GET to maintain compatibility.
		// See Issue 18570.
		if reqMethod != "GET" && reqMethod != "HEAD" {
			redirectMethod = "GET"
		}

Q:go run 执行程序,报以下错误

/usr/local/go/pkg/tool/linux_arm64/link: running gcc failed: exit status 1
/usr/bin/ld: 找不到 -l***
collect2: 错误:ld 返回 1

A:因为程序内部通过cgo调用了第三方的动态库,而该动态库又没有在ldconfig或者系统默认目录下,所以找不到对应库导致编译出错,可以通过以下命令临时指定并执行

CGO_LDFLAGS="-L第三方动态库所在路径 -O2 -g" go run test.go

参考链接

Http Post请求被强制转换为Http Get请求
自签名根证书、中间证书、服务器证书生成流程详解

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜晓码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值