[GH-ISSUE #1192] Connection interuption results in partially written content body #936

Closed
opened 2026-05-05 12:35:41 -06:00 by gitea-mirror · 13 comments
Owner

Originally created by @jsheely on GitHub (Apr 10, 2019).
Original GitHub issue: https://github.com/fatedier/frp/issues/1192

Originally assigned to: @fatedier, @yuyulei on GitHub.

What version of frp are you using (./frpc -v or ./frps -v)?
v0.25.3

What operating system and processor architecture are you using (go env)?
Windows

Steps to reproduce the issue:
Client -> FRP Server -> FRP Client -> API
Client -> FRP Server -> (Internet Outage) -> FRP Client -> API

Describe the results you received:
If the connection between FRP Server and FRP Client is broken (internet outage) while content body is streaming FRP will close the partially completed stream successfully and your result will be half complete

Describe the results you expected:
If the connection between FRP Server and FRP Client is interupted in the middle of the stream the connection from FRP Server to Client should be disconnected. This will result in an "Error while copying content to a stream" in most standard HTTP clients so the error can be handled gracefully

Originally created by @jsheely on GitHub (Apr 10, 2019). Original GitHub issue: https://github.com/fatedier/frp/issues/1192 Originally assigned to: @fatedier, @yuyulei on GitHub. **What version of frp are you using (./frpc -v or ./frps -v)?** v0.25.3 **What operating system and processor architecture are you using (`go env`)?** Windows **Steps to reproduce the issue:** Client -> FRP Server -> FRP Client -> API Client -> FRP Server -> (Internet Outage) -> FRP Client -> API **Describe the results you received:** If the connection between FRP Server and FRP Client is broken (internet outage) while content body is streaming FRP will close the partially completed stream successfully and your result will be half complete **Describe the results you expected:** If the connection between FRP Server and FRP Client is interupted in the middle of the stream the connection from FRP Server to Client should be disconnected. This will result in an "Error while copying content to a stream" in most standard HTTP clients so the error can be handled gracefully
gitea-mirror 2026-05-05 12:35:41 -06:00
  • closed this issue
  • added the
    question
    label
Author
Owner

@fatedier commented on GitHub (Apr 12, 2019):

I'm not sure what is the problem.

Internet Outage can be detect by heartbeat between frps and frpc. So frpc will close old connection and create a new one.

HTTP clients should have their own timeout way to prevent this problem?

<!-- gh-comment-id:482478429 --> @fatedier commented on GitHub (Apr 12, 2019): I'm not sure what is the problem. Internet Outage can be detect by heartbeat between frps and frpc. So frpc will close old connection and create a new one. HTTP clients should have their own timeout way to prevent this problem?
Author
Owner

@jsheely commented on GitHub (Apr 12, 2019):

@fatedier So the issue is that if the connection goes down while a file is being streamed FRP ends the stream succesfully as if everything was fine and that was the end of the file. Even if only half the file got transmitted

The http client does not know that FRP Server lost connection to FRP Client in the middle of the stream because FRP server is returning successfully.

The HTTP Client at this point doesn't know the difference between a stream ending because it has all the data or that the stream ended prematurely and it only has half the data because FRP in both cases flushes the stream the same way.

Hope that helps explain the issue more.

<!-- gh-comment-id:482585627 --> @jsheely commented on GitHub (Apr 12, 2019): @fatedier So the issue is that if the connection goes down while a file is being streamed FRP ends the stream succesfully as if everything was fine and that was the end of the file. Even if only half the file got transmitted The http client does not know that FRP Server lost connection to FRP Client in the middle of the stream because FRP server is returning successfully. The HTTP Client at this point doesn't know the difference between a stream ending because it has all the data or that the stream ended prematurely and it only has half the data because FRP in both cases flushes the stream the same way. Hope that helps explain the issue more.
Author
Owner

@fatedier commented on GitHub (Apr 12, 2019):

I have different ideas.

Why does HTTP client think it has the complete file content while the length of bytes it read is not equal with Content-Length ?

The http client does not know that FRP Server lost connection to FRP Client in the middle of the stream because FRP server is returning successfully.

What's the meaning of returning successfully? If the connection from FRP server to client is broken, frps will close the connections from HTTP client. I think the normal HTTP client should return a unexpected EOF since it hasn't read all of the content.

<!-- gh-comment-id:482598350 --> @fatedier commented on GitHub (Apr 12, 2019): I have different ideas. Why does HTTP client think it has the complete file content while the length of bytes it read is not equal with `Content-Length` ? > The http client does not know that FRP Server lost connection to FRP Client in the middle of the stream because FRP server is returning successfully. What's the meaning of `returning successfully`? If the connection from FRP server to client is broken, frps will close the connections from HTTP client. I think the normal HTTP client should return a unexpected EOF since it hasn't read all of the content.
Author
Owner

@jsheely commented on GitHub (Apr 12, 2019):

If the connection from FRP server to client is broken, frps will close the connections from HTTP client.

That is the correct behavior.

If the connection is closed during a content stream from Http Client -> FRP Server then it will throw an exception correctly

However if the connection is closed from the FRP Server -> FRP Client then the Http Client is given a partial response and no exception is raised.

These two scenarios should be treated the same so the client is given the exception

<!-- gh-comment-id:482600217 --> @jsheely commented on GitHub (Apr 12, 2019): >If the connection from FRP server to client is broken, frps will close the connections from HTTP client. That is the correct behavior. If the connection is closed during a content stream from Http Client -> FRP Server then it will throw an exception correctly However if the connection is closed from the FRP Server -> FRP Client then the Http Client is given a partial response and no exception is raised. These two scenarios should be treated the same so the client is given the exception
Author
Owner

@jsheely commented on GitHub (Apr 12, 2019):

Here is a code example of the HTTP Client in C# .net core and where the issue shows up

static async Task Main(string[] args)
{
	try
	{
		var client = new HttpClient();
		client.BaseAddress = new Uri("https://localhost:5021/");
		// Request Sent and connection made to FRP Server
		var response = await client.GetAsync("api/values", HttpCompletionOption.ResponseHeadersRead);
		// Received HTTP Status OK 200.
		if (response.IsSuccessStatusCode)
		{
			// Begin streaming content.
			// If connection between FRP Server and FRP Client goes down 
			//      we will receive a partial JSON response for content
			// This would throw an exception if the connection is closed 
			//      in the middle of the stream and the request could be retried.
			var content = await response.Content.ReadAsStringAsync();

			// Because we got a partial JSON object. This will fail to deserialize correctly
			// If the connection were closed due to streaming error we would never get here 
			//      and waste time trying to deserialize a partial object
			var model = JsonConvert.DeserializeObject<dynamic>(content);
		}

	}
	catch (Exception ex)
	{
		Console.WriteLine("Failed:" + ex.Message);
	}

}
<!-- gh-comment-id:482601378 --> @jsheely commented on GitHub (Apr 12, 2019): Here is a code example of the HTTP Client in C# .net core and where the issue shows up ```csharp static async Task Main(string[] args) { try { var client = new HttpClient(); client.BaseAddress = new Uri("https://localhost:5021/"); // Request Sent and connection made to FRP Server var response = await client.GetAsync("api/values", HttpCompletionOption.ResponseHeadersRead); // Received HTTP Status OK 200. if (response.IsSuccessStatusCode) { // Begin streaming content. // If connection between FRP Server and FRP Client goes down // we will receive a partial JSON response for content // This would throw an exception if the connection is closed // in the middle of the stream and the request could be retried. var content = await response.Content.ReadAsStringAsync(); // Because we got a partial JSON object. This will fail to deserialize correctly // If the connection were closed due to streaming error we would never get here // and waste time trying to deserialize a partial object var model = JsonConvert.DeserializeObject<dynamic>(content); } } catch (Exception ex) { Console.WriteLine("Failed:" + ex.Message); } } ```
Author
Owner

@fatedier commented on GitHub (Apr 12, 2019):

Thanks for your example code. I will test it when i have a free time.

I still have a question. What's your scenarios when you say the connection is down, frpc is down(like being killed) or network interruption ?

<!-- gh-comment-id:482606491 --> @fatedier commented on GitHub (Apr 12, 2019): Thanks for your example code. I will test it when i have a free time. I still have a question. What's your scenarios when you say the connection is down, frpc is down(like being killed) or network interruption ?
Author
Owner

@jsheely commented on GitHub (Apr 12, 2019):

@fatedier Connection interuption

I just tested the scenario again and now I am confused. Because using the latest v0.26 the issue is now happening

If I close the FRP Client manually then I get the error I expect on the Http Client
Error while copying content to a stream.

So either this was fixed in v0.26 or my scenario in production is different then in testing.

I will keep testing and see if I can get some more information for you. Perhaps this is no longer a problem

Thank you!

<!-- gh-comment-id:482608917 --> @jsheely commented on GitHub (Apr 12, 2019): @fatedier Connection interuption I just tested the scenario again and now I am confused. Because using the latest v0.26 the issue is now happening If I close the FRP Client manually then I get the error I expect on the Http Client ```Error while copying content to a stream.``` So either this was fixed in v0.26 or my scenario in production is different then in testing. I will keep testing and see if I can get some more information for you. Perhaps this is no longer a problem Thank you!
Author
Owner

@fatedier commented on GitHub (Apr 12, 2019):

There are some differences between the two scenarios.

  • Close frpc manually : frps will get the close event as soon as possible, then it will close the connection from HTTP client immediately. So this is the same behavior if you close frps manually.
  • Network interruption: frps can only know the interruption by the heartbeat timeout. Maybe it's 90 seconds after the network interruption.
<!-- gh-comment-id:482614343 --> @fatedier commented on GitHub (Apr 12, 2019): There are some differences between the two scenarios. * **Close frpc manually** : frps will get the close event as soon as possible, then it will close the connection from HTTP client immediately. So this is the same behavior if you close frps manually. * **Network interruption**: frps can only know the interruption by the heartbeat timeout. Maybe it's 90 seconds after the network interruption.
Author
Owner

@jsheely commented on GitHub (Apr 12, 2019):

Oh yes good point.

So I did end up testing that and in both scenarios if I manually close FRPC or if I just kill the process. It's sending the [newhttp.go:221] httputil: ReverseProxy read error during body copy: unexpected EOF to FRPS and my Http Client is getting Error while copying content to a stream.

Which is perfect and how I expect it to work. But this is still not the behavior I'm seeing in my production environment. So I will keep testing.

At this point I believe FRP is handling the scenario correctly and it is either a edge case or a problem in my environment.

Thank you again for your assistance. It's much appreciated.

<!-- gh-comment-id:482619185 --> @jsheely commented on GitHub (Apr 12, 2019): Oh yes good point. So I did end up testing that and in both scenarios if I manually close FRPC or if I just kill the process. It's sending the ``` [newhttp.go:221] httputil: ReverseProxy read error during body copy: unexpected EOF``` to FRPS and my Http Client is getting ```Error while copying content to a stream.``` Which is perfect and how I expect it to work. But this is still not the behavior I'm seeing in my production environment. So I will keep testing. At this point I believe FRP is handling the scenario correctly and it is either a edge case or a problem in my environment. Thank you again for your assistance. It's much appreciated.
Author
Owner

@jsheely commented on GitHub (Apr 12, 2019):

Okay I have some more details

  • If the Transfer-Encoding: chunked is set and the API does not return a content-length then what is happening is that it's not looking for the content termination character which says that it has received all the content

So in the case that the stream cuts out and it hasn't received that stream ending character then it should also throw an EOF exception and kill the connection as it does in the other two scenarios.

<!-- gh-comment-id:482721290 --> @jsheely commented on GitHub (Apr 12, 2019): Okay I have some more details - If the ```Transfer-Encoding: chunked``` is set and the API does not return a content-length then what is happening is that it's not looking for the content termination character which says that it has received all the content So in the case that the stream cuts out and it hasn't received that stream ending character then it should also throw an EOF exception and kill the connection as it does in the other two scenarios.
Author
Owner

@fatedier commented on GitHub (Apr 15, 2019):

I think your c# http client doesn't handler chunked stream correctly.

From https://en.wikipedia.org/wiki/Chunked_transfer_encoding

The transmission ends when a zero-length chunk is received.

HTTP client should return unexpect EOF after the connection closed when it hasn't received the last zero-length chunk.

<!-- gh-comment-id:483215168 --> @fatedier commented on GitHub (Apr 15, 2019): I think your c# http client doesn't handler `chunked` stream correctly. From https://en.wikipedia.org/wiki/Chunked_transfer_encoding > The transmission ends when a zero-length chunk is received. HTTP client should return unexpect EOF after the connection closed when it hasn't received the last zero-length chunk.
Author
Owner

@meluskyc commented on GitHub (May 17, 2019):

Hello,

I was able to reproduce this issue using the go http client and also curl. See the sample code here.

Here is the expected behavior. When using ngrok and killing the ngrok client during transmission of a chunked response, the go http client errors with unexpected EOF. curl returns error 18:

curl -v http://4814c77a.ngrok.io

*   Trying 2600:1f16:d83:1200:15a9:cc32:92d3:c8a9...
* TCP_NODELAY set
* Connected to d69f0dbb.ngrok.io (2600:1f16:d83:1200:15a9:cc32:92d3:c8a9) port 80 (#0)
> GET / HTTP/1.1
> Host: d69f0dbb.ngrok.io
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< X-Content-Type-Options: nosniff
< Date: Fri, 17 May 2019 02:23:20 GMT
< Content-Type: text/plain; charset=utf-8
< Transfer-Encoding: chunked
<
Chunk #1
Chunk #2
Chunk #3
Chunk #4
Chunk #5
Chunk #6
Chunk #7
* transfer closed with outstanding read data remaining
* Closing connection 0
curl: (18) transfer closed with outstanding read data remaining

echo $?
18

When running frps and killing frpc during transmission, the go http client does not error. curl returns no errors:

curl -v web01.meluskyc.org:8080

*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to web01.meluskyc.org (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: web01.meluskyc.org:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain; charset=utf-8
< Date: Fri, 17 May 2019 02:06:14 GMT
< X-Content-Type-Options: nosniff
< Content-Length: 36
<
Chunk #1
Chunk #2
Chunk #3
Chunk #4
* Connection #0 to host web01.meluskyc.org left intact
* Closing connection 0

echo $?
0
<!-- gh-comment-id:493300250 --> @meluskyc commented on GitHub (May 17, 2019): Hello, I was able to reproduce this issue using the `go http` client and also `curl`. See the sample code [here](https://github.com/meluskyc/frp-1192). Here is the expected behavior. When using `ngrok` and killing the `ngrok` client during transmission of a chunked response, the `go` http client errors with `unexpected EOF`. curl returns error `18`: ```bash curl -v http://4814c77a.ngrok.io * Trying 2600:1f16:d83:1200:15a9:cc32:92d3:c8a9... * TCP_NODELAY set * Connected to d69f0dbb.ngrok.io (2600:1f16:d83:1200:15a9:cc32:92d3:c8a9) port 80 (#0) > GET / HTTP/1.1 > Host: d69f0dbb.ngrok.io > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < X-Content-Type-Options: nosniff < Date: Fri, 17 May 2019 02:23:20 GMT < Content-Type: text/plain; charset=utf-8 < Transfer-Encoding: chunked < Chunk #1 Chunk #2 Chunk #3 Chunk #4 Chunk #5 Chunk #6 Chunk #7 * transfer closed with outstanding read data remaining * Closing connection 0 curl: (18) transfer closed with outstanding read data remaining echo $? 18 ``` When running `frps` and killing `frpc` during transmission, the `go` http client does not error. `curl` returns no errors: ```bash curl -v web01.meluskyc.org:8080 * Trying 127.0.0.1... * TCP_NODELAY set * Connected to web01.meluskyc.org (127.0.0.1) port 8080 (#0) > GET / HTTP/1.1 > Host: web01.meluskyc.org:8080 > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: text/plain; charset=utf-8 < Date: Fri, 17 May 2019 02:06:14 GMT < X-Content-Type-Options: nosniff < Content-Length: 36 < Chunk #1 Chunk #2 Chunk #3 Chunk #4 * Connection #0 to host web01.meluskyc.org left intact * Closing connection 0 echo $? 0 ```
Author
Owner

@yuyulei commented on GitHub (Nov 5, 2020):

@jsheely @meluskyc fixed by PR#2051,please try next release。

<!-- gh-comment-id:722229583 --> @yuyulei commented on GitHub (Nov 5, 2020): @jsheely @meluskyc fixed by [PR#2051](https://github.com/fatedier/frp/pull/2051),please try next release。
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: github-starred/frp#936
No description provided.