Skip to content

Conversation

@gunir
Copy link

@gunir gunir commented Dec 5, 2025

Add extra features from this PR: bogdanfinn/tls-client#205

And also fix an issue with Firefox's Initial Window Size being small 131072 causing server to hang the request forever, this is a test script:

package main

import (
	"fmt"
	"io"
	"log"

	http "github.com/bogdanfinn/fhttp"
	tls_client "github.com/bogdanfinn/tls-client"
	"github.com/bogdanfinn/tls-client/profiles"
)

func main() {
	// 1. Use a Pre-defined Profile
	// Instead of manually building a profile (which is error-prone), start with a built-in one.
	// Firefox 133 is the latest built-in; close enough to 135 for most checks.
	// If you strictly need 135, we can inject it, but let's try a known working profile first.

	// Note: If your library version supports Firefox_135, use it.
	// Otherwise, Firefox_133 is a safer bet to avoid "unknown profile" errors.
	options := []tls_client.HttpClientOption{
		tls_client.WithTimeoutSeconds(30),
		tls_client.WithClientProfile(profiles.Firefox_135),
		tls_client.WithNotFollowRedirects(),
	}

	// 2. Create the Client
	client, err := tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
	if err != nil {
		log.Fatalf("Failed to create client: %v", err)
	}

	// 3. Define Target
	targetURL := "https://d4.alternativeto.net/x6TNSfesFlBfKuoPEALmeKj9fgKv4x_F9I0viReXaKE/rs:fill:1520:760:0/g:ce:0:0/YWJzOi8vZGlzdC9jb250ZW50LzE3NjQ4NjQ3OTY4NDMucG5n.png"

	// 4. Create Request using fhttp
	req, err := http.NewRequest(http.MethodGet, targetURL, nil)
	if err != nil {
		log.Fatalf("Failed to create request: %v", err)
	}

	// 5. Set Headers Correctly
	// Important: fhttp/tls-client will handle the pseudo-header order (:method, :path, etc.)
	// based on the profile. We just need to provide the standard headers.
	// Note the use of specific ordering via HeaderOrderKey if strict ordering is needed,
	// but tls-client often handles this for standard profiles.

	req.Header = http.Header{
		"User-Agent":                {"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0"},
		"Accept":                    {"image/avif,image/webp,*/*"},
		"Accept-Language":           {"en-US,en;q=0.5"},
		"Accept-Encoding":           {"gzip, deflate, br, zstd"}, // Added zstd which modern Firefox supports
		"Connection":                {"keep-alive"},
		"Sec-Fetch-Dest":            {"image"},
		"Sec-Fetch-Mode":            {"no-cors"},
		"Sec-Fetch-Site":            {"cross-site"},
		"Pragma":                    {"no-cache"},
		"Cache-Control":             {"no-cache"},
		"Te":                        {"trailers"},
		// Explicitly define order to match Firefox
		http.HeaderOrderKey: {
			"User-Agent",
			"Accept",
			"Accept-Language",
			"Accept-Encoding",
			"Connection",
			"Sec-Fetch-Dest",
			"Sec-Fetch-Mode",
			"Sec-Fetch-Site",
			"Pragma",
			"Cache-Control",
			"Te",
		},
        // Set pseudo-header order explicitly for fhttp requests
        http.PHeaderOrderKey: {
            ":method",
            ":path",
            ":authority",
            ":scheme",
        },
	}

	// 6. Execute
	log.Printf("Requesting %s...", targetURL)
	resp, err := client.Do(req)
	if err != nil {
		log.Fatalf("Request failed: %v", err)
	}
	defer resp.Body.Close()

	// 7. Output
	fmt.Println("--------------------------------------------------")
	fmt.Printf("Status Code: %d %s\n", resp.StatusCode, resp.Status)
	fmt.Printf("Protocol:    %s\n", resp.Proto)

	// Read body
	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Failed to read body: %v", err)
	} else {
		fmt.Printf("Body Length: %d bytes\n", len(bodyBytes))
		if len(bodyBytes) > 8 && string(bodyBytes[:4]) == "\x89PNG" {
			fmt.Println("Content Check: Valid PNG Header detected.")
		} else {
			fmt.Printf("Content Check: Not a PNG. First 20 bytes: %q\n", string(bodyBytes[:min(20, len(bodyBytes))]))
		}
	}
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

Run it:

