百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

【测试平台系列】第一章 手撸压力机(九)- 封装函数

bigegpt 2024-09-22 00:41 3 浏览

到这里,大家已经知道如何通过一个web服务,来构建我们的压力机的逻辑了。上一篇我们已经通过/engin/run/testObject/接口完成了,我们对一次接口请求的调用。算是大家对这种设计思想有了初步的了解。但是我们只是完成了初步的逻辑处理,这一篇我们继续进行优化。

将我们的一些代码封装到函数和方法中,这样我们看来代码可读性更好。如果发现bug,也可以更好的进行追踪。

我们首先将header\query\body\cookie结构体进行一下变更。


// Header header
type Header struct {
        IsEnable  bool   `json:"is_enable"`  // 是否启用
        Field     string `json:"field"`      // 字段名称
        Value     string `json:"value"`      // 字段值
        FieldType string `json:"field_type"` // 字段类型
}

// Query query
type Query struct {
        IsEnable  bool   `json:"is_enable"` // 是否启用
        Field     string `json:"field"`
        Value     string `json:"value"`
        FieldType string `json:"field_type"`
}

// Cookie cookie
type Cookie struct {
        IsEnable  bool   `json:"is_enable"` // 是否启用
        Field     string `json:"field"`
        Value     string `json:"value"`
        FieldType string `json:"field_type"`
}

// Body body
type Body struct {
        Type      string     `json:"type"`
        Raw       string     `json:"raw"`
        Parameter []*VarForm `json:"parameter"`
}

// VarForm 参数表
type VarForm struct {
         IsEnable    bool        `json:"is_enable" bson:"is_enable"`
        Type        string      `json:"type" bson:"type"`
        FileBase64  []string    `json:"fileBase64"`
        Key         string      `json:"key" bson:"key"`
        Value       interface{} `json:"value" bson:"value"`
        NotNull     int64       `json:"not_null" bson:"not_null"`
        Description string      `json:"description" bson:"description"`
        FieldType   string      `json:"field_type" bson:"field_type"`
}

在global目录下新建constant/constant.go存放我们使用的常量。

// Package constant -----------------------------
// @file      : constant.go
// @author    : 被测试耽误的大厨
// @contact   : 13383088061@163.com
// @time      : 2023/7/5 18:30
// -------------------------------------------
package constant

// http请求body的类型, 由于http的body有很多类型,所以我们定义使用常量定义好这些类型
const (
        NoneMode      = "none"
        FormMode      = "multipart/form-data"
        UrlencodeMode = "application/x-www-form-urlencoded"
        JsonMode      = "application/json"
        XmlMode       = "application/xml"
        JSMode        = "application/javascript"
        PlainMode     = "text/plain"
        HtmlMode      = "text/html"
)

然后在http_request.go文件如新增如下的HttpRequest对象的方法

// 设置url
func (hr *HttpRequest) urlInit(req *fasthttp.Request) {
        // 去除url两端的空格
        hr.Url = strings.TrimSpace(hr.Url)

        // 判断url是否以http或者https开头,如果不是,则给url加上http://
        if !strings.HasPrefix(hr.Url, "http") && !strings.HasPrefix(hr.Url, "https") {
                hr.Url = fmt.Sprintf("%s%s", "http://", hr.Url)
        }

        // 添加该请求的http的url
        req.SetRequestURI(hr.Url)
}

// 设置method
func (hr *HttpRequest) methodInit(req *fasthttp.Request) {
        // 将method转换为大写,如get->GET
        hr.Method = strings.ToUpper(hr.Method)

        // 添加该请求的http方法:get、post、delete、update等等
        req.Header.SetMethod(hr.Method)
}

// 设置header
func (hr *HttpRequest) headerInit(req *fasthttp.Request) {
        for _, header := range hr.Headers {
                // 该跳header没有启用,则跳过
                if !header.IsEnable {
                        continue
                }
                // 去除header的key、value中两端的空格
                header.Field = strings.TrimSpace(header.Field)
                header.Value = strings.TrimSpace(header.Value)
                if strings.EqualFold(header.Field, "host") {
                        // 由于在header中设置host不生效,所以需要强制设置生效
                        req.SetHost(header.Value)
                        req.UseHostHeader = true
                } else {
                        req.Header.Add(header.Field, header.Value)
                }

        }

}

// 设置query
func (hr *HttpRequest) queryInit(req *fasthttp.Request) {
        // 如果query不为空则设置query
        urlQuery := req.URI().QueryArgs()
        for _, query := range hr.Querys {
                if !query.IsEnable {
                        continue
                }
                // 去除query的key中两端空格
                query.Field = strings.TrimSpace(query.Field)

                // 如果url中已经包含了该query的key则跳过
                if strings.Contains(hr.Url, query.Field+"=") {
                        continue
                }

                // 去除query的value中两端空格
                query.Value = strings.TrimSpace(query.Value)
                queryBy := []byte(query.Value)
                urlQuery.AddBytesV(query.Field, queryBy)
                hr.Url += fmt.Sprintf("&%s=%s", query.Field, query.Value)
        }

}

// 设置cookie
func (hr *HttpRequest) cookieInit(req *fasthttp.Request) {
        // 设置cookie
        for _, cookie := range hr.Cookies {
                if !cookie.IsEnable {
                        continue
                }
                // 去除header的key、value中两端的空格
                cookie.Field = strings.TrimSpace(cookie.Field)
                cookie.Value = strings.TrimSpace(cookie.Value)
                req.Header.SetCookie(cookie.Field, cookie.Value)
        }
}

// 设置body
func (hr *HttpRequest) bodyInit(req *fasthttp.Request) {
        if hr.Body == nil {
                return
        }
        switch hr.Body.Type {
        case constant.JSMode, constant.JsonMode, constant.HtmlMode, constant.PlainMode, constant.XmlMode:
                req.Header.SetContentType(hr.Body.Type)
                req.SetBody([]byte(hr.Body.Raw))
        }

}

修改HttpRequest对象的Request方法如下:

func (hr *HttpRequest) Request(response *TestObjectResponse) {

        // 使用fasthttp 协程池

        // 新建一个http请求
        req := fasthttp.AcquireRequest()
        defer fasthttp.ReleaseRequest(req)
        // 新建一个http响应接受服务端的返回
        resp := fasthttp.AcquireResponse()
        defer fasthttp.ReleaseResponse(resp)

        // 新建一个http的客户端, newHttpClient是一个方法,在下面
        client := newHttpClient(hr.HttpClientSettings)

        // 设置请求方法
        hr.methodInit(req)

        // 设置header
        hr.headerInit(req)
        // 设置query
        hr.queryInit(req)
        // 设置cookie
        hr.cookieInit(req)

        // 设置url
        hr.urlInit(req)

        // 设置body
        hr.bodyInit(req)

        // 记录开始时间
        startTime := time.Now()
        // 开始请求
        err := client.Do(req, resp)
        // 计算响应时间差值
        requestTime := time.Since(startTime)
        response.RequestTime = requestTime.Milliseconds()
        response.Code = resp.StatusCode()
        if err != nil {
                response.Response = err.Error()
                return
        }
        response.Response = string(resp.Body())

}

目前http_request.go文件全部代码如下:

package model

import (
        "crypto/tls"
        "crypto/x509"
        "fmt"
        "github.com/valyala/fasthttp"
        "io/ioutil"
        "kitchen-engine/global/constant"
        "strings"
        "time"
)

// HttpRequest http请求的结构
type HttpRequest struct {
        Url                string             `json:"url"`                  // 接口uri
        Method             string             `json:"method"`               // 接口方法,Get Post Update...
        Headers            []Header           `json:"headers"`              // 接口请求头
        Querys             []Query            `json:"querys"`               // get请求时的url
        Cookies            []Cookie           `json:"cookies"`              // cookie
        Body               *Body              `json:"body"`                 // 请求提
        HttpClientSettings HttpClientSettings `json:"http_client_settings"` // http客户端配置
}

// 设置url
func (hr *HttpRequest) urlInit(req *fasthttp.Request) {
        // 去除url两端的空格
        hr.Url = strings.TrimSpace(hr.Url)

        // 判断url是否以http或者https开头,如果不是,则给url加上http://
        if !strings.HasPrefix(hr.Url, "http") || !strings.HasPrefix(hr.Url, "https") {
                hr.Url = fmt.Sprintf("%s%s", "https://", hr.Url)
        }

        // 添加该请求的http的url
        req.SetRequestURI(hr.Url)
}

// 设置method
func (hr *HttpRequest) methodInit(req *fasthttp.Request) {
        // 将method转换为大写,如get->GET
        hr.Method = strings.ToUpper(hr.Method)

        // 添加该请求的http方法:get、post、delete、update等等
        req.Header.SetMethod(hr.Method)
}

// 设置header
func (hr *HttpRequest) headerInit(req *fasthttp.Request) {
        for _, header := range hr.Headers {
                // 该跳header没有启用,则跳过
                if !header.IsEnable {
                        continue
                }
                // 去除header的key、value中两端的空格
                header.Field = strings.TrimSpace(header.Field)
                header.Value = strings.TrimSpace(header.Value)
                if strings.EqualFold(header.Field, "host") {
                        // 由于在header中设置host不生效,所以需要强制设置生效
                        req.SetHost(header.Value)
                        req.UseHostHeader = true
                } else {
                        req.Header.Add(header.Field, header.Value)
                }

        }

}

// 设置query
func (hr *HttpRequest) queryInit(req *fasthttp.Request) {
        // 如果query不为空则设置query
        urlQuery := req.URI().QueryArgs()
        for _, query := range hr.Querys {
                if !query.IsEnable {
                        continue
                }
                // 去除query的key中两端空格
                query.Field = strings.TrimSpace(query.Field)

                // 如果url中已经包含了该query的key则跳过
                if strings.Contains(hr.Url, query.Field+"=") {
                        continue
                }

                // 去除query的value中两端空格
                query.Value = strings.TrimSpace(query.Value)
                queryBy := []byte(query.Value)
                urlQuery.AddBytesV(query.Field, queryBy)
                hr.Url += fmt.Sprintf("&%s=%s", query.Field, query.Value)
        }

}

// 设置cookie
func (hr *HttpRequest) cookieInit(req *fasthttp.Request) {
        // 设置cookie
        for _, cookie := range hr.Cookies {
                if !cookie.IsEnable {
                        continue
                }
                // 去除header的key、value中两端的空格
                cookie.Field = strings.TrimSpace(cookie.Field)
                cookie.Value = strings.TrimSpace(cookie.Value)
                req.Header.SetCookie(cookie.Field, cookie.Value)
        }
}

// 设置body
func (hr *HttpRequest) bodyInit(req *fasthttp.Request) {
        if hr.Body == nil {
                return
        }
        switch hr.Body.Type {
        case constant.JSMode, constant.JsonMode, constant.HtmlMode, constant.PlainMode, constant.XmlMode:
                req.Header.SetContentType(hr.Body.Type)
                req.SetBody([]byte(hr.Body.Raw))
        }

}

func (hr *HttpRequest) Request(response *TestObjectResponse) {

        // 使用fasthttp 协程池

        // 新建一个http请求
        req := fasthttp.AcquireRequest()
        defer fasthttp.ReleaseRequest(req)
        // 新建一个http响应接受服务端的返回
        resp := fasthttp.AcquireResponse()
        defer fasthttp.ReleaseResponse(resp)

        // 新建一个http的客户端, newHttpClient是一个方法,在下面
        client := newHttpClient(hr.HttpClientSettings)

        // 设置请求方法
        hr.methodInit(req)

        // 设置header
        hr.headerInit(req)
        // 设置query
        hr.queryInit(req)
        // 设置cookie
        hr.cookieInit(req)

        // 设置url
        hr.urlInit(req)

        // 设置body
        hr.bodyInit(req)

        // 记录开始时间
        startTime := time.Now()
        // 开始请求
        err := client.Do(req, resp)
        // 计算响应时间差值
        requestTime := time.Since(startTime)
        response.RequestTime = requestTime.Milliseconds()
        response.Code = resp.StatusCode()
        if err != nil {
                response.Response = err.Error()
                return
        }
        response.Response = string(resp.Body())

}

// 新建一个http客户端
func newHttpClient(httpClientSettings HttpClientSettings) (httpClient *fasthttp.Client) {
        // tls验证,关闭验证
        tr := &tls.Config{
                InsecureSkipVerify: true,
        }
        // 新建指针类型的客户端
        httpClient = &fasthttp.Client{}

        if httpClientSettings.Name != "" {
                httpClient.Name = httpClientSettings.Name
        }

        if httpClientSettings.NoDefaultUserAgentHeader == true {
                httpClient.NoDefaultUserAgentHeader = true
        }

        // 如果最大连接数不为0,将设置此数
        if httpClientSettings.MaxConnsPerHost != 0 {
                httpClient.MaxConnsPerHost = httpClientSettings.MaxConnsPerHost
        }

        // url不按照标准输出,按照原样输出
        if httpClientSettings.DisablePathNormalizing == true {
                httpClient.DisablePathNormalizing = true
        }
        // 请求头不按标准格式传输
        if httpClientSettings.DisableHeaderNamesNormalizing == true {
                httpClient.DisableHeaderNamesNormalizing = true
        }

        // 如果此时间不为0,那么将设置此时间。keep-alive维持此时长后将关闭。时间单位为毫秒
        if httpClientSettings.MaxConnDuration != 0 {
                httpClient.MaxConnDuration = time.Duration(httpClientSettings.MaxConnDuration) * time.Millisecond
        }

        if httpClientSettings.ReadTimeout != 0 {
                httpClient.ReadTimeout = time.Duration(httpClientSettings.ReadTimeout) * time.Millisecond
        }

        if httpClientSettings.WriteTimeout != 0 {
                httpClient.WriteTimeout = time.Duration(httpClientSettings.WriteTimeout) * time.Millisecond
        }

        // 该连接如果空闲的话,在此时间后断开。
        if httpClientSettings.MaxIdleConnDuration != 0 {
                httpClient.MaxIdleConnDuration = time.Duration(httpClientSettings.MaxIdleConnDuration) * time.Millisecond
        }

        //
        httpsTls := httpClientSettings.AdvancedOptions.Tls

        // 如果开启认证
        if httpsTls.IsVerify {
                // switch条件选择语句,如果认证类型为0:则表示双向认证,如果是1:则表示为单向认证
                switch httpsTls.VerifyType {
                case 0: // 开启双向验证
                        tr.InsecureSkipVerify = false
                        // 如果密钥文件为空则跳出switch语句
                        if httpsTls.CaCert == "" {
                                break
                        }
                        // 生成一个cert对象池
                        caCertPool := x509.NewCertPool()
                        if caCertPool == nil {
                                fmt.Println("生成CertPool失败!")
                                break
                        }

                        // 读取认证文件,读出后为字节
                        key, err := ioutil.ReadFile(httpsTls.CaCert)
                        // 如果读取错误,则跳出switch语句
                        if err != nil {
                                fmt.Println("打开密钥文件失败: ", err.Error())
                                break
                        }
                        // 将认证文件添加到cert池中
                        ok := caCertPool.AppendCertsFromPEM(key)
                        // 如果添加失败则跳出switch语句
                        if !ok {
                                fmt.Println("密钥文件错误,生成失败!!!")
                                break
                        }
                        // 将认证信息添加到客户端认证结构体
                        tr.ClientCAs = caCertPool
                case 1: // 开启单向验证,客户端验证服务端密钥
                        tr.InsecureSkipVerify = false
                }
        }

        fmt.Println("tr:    ", tr.InsecureSkipVerify)
        // 客户端认证配置项
        httpClient.TLSConfig = tr
        return
}

