GoLang net/http CookieJar头在函数间持久化

siotufzp  于 2023-08-01  发布在  Go
关注(0)|答案(1)|浏览(97)

我有一个功能,分配任务的请求,使独特的请求,也需要独特的cookie。

func DistributeTasks(url string) error {
       // redacted code 

    responseCh := make(chan utilities.WebsiteData, len(distributedCoupons))
    errorCh := make(chan error)
    var wg sync.WaitGroup
    var cookieJarMutex sync.Mutex

    for _, couponArray := range distributedCoupons {
        wg.Add(1)
        CookieJar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
        if err != nil {
            cookieJarMutex.Lock()
            errorCh <- err
            cookieJarMutex.Unlock()
        }
        go func(couponArray utilities.TaskData) {
            defer wg.Done()

            cookieJarMutex.Lock() // Acquire the mutex
            _, err = TaskToDistribute(parsedURL, couponArray, responseCh, errorCh, CookieJar)
            cookieJarMutex.Unlock() // Release the mutex

            if err != nil {
                cookieJarMutex.Lock()
                errorCh <- err
                cookieJarMutex.Unlock()
            }
        }(couponArray)

    }

    go func() {
        wg.Wait()
        close(responseCh)
        close(errorCh)
    }()

        // Channels are handled here, also redacted
}

字符串
这很好地分配了任务,但即使在创建了不同的cookie jar之后,cookie也会堆积在每个请求上(基本上是将所有cookie都放在一个jar中),并且只有在执行了几个任务之后,Web服务器才会开始返回cookie过大的错误(确认cookie超过20,000个字符)。在查看它时,每次都会从每个单独的请求中添加会话令牌(所有会话令牌都在同一个jar中)。
下面是生成HTTP请求的代码:

for _, task:= range taskData.tasks{
    // Prepare GraphQL to be sent off to test a promo code
    GraphQLQuery := CreateGraphQLQuery(taskDataV2, parsedSessionCookie, testProduct_ProductID)
    // Test Coupon
    CouponValue, err := TestCoupon(*parsedURL, GraphQLQuery, &taskData.ProxyURL, CookieJar)
    if err != nil {
        log.Error(err)
    }
    // Redacted return code
    // Wait Time Between to avoid ratelimiting
    time.Sleep(350 * time.Millisecond)
}


测试试样:

func TestCoupon(domain url.URL, GraphQLquery string, proxyURL *url.URL, CookieJar *cookiejar.Jar) (util.PromoCode, error) {
    var parsedData util.ShopifyPromoResponse

    GRAPHQL_URL := ParsedURLtoString(domain) + "/testData"
    // Make the Request
    client := &http.Client{
        Jar: CookieJar,
        Transport: &http.Transport{
            Proxy: http.ProxyURL(proxyURL),
        },
    }
    request, err := http.NewRequest(http.MethodPost, GRAPHQL_URL, strings.NewReader(GraphQLquery))
    PrintHeaders(&request.Header)
    if err != nil {
        log.Error(err)
    }
    request.Header = util.Headers
    response, err := client.Do(request)
    if err != nil {
        log.Error(err)
    }
        // Redacted return code
}


这是一个非常奇怪的问题,我无法理解!谢谢你,谢谢
我已经尝试过(如上所示)为每个函数调用使用不同的CookieJar,这并不像在函数本身中初始化CookieJar那样有效。这些函数在cookie中具有与调用它们时相同的会话令牌。(函数调用5将拥有函数调用1、2、3和4的所有会话令牌)函数调用引用从分发器调用的goroutine。

bzzcjhmw

bzzcjhmw1#

因为util.Headers不包含Cookie头(每个注解)。这应该意味着http.Client确实在为每个单独的请求使用CookieJar
您需要调试此问题:在代码中的不同位置打印出CookieJar的内容,以确认cookie没有在不同的CookieJars之间共享。
您可以打印出CookieJar的内容(名称和值),如下所示:

func printCookies(jar *cookiejar.Jar, u *url.URL) {
    for _, cookie := range jar.Cookies(u) {
        fmt.Printf("Cookie: %s = %s\n", cookie.Name, cookie.Value)
    }
}

字符串
您可以在TestCoupon函数中紧接在client.Do(request)之前和之后调用此函数。这将让您看到在请求中发送了哪些cookie,以及在请求后收到并存储在CookieJar中的cookie。
检查是否有任何请求的响应cookie设置了Domain属性。如果设置了Domain属性,则cookie对该域的所有子域也有效。
注意:您已经使用sync.Mutex来处理cookieJarMutex的错误,这很好,但真实的上没有必要使用互斥锁来保护对错误通道的访问。通道被设计成可以安全地被多个goroutine并发使用,你也可以向errorCh通道发送错误,而不需要锁定互斥锁:

if err != nil {
    errorCh <- err
}


另见“When should you use a mutex over a channel?
但是当打印出response.Request.Header(发出的请求)时,提供的cookie头长度超过20,000个字符,这意味着所有的CookirJars都以某种方式组合在一起。这在几个请求后显示,Web服务器启动错误,因为cookie太大。
我可以建议尝试为每个goroutine创建一个全新的客户端。在现有的代码中,您正在跨goroutines共享http.Client
为了隔离这一点,尝试在goroutine中创建一个带有自己的cookie jar的新客户端。

for _, couponArray := range distributedCoupons {
    wg.Add(1)

    go func(couponArray utilities.TaskData) {
        defer wg.Done()
        
        CookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})

        client := &http.Client{
            Jar: CookieJar,
            // Additional fields...
        }

        _, err := TaskToDistribute(parsedURL, couponArray, responseCh, errorCh, client)

        if err != nil {
            errorCh <- err
        }
    }(couponArray)
}


这种方法为每个goroutine创建了一个唯一的客户端和cookie jar,这可能对您的场景有所帮助。
这意味着TaskToDistribute函数需要修改为接受*http.Client作为参数,而不是*cookiejar.Jar,并使用该客户端发出HTTP请求。

func TaskToDistribute(parsedURL string, couponArray utilities.TaskData, responseCh chan utilities.WebsiteData, errorCh chan error, client *http.Client) (*utilities.WebsiteData, error) {

    for _, task := range couponArray.tasks {
        // Prepare GraphQL to be sent off to test a promo code
        GraphQLQuery := CreateGraphQLQuery(taskDataV2, parsedSessionCookie, testProduct_ProductID)
        // Test Coupon
        CouponValue, err := TestCoupon(*parsedURL, GraphQLQuery, &taskData.ProxyURL, client)
        if err != nil {
            log.Error(err)
            return nil, err
        }
        // Redacted return code
        // Wait Time Between to avoid ratelimiting
        time.Sleep(350 * time.Millisecond)
    }

    // assume we return some data here, just as an example
    return &utilities.WebsiteData{}, nil
}


TestCoupon

func TestCoupon(domain url.URL, GraphQLquery string, proxyURL *url.URL, client *http.Client) (util.PromoCode, error) {
    var parsedData util.ShopifyPromoResponse

    GRAPHQL_URL := ParsedURLtoString(domain) + "/testData"
    // Make the Request
    request, err := http.NewRequest(http.MethodPost, GRAPHQL_URL, strings.NewReader(GraphQLquery))
    PrintHeaders(&request.Header)
    if err != nil {
        log.Error(err)
        return util.PromoCode{}, err
    }
    request.Header = util.Headers
    response, err := client.Do(request)
    if err != nil {
        log.Error(err)
        return util.PromoCode{}, err
    }
        // Redacted return code
    // assume we return some promo code here, just as an example
    return util.PromoCode{}, nil
}


即使在更新它以删除互斥体并创建和使用新的HTTP客户端之后,头仍然以某种方式合并。
看看你的TestCoupon函数:

request.Header = util.Headers


这一行实际上替换了request的整个Header,这意味着它可能会丢弃之前设置的任何Cookie头(由http.Client在执行Do时设置)。客户端的cookie jar在执行请求之前自动设置jar中的Cookie头。
您可以只更新您感兴趣的特定头文件,而不是替换整个头文件,就像这样:

request, err := http.NewRequest(http.MethodPost, GRAPHQL_URL, strings.NewReader(GraphQLquery))
if err != nil {
    log.Error(err)
    return util.PromoCode{}, err
}

for k, v := range util.Headers {
    request.Header[k] = v
}


这样,您可以保留http.Client从cookie jar设置的Cookie头,以及Go的HTTP包可能自动设置的任何其他头。

相关问题