go run tlscli.go 
2025/12/05 00:45:47 Requesting https://d4.alternativeto.net/x6TNSfesFlBfKuoPEALmeKj9fgKv4x_F9I0viReXaKE/rs:fill:1520:760:0/g:ce:0:0/YWJzOi8vZGlzdC9jb250ZW50LzE3NjQ4NjQ3OTY4NDMucG5n.png...
--------------------------------------------------
Status Code: 200 200 OK
Protocol:    HTTP/2.0
2025/12/05 00:46:17 Failed to read body: net/http: request canceled (Client.Timeout or context cancellation while reading body)

If you change profiles.Firefox_135 to profiles.Chrome_133, it works:

go run tlscli.go 
2025/12/05 00:47:06 Requesting https://d4.alternativeto.net/x6TNSfesFlBfKuoPEALmeKj9fgKv4x_F9I0viReXaKE/rs:fill:1520:760:0/g:ce:0:0/YWJzOi8vZGlzdC9jb250ZW50LzE3NjQ4NjQ3OTY4NDMucG5n.png...
--------------------------------------------------
Status Code: 200 200 OK
Protocol:    HTTP/2.0
Body Length: 371976 bytes
Content Check: Valid PNG Header detected.

gunir added 2 commits December 5, 2025 08:58
…ize of Firefox Fingeprints causing hang

Add extra logics to expose AllowHTTP, StreamID and fix small window size of Firefox Fingeprints causing hang
@gunir gunir changed the title Fhttp streamid fixsmallwindowsizehangfirefox Add AllowHTTP, set StreamID value and fix small WindowSize hanging Firefox Fingerprint Dec 5, 2025
@bogdanfinn
Copy link
Owner

@gunir when i run the above mentioned example code i gett two times:

Status Code: 200 200 OK
Protocol:    HTTP/2.0
Body Length: 34038 bytes
Content Check: Not a PNG. First 20 bytes: "RIFF\xee\x84\x00\x00WEBPVP8X\n\x00\x00\x00"

@gunir
Copy link
Author

gunir commented Dec 28, 2025

34038 bytes

The issue is this, they optimized their image to be small enough so Firefox's window size pass without issue, check my previous result, it was so much bigger:

371976 bytes

The website seems to run image compression after a while

… clients like Firefox and previous features intact

Update transport.go - Improve window size logic for small window size clients like Firefox and previous features intact
@gunir
Copy link
Author

gunir commented Dec 28, 2025

Okay, with this fix the small window size will be no more, this only affect Firefox profile when downloading huge files like MB images on some websites

It hard to reproduce this issue again because I have no other resource example to replace the OP example

Update transport.go - Added safety check and merged upstream:

```
	// ------------------------------------------------------------------
	// SAFETY: Validate flow control values don't exceed int32 max
	// RFC 7540 Section 6.9.1: Max window size is 2^31-1
	// ------------------------------------------------------------------
	if cc.connFlow > math.MaxInt32 {
		return nil, fmt.Errorf("http2: connection flow control window too large: %d (max: %d)", cc.connFlow, math.MaxInt32)
	}
	if cc.streamFlow > math.MaxInt32 {
		return nil, fmt.Errorf("http2: stream flow control window too large: %d (max: %d)", cc.streamFlow, math.MaxInt32)
	}
```
@gunir
Copy link
Author

gunir commented Dec 29, 2025

Done, I added a overflow safety check:

	// ------------------------------------------------------------------
	// SAFETY: Validate flow control values don't exceed int32 max
	// RFC 7540 Section 6.9.1: Max window size is 2^31-1
	// ------------------------------------------------------------------
	if cc.connFlow > math.MaxInt32 {
		return nil, fmt.Errorf("http2: connection flow control window too large: %d (max: %d)", cc.connFlow, math.MaxInt32)
	}
	if cc.streamFlow > math.MaxInt32 {
		return nil, fmt.Errorf("http2: stream flow control window too large: %d (max: %d)", cc.streamFlow, math.MaxInt32)
	}

And merged upstream, you can now safety merge it

@bogdanfinn
Copy link
Owner

@gunir thank you for the contribution. please double check my comments on bogdanfinn/tls-client#205

Regarding this change here i will do a few tests and try to check if nothing broke.

@gunir
Copy link
Author

gunir commented Jan 1, 2026

Should be stable enough, I've been using it for a month actively in my proxy surfing at least 5-hour a day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants