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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

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))
}

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

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go