首页 > 其他分享 >Golang URL query contains semicolon 报错解决方案

Golang URL query contains semicolon 报错解决方案

时间:2023-05-16 16:24:12浏览次数:58  
标签:http semicolon RawQuery URL req 报错 func query

 报错信息

http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192

 高版本http废除了分号做分隔符,会在http库中做报警输出,基础库代码如下:

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }

    if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {
        var allowQuerySemicolonsInUse int32
        req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() {
            atomic.StoreInt32(&allowQuerySemicolonsInUse, 1)
        }))
        defer func() {
            if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 {
                sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")
            }
        }()
    }

    handler.ServeHTTP(rw, req)
}

 

解决方法有两种

 

1、http中不要用分号做分隔符,不要含有分号

2、调用库函数http.AllowQuerySemicolons()允许分号,解除报错

由于项目历史版本缘故,无法完全做到第1点,需要做第2点来补充。如果直接调用http.AllowQuerySemicolons,发现其就是简单粗暴将分号直接替换成&(代码如下),从而可能会引发后续http body解析报错

func AllowQuerySemicolons(h Handler) Handler {
    return HandlerFunc(func(w ResponseWriter, r *Request) {
        if silenceSemicolonsWarning, ok := r.Context().Value(silenceSemWarnContextKey).(func()); ok {
            silenceSemicolonsWarning()
        }
        if strings.Contains(r.URL.RawQuery, ";") {
            r2 := new(Request)
            *r2 = *r
            r2.URL = new(url.URL)
            *r2.URL = *r.URL
            r2.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&")
            h.ServeHTTP(w, r2)
        } else {
            h.ServeHTTP(w, r)
        }
    })
}

 

最终解决方案

1、将http内容中的分号替换,并记录位置

2、调用http.AllowQuerySemicolons,解除报警输出

3、将1中被替换的分号还原

4、调用handler处理函数,解析参数,进行业务逻辑

最终代码如下:

package main

import (
    "context"
    "fmt"
    "net/http"
    "regexp"
    "strings"
    "time"
    "unsafe"
)

// BytesToString converts byte slice to string.
func BytesToString(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}

func MarkQuerySemicolons(h http.Handler) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if strings.Contains(r.URL.RawQuery, ";") {
            // 正则匹配 将原url中的';'索引位置标记放到context中
            semicolonsIndexes := regexp.MustCompile(";").FindAllStringIndex(r.URL.RawQuery, -1)
            // 索引信息放到context中 在具体处理方法前再根据context中存的下标还原';'的位置
            r = r.WithContext(context.WithValue(r.Context(), "semicolonMarks", semicolonsIndexes))
            r.URL.RawQuery = strings.ReplaceAll(r.URL.RawQuery, ";", "&")
            h.ServeHTTP(w, r)
        } else {
            h.ServeHTTP(w, r)
        }
    }
}

func RecoverRawQuerySemicolons(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        marksContextV := r.Context().Value("semicolonMarks")
        semicolonsIndexes, ok := marksContextV.([][]int)
        if ok {
            urlReduceSemicolons := []byte(r.URL.RawQuery)
            for _, index := range semicolonsIndexes {
                if len(index) != 0 {
                    urlReduceSemicolons[index[0]] = ';'
                }
            }
            r.URL.RawQuery = BytesToString(urlReduceSemicolons)
        }
        next.ServeHTTP(w, r)
    })
}

func HandleRawQuerySemicolons(next http.Handler) http.HandlerFunc {
    return MarkQuerySemicolons(http.AllowQuerySemicolons(RecoverRawQuerySemicolons(next)))
}

func Health(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.URL.Path, r.URL.RawQuery)
    w.Write([]byte("ok"))
}