// Header header
type Header struct {
        IsEnable  bool   `json:"is_enable"`  // 是否启用
        Field     string `json:"field"`      // 字段名称
        Value     string `json:"value"`      // 字段值
        FieldType string `json:"field_type"` // 字段类型
}

// Query query
type Query struct {
        IsEnable  bool   `json:"is_enable"` // 是否启用
        Field     string `json:"field"`
        Value     string `json:"value"`
        FieldType string `json:"field_type"`
}

// Cookie cookie
type Cookie struct {
        IsEnable  bool   `json:"is_enable"` // 是否启用
        Field     string `json:"field"`
        Value     string `json:"value"`
        FieldType string `json:"field_type"`
}

// Body body
type Body struct {
        Type      string     `json:"type"`
        Raw       string     `json:"raw"`
        Parameter []*VarForm `json:"parameter"`
}

// VarForm 参数表
type VarForm struct {
        IsEnable    bool        `json:"is_enable" bson:"is_enable"`
        Type        string      `json:"type" bson:"type"`
        FileBase64  []string    `json:"fileBase64"`
        Key         string      `json:"key" bson:"key"`
        Value       interface{} `json:"value" bson:"value"`
        NotNull     int64       `json:"not_null" bson:"not_null"`
        Description string      `json:"description" bson:"description"`
        FieldType   string      `json:"field_type" bson:"field_type"`
}

type HttpClientSettings struct {
        //  客户端的名称,在header中的user-agent使用,通常我们默认就好
        Name string `json:"name"`

        // 默认为flase,表示User-Agent使用fasthttp的默认值
        NoDefaultUserAgentHeader bool `json:"no_default_user_agent_header"`

        // 每台主机可以建立的最大连接数。如果没有设置,则使用DefaultMaxConnsPerHost。
        MaxConnsPerHost int `json:"max_conns_per_host"`

        // 空闲的保持连接在此持续时间之后关闭。默认情况下,在DefaultMaxIdleConnDuration之后关闭空闲连接。
        // 该连接如果空闲的话,在此时间后断开。
        MaxIdleConnDuration int64 `json:"max_idle_conn_duration"`

        // Keep-alive连接在此持续时间后关闭。默认情况下,连接时间是不限制的。
        MaxConnDuration int `json:"max_conn_duration"`

        // 默认情况下,响应读取超时时间是不限制的。
        ReadTimeout int64 `json:"read_timeout"`
        // 默认情况下,请求写超时时间不受限制。
        WriteTimeout int64 `json:"write_timeout"`

        // 请求头是否按标准格式传输
        DisableHeaderNamesNormalizing bool `json:"disable_header_names_normalizing"`
        // url路径是按照原样输出,还是按照规范化输出。默认按照规范化输出
        DisablePathNormalizing bool            `json:"disable_path_normalizing"`
        AdvancedOptions        AdvancedOptions `json:"advanced_options"` // 高级选项
}

// AdvancedOptions 高级选项
type AdvancedOptions struct {
        Tls Tls `json:"tls"` // 验证设置
}

// Tls tls认证结构体
type Tls struct {
        IsVerify   bool   `json:"is_verify"`   // 是否开启验证,默认不开启,开启后需要上传密钥文件
        VerifyType int32  `json:"verify_type"` // 认证类型:0表示双向认证;1表示单向认证;默认为0
        CaCert     string `json:"ca_cert"`     // 密钥文件
}

好了,现在我们简单对http请求做了一些优化,下一步,我们开始将单个接口组成按业务流程组成场景进行使用。

相关推荐

Dify「模板转换」节点终极指南:动态文本生成进阶技巧(附代码)Jinja2引擎解析

这篇文章是关于Dify「模板转换」节点的终极指南,解析了基于Jinja2模板引擎的动态文本生成技巧,涵盖多源文本整合、知识检索结构化、动态API构建及个性化内容生成等六大应用场景,助力开发者高效利用模...

我用C#造了个AI程序员:自动调试+重构代码实战

在软件开发的世界里,调试和重构代码往往占据了程序员大量的时间。我一直梦想着能有一个智能助手,帮我处理这些繁琐的工作。于是,我决定用C#打造一个AI程序员,让它具备自动调试和重构代码的能力。系统架构设计...

公文自动排版vba代码(公文自动排版vba代码)

Sub公文自动排版()'设置页面参数(单位:厘米)WithActiveDocument.PageSetup.TopMargin=CentimetersToPoints(3.7)&#...

Anthropic最强代码神器:Claude Code系统提示词

最近,在融合Opus-4之后,ClaudeCode的整体能力直线飙升.甚至一度把曾经的最强开发工具——Cursor打的抬不起头来。无论是代码生成的准确度,还是智能补全的丝滑体验,都让人印象深...

使用 Ruff 进行 Python 代码格式化与静态检查

随着Python项目的规模增大,保持一致的代码风格和高质量的代码变得尤为重要。Ruff是一个现代、高性能、支持lint和格式化的Python工具,能帮助你快速发现并修复常见代码问题。本文...

基础语法篇:格式化输出 含完整示例代码

所谓格式化输出就是按照一定格式来输出对应的内容,在Python的语法中格式化输出包含两种:格式化符号、格式化字符串一、格式化符号常用的格式化符号包括%s(将内容转换为字符串,放入占位位置)、%d(将内...

代码整洁如诗!Keil 插件上线,一键格式化代码,告别风格混乱!

引言:代码格式不统一?你的团队还在为“括号位置”吵架吗?嵌入式开发者们,你是否经历过这些抓狂瞬间?代码风格“百花齐放”:同事的代码缩进用空格,你的用Tab,合并时冲突频发!手动调整耗时费力:为了通过C...

[信捷PLC] 信捷PLC之C函数编程(一)

前言写PLC程序,越来越觉得结构化文本编程语言(ST)给PC编程带来的便利,在处理一些数据上,可以写的更加灵活。所以,在项目PLC选型上,我都会优先选择支持结构化文本的PLC。国内有些厂商推出了一些较...

C语言-HelloWorld解析(c语言的helloworld怎么写)

使用VisualStudio2017开发工具新创建一个项目,编写第一个C语言程序。#include<stdio.h>voidmain(){printf("HelloW...

VSCode 配置 C++ 开发环境!教程详解

第一步、安装VSCode应用程序打开VSCode官网,下载对应安装包并默认安装(这里指明:安装路径可以修改)第二步、安装相关插件此时的VSCode仅仅是一个英文文本编辑器,还称不上开发工具,所以需要...

C语言进阶教程:C语言与汇编语言交互

C语言和汇编语言的交互是底层编程和性能优化中的一个重要方面。理解它们如何协同工作,可以帮助开发者更好地控制硬件、优化关键代码段以及理解编译器的行为。为什么需要在C语言中嵌入汇编?尽管C语言已经提供了相...

C语言如何处理平台相关代码(c语言的开发平台)

在进行跨平台C编程时,不可避免地会遇到需要针对不同操作系统或硬件架构编写特定代码的情况。C语言通过预处理器指令,特别是条件编译指令,为我们提供了处理平台相关代码的有效机制。最常用的就是利用预定义的宏(...

C语言:hello world(c语言helloworld代码)

环境:a.初学者建议用“啊哈C”,这款软件简单易装;b.devc.visualstdiod.Vc6.0第一行代码:#include<stdio.h>#<stdio.h&g...

C语言之编译器集合(编写c语言编译器)

C语言有多种不同的编译器,以下是常见的编译工具及其特点:一、主流C语言编译器1.GCC(GNUCompilerCollection)特点:开源、跨平台,支持多种语言(C、C++、Fortran...

适合零基础初学者学习C语言第一课教程,揭开C语言的神秘面纱

一、C语言简介我刚接触编程,首先想要学习的就是C语言,这次我就把我的感悟用我自己理解的文字表述出来,这样对刚学C语言的人来说,才是比较友好的。因为我们都没有C语言的基础,不懂啥是编程,啥事代码。我们...