#2520 WebClient issue

go4 Wed 6 Apr 2016

I find some hang problems when using Draft Proxy. And successfully reproduced by WebClient.

Chunked Issue

c := WebClient(`http://localhost:8081/pod/fwt/fwt.js`)
c.reqHeaders["Content-Type"] = "text/plain"

c.writeReq
//c.reqOut.print("hello").flush
c.readRes

buf := c.resIn.readAllBuf
s := buf.readAllStr
echo("get:"+s)

c.close

WebClient auto add the header Transfer-Encoding: chunked. This bad chunked Stream lead Wisp overtime. Why GET request has Content-Type, It's a issue of web::HttpReq.

304 Issue

c := WebClient(`http://localhost:8081/pod/fwt/fwt.js`)
c.reqHeaders["If-None-Match"] = "\"684e5-693f5500ee34c00\""
c.reqHeaders["If-Modified-Since"] = "Thu, 08 Jan 2015 00:28:30 GMT"
c.reqHeaders["Connection"] = "keep-alive"

c.writeReq
c.readRes
buf := c.resIn.readAllBuf
s := buf.readAllStr
echo("get:"+s)

c.close

The request return 304, and WebClient try to read the content of resIn and blocked.

brian Wed 6 Apr 2016

Regarding the first issue - you have to use a chunked stream in that case because you don't know how many bytes you will write until after you written them and closed the stream. The work around is easy though - just precompute your content size and set the Content-Length header (your case looks sort of odd since you are setting a content type but no content, but in that case you should content-length to zero)

Don't fully understand the second issue, but we'd really need to see what the response headers are

SlimerDude Wed 6 Apr 2016

The second issue is due to the keep-alive connection header. It tells Wisp not to close the connection, hence the InputStream c.resIn stays open, and c.resIn.readAllBuf then hangs waiting to read input that never arrives.

go4 Wed 6 Apr 2016

Thanks.

The first issue, It's a GET request no content. web::HttpReq set the Content-Type header.

The second issue response:

< HTTP/1.1 304 Not Modified
< Date: Thu, 07 Apr 2016 01:58:14 GMT
< Server: Wisp/1.0.67
< Last-Modified: Thu, 08 Jan 2015 00:28:30 GMT
< ETag: "684e5-693f5500ee34c00"
< Content-Type: text/html; charset=utf-8

And thanks SlimerDude's explanation.

SlimerDude Thu 7 Apr 2016

Hi Go4, I believe you're right on the first issue and I've just updated Butter (in this commit) to ensure it sets the Content-Length should Content-Type be specified. (It was already doing it for non-empty requests.)

Incidentally, the same web request in Butter would look like:

client   := Butter.churnOut 
response := client.sendRequest(
    ButterRequest(`http://localhost:8081/pod/fwt/fwt.js`) {
        it.headers.contentType = MimeType("text/plain")
    }
)

Because a Butter client stores cookies and headers, a single instance can be used to make multiple requests:

response := client.get(`/api/read/1`)
response  = client.get(`/api/read/2`)
response  = client.get(`/api/read/3`)

I've tried to make Butter convenient, so it also has handy methods for doing things like posting JSON, and getting and setting HTTP Headers

It currently has no support for persistent (keep-alive) connections though.

Login or Signup to reply.