【go语言http2.0代码实现】

文章通过Go语言展示了如何实现HTTP2.0的服务器和客户端。服务器部分使用http.NewServeMux()创建路由,监听HTTPS端口,客户端则配置TLS设置,包括证书和根CA,以建立安全连接。示例中还提到,为了处理运行目录问题,使用runtime.Caller获取当前目录来定位证书文件。
摘要由CSDN通过智能技术生成

go语言之http2.0

之前主要说的是基于http1.1的。然后因为http1.1存在的局限性,现在http2.0越来越流行,这里来根据go的源码来分析一下http2.0的实现。首先需要注意的是http2.0是在https的基础之上,因此推荐有https的基础或者看前面openssl https分析。首先看一下源码的实现。

Server

package main

import (
	"fmt"
	"net/http"
	"os"
	"runtime"
	"strings"
)

type textHandler struct {
	responseText string
}

func (th *textHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, th.responseText)
}

type indexHandler struct{}

func (ih *indexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	w.Write([]byte("Hello"))
}

func main() {
	mux := http.NewServeMux()
	// 获取当前的目录
	_, fp, _, _ := runtime.Caller(0)
	dir := getParentDirectory(fp)

	mux.Handle("/", &indexHandler{})

	thWelcome := &textHandler{"TextHandler !"}
	mux.Handle("/text", thWelcome)
  
    // 监听服务 server.pem是签名证书 server.key是私钥
	err := http.ListenAndServeTLS(":8084", fmt.Sprintf("%s/%s", dir, "server.pem"), fmt.Sprintf("%s/%s", dir, "server.key"), mux)
	fmt.Printf("err:%v\n", err)
}
// 获取当前的目录
func getParentDirectory(directory string) string {
	return substr(directory, 0, strings.LastIndex(directory, string(os.PathSeparator)))
}
// 截取字符串
func substr(s string, pos, length int) string {
	runes := []rune(s)
	l := pos + length
	if l > len(runes) {
		l = len(runes)
	}
	return string(runes[pos:l])
}

需要注意的是这里因为之所以使用的runtime.Caller还不是使用./server.pem和./server.key的方式去,是因为往往受到运行目录的影响,并不是当前的目录,所以使用runtime.Caller去获取当前的目录。
然后ListenAndServeTLS 是在监听https的目录,第一个就是端口,然后第二个和第三个参数分别是签名的证书和生成的私钥。

client

package main

import (
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"crypto/tls"
	"net/http"
	"net/http2"
	"log"
	"os"
	"runtime"
	"strings"
	"time"
)

func main() {
	_, fp, _, _ := runtime.Caller(0)
	dir := getParentDirectory(fp)
	// 客户端证书
	clientCertFile := fmt.Sprintf("%s/%s", dir, "client.pem")
	// 客户端私钥
	clientKeyFile := fmt.Sprintf("%s/%s", dir, "client.key")
	// CA证书
	caCertFile := clientKeyFile := fmt.Sprintf("%s/%s", dir, "ca.pem")
	var cert tls.Certificate
	var err error
	if clientCertFile != "" && clientKeyFile != "" {
	// 加载客户端的私钥和证书
		cert, err = tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
		if err != nil {
			log.Fatalf("Error creating x509 keypair from client cert file %s and client key file %s", clientCertFile, clientKeyFile)
			return
		}
	}
	// 读取ca的证书
	caCert, err := ioutil.ReadFile(caCertFile)
	if err != nil {
		fmt.Printf("Error opening cert file %s, Error: %s", caCertFile, err)
		return
	}
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)
    // 使用http2的Transport
	t := &http2.Transport{
		TLSClientConfig: &tls.Config{
			Certificates:       []tls.Certificate{cert},
			RootCAs:            caCertPool,
		},
	}
    // 调用
	client := http.Client{Transport: t, Timeout: 15 * time.Second}
	resp, err := client.Get("https://localhost:8084/")
	if err != nil {
		fmt.Printf("Failed get: %s\r\n", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("Failed reading response body: %s\r\n", err)
	}
	fmt.Printf("Client Got response %d: %s %s\r\n", resp.StatusCode, resp.Proto, string(body))
}

func getParentDirectory(directory string) string {
	return substr(directory, 0, strings.LastIndex(directory, string(os.PathSeparator)))
}
func substr(s string, pos, length int) string {
	runes := []rune(s)
	l := pos + length
	if l > len(runes) {
		l = len(runes)
	}
	return string(runes[pos:l])
}

如果生成秘钥这些比较麻烦,也可以进行跳过,简化代码,如下:

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"golang.org/x/net/http2"
)

func main() {
	t := &http2.Transport{
		TLSClientConfig: &tls.Config{
		// 指定跳过校验
			InsecureSkipVerify: true,
		},
	}

	client := http.Client{Transport: t, Timeout: 15 * time.Second}
	resp, err := client.Get("https://localhost:8084/")
	if err != nil {
		fmt.Printf("Failed get: %s\r\n", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Printf("Failed reading response body: %s\r\n", err)
	}
	fmt.Printf("Client Got response %d: %s %s\r\n", resp.StatusCode, resp.Proto, string(body))
}

这里运行一下。可以看出来已经是运行成功了。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值