#2396 dom::HttpReq.send() should take nullable content

SlimerDude Thu 26 Feb 2015

Hi, I was investigating this BedSheet BitBucket issue where, to cut a long story short, setting the Content-Type HTTP Request Header of a simple GET in web::WebClient causes a 10 second delay. Here is a test case:

using wisp
using web

class TenSeconds {
    Void main() {
        willow := WispService { it.port=8099 }.install.start
        start  := Duration.now
        wc     := WebClient(`http://localhost:8099/`)

        // This line causes the request to take 10 seconds to write / read
        // comment it out for an immediate response
        wc.reqHeaders["Content-Type"] = "text/plain"


        echo("\nTOTAL TIME: ${(Duration.now-start).toLocale}")        

By setting the Content-Type req header, WebUtil.makeContentOutStream() creates a chunked OutStream and sets the Transfer-Encoding req header. Wisp, on the other side of the request, then creates a chunked InStream (because of the Transfer-Encoding req header).

The 10 second delay appears to be some kind of socket time out because, despite these chunked streams being setup, no content is actually transferred.

Is there anything I (or the Fantom web pod) could do to prevent this? I ask because the code flow all seems quite logical.

The Real Problem

So you may be asking:

Why do you set the Content-Type request header in the first place? Just... don't!

It's because the original request is made by dom::HttpReq.send() which I have no control over. (The req is intercepted by a BedSheet proxy which uses WebClient to forward the req to the real application - same as draft.)

Looking at send() in HttpReqPeer.js, the Content-Type header is set when content is sent. But then HttpReq.get() defaults to always sending an empty string:

** Convenience for 'send("GET", "", c)'.
Void get(|HttpRes res| c)
  send("GET", "", c)

Which means GET requests, when repeated by WebClient, will always incur the 10 seconds delay.

Get To The Point!

Could HttpReq.send() be updated to take null content and get() updated to send null?

** Send a request with the given content using the given
** HTTP method (case does not matter).  After receiving
** the response, call the given closure with the resulting
** HttpRes object.
native Void send(Str method, Obj? content, |HttpRes res| c)

** Convenience for 'send("GET", "", c)'.
Void get(|HttpRes res| c)
  send("GET", null, c)

By The Way

On my travels I noticed that web::WebUtil.doMakeContentInStream() reads:

len := headers["Content-Length"]
if (len != null)
  return makeFixedInStream(in, len.toInt) { charset = cs }

Should it also check that the Content-Length header is greater than zero? I ask because I've seen many requests with a Content-Length of 0:

len := headers["Content-Length"]
if (len != null && len.toInt > 0)
  return makeFixedInStream(in, len.toInt) { charset = cs }

(Phew. I think that's all! I was trying to keep this topic short, but there's so much back-story that it's not really possible - lemme know if you want me to re-explain any part.)

andy Fri 27 Feb 2015

Ticket promoted to #2396 and assigned to andy

I'll have to double check the code - but yeah that should be fine to change.

andy Tue 3 Mar 2015

Ticket resolved in 1.0.68

Fixed - changeset

shturman Tue 3 Mar 2015

After update i get "uncaught exception: sys::Err: sys::Type.toFanType: obj is null" in browser console on HttpReq

andy Tue 3 Mar 2015

Need to make sure you pickup the updated peer - double-check your browser cache.

shturman Tue 3 Mar 2015

I did it and see updated dom.js content in Firebug, but the error is still here

SlimerDude Wed 4 Mar 2015

I made a comment on the BitBucket commit.

andy Wed 4 Mar 2015

Yeah typo - should work now

Login or Signup to reply.