func main() {
    http.HandleFunc("/health", HandleRawQuerySemicolons(http.HandlerFunc(Health)))
    http.HandleFunc("/health2", Health)
    s := &http.Server{
        Addr:           ":10099",
        Handler:        nil,
        ReadTimeout:    time.Second,
        WriteTimeout:   time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    if err := s.ListenAndServe(); err != nil {
        fmt.Println(err.Error())
    }
}

 

测试情况

分别请求

http://127.0.0.1:10099/health?a=1;b=2

http://127.0.0.1:10099/health2?a=1;b=2

Health中输出如下: 

/health a=1;b=2
/health2 a=1;b=2
2023/05/16 15:17:18 http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192

 测试OK。直接调用Health,引发了报错信息。用HandleRawQuerySemicolons包装的没有报错,且url内容正常。

参考文档

https://www.cnblogs.com/tinfy/archive/2023/01/13/17049049.html

标签:http,semicolon,RawQuery,URL,req,报错,func,query
From: https://www.cnblogs.com/langit/p/17405972.html

相关文章

  • 【Mysql】update时报错:Lock wait timeout exceeded; try restarting transaction
    1、查找正在执行的事务SELECT*FROMinformation_schema.INNODB_TRX;2、杀掉LOCKWAIT的进程trx_mysql_thread_id:进程idkillxxxOR3、查看所有进程SHOWPROCESSLIST;4、杀掉异常的进程,比如Sleep状态、影响你正在操作的业务的进程id:进程idkillxxx......
  • 【遇到的问题】com.mysql.jdbc.MysqlDataTruncation 报错
    com.mysql.jdbc.MysqlDataTruncation:Datatruncation:Incorrectdatevalue:‘null’forcolum‘time’atrow1发现代码执行过程中数据存储失败,但是在数据库中执行语句又可以成功。在网络上搜索解决方案,但都解决无果:以为是String类型和Date类型转换的问题mysql-......
  • java的urlRewriter
    现在UrlRewriter技术有两个技术平台的,一个就是在Java方向的,另一个就是.NET方向的。今天我们讲的是Java方向的应用。首先让我们了解它的工作原理,说白了它就是一个简单的过滤器(Filter),看看源码你就会很快的明白,它就是通过我们在jsp中常用的两个方法实现的forward(),sendRedirect().下......
  • Python_报错:curl: (7) Failed to connect to raw.githubusercontent.com port 443: Op
    解决:https://blog.csdn.net/Jimmmyking/article/details/126105788作为mac的用户,如果你还没安装Homebrew那真的就太遗憾了,应为其真的很好用,然后安装Homebrew有时候有不是那么简单,会出现很多奇奇怪怪的错误,如下是我本人第一次安装就成功,其重要用的是中科大的brew主体,使用这个只需......
  • JAVA反序列化-URLDNS分析
    目录0x01URLDNS0x02利用链分析本文基于P大的《java安全漫谈》环境jdk1.7urldns是学习JAVA反序列化的入门利用链0x01URLDNSURLDNS就是ysoserial中⼀个利⽤链的名字,但准确来说,这个其实不能称作“利⽤链”。因为其参数不是⼀个可以“利⽤”的命令,⽽仅为⼀个URL,其能触发的结......
  • dotnet6 项目在Docker部署报错 The type initializer for 'Gdip' threw an exception.
    asp.netcore部署在docker中,使用了System.Drawing.Common包,在容器中使用时报错“Thetypeinitializerfor'Gdip'threwanexception.”原因:因为System.Drawing.Common在跨平台的兼容性不够理想,官方设置为后续版本只能在Windows平台使用,默认是不开启的,需要在运行时配置中开启......
  • vue报错EISDIR: illegal operation on a directory, read解决
    报错:EISDIR:illegaloperationonadirectory,readInternalservererror:EISDIR:illegaloperationonadirectory,read原因:引入子组件时,子组件是xxx/文件夹名/index.vue,引入只写到了xxx/文件夹名解决:引入子组件时,路径写到xxx/文件夹名/index.vue......
  • 3D打印报错!! {"code":"key111", "msg": "Extrude below minimum temp
    问题:!!{"code":"key111","msg":"Extrudebelowminimumtemp 解决办法:在配置文件中修改:增加:min_extrude_temp:0......
  • 【jmeter】生成测试报告报错
    1、生成jmeter报告报错Consumerfailedwithmessage:Beginsize0isnotequaltofixedsize5 2、jmeter的jmx格式的聚合报告转换成html时报错Anerroroccurred:Reportgenerationrequirescsvoutputformat,check'jmeter.save.saveservice.output_format'proper......
  • 报错问题:谷粒商城Access to XMLHttpRequest at 'http://localhost:88/api/sys/login'
    大概在P46P47,跟着配置后出现问题AccesstoXMLHttpRequestat'http://localhost:88/api/sys/login'fromorigin'http://localhost:8001'hasbeenblockedbyCORSpolicy: 上网查了一下,说是跨域的问题,检查了一会,有人说是nacos的命名空间的问题,也有人说是版本上的问题,大多......