1. Oops - I overlooked the fact that the redirectCount attribute of a Request is exported (it isn&#39;t listed on the <a href="http://hackage.haskell.org/packages/archive/http-conduit/1.2.0/doc/html/Network-HTTP-Conduit.html">documentation</a> probably because the constructor itself isn&#39;t exported. This seems like a flaw in Haddock...). Silly me. No need to export httpRaw.<div>

<br></div><div>2. I think that stuffing many arguments into the &#39;http&#39; function is ugly. However, I&#39;m not sure that the number of arguments to &#39;http&#39; could ever reach an unreasonably large amount. Perhaps I have bad foresight, but I personally feel that adding cookies to the http request will be the last thing that we will need to add. Putting a bound on this growth of arguments makes me more willing to think about this option. On the other hand, using a BrowserAction to modify internal state is very elegant. Which approach do you think is best? I think I&#39;m leaning toward the upper-level Browser module idea.</div>

<div><br></div><div>If there was to be a higher-level HTTP library, I would argue that the redirection code should be moved into it, and the only high-level function that the Network.HTTP.Conduit module would export is &#39;http&#39; (or httpRaw). What do you think about this?</div>

<div><br></div><div>Thanks for helping me out with this,</div><div>Myles C. Maxfield</div><div><br></div><div><div class="gmail_quote">On Sun, Jan 22, 2012 at 9:56 PM, Michael Snoyman <span dir="ltr">&lt;<a href="mailto:michael@snoyman.com">michael@snoyman.com</a>&gt;</span> wrote:<br>

<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Sun, Jan 22, 2012 at 11:07 PM, Myles C. Maxfield<br>
<div><div class="h5">&lt;<a href="mailto:myles.maxfield@gmail.com">myles.maxfield@gmail.com</a>&gt; wrote:<br>
&gt; Replies are inline. Thanks for the quick and thoughtful response!<br>
&gt;<br>
&gt; On Sat, Jan 21, 2012 at 8:56 AM, Michael Snoyman &lt;<a href="mailto:michael@snoyman.com">michael@snoyman.com</a>&gt;<br>
&gt; wrote:<br>
&gt;&gt;<br>
&gt;&gt; Hi Myles,<br>
&gt;&gt;<br>
&gt;&gt; These sound like two solid features, and I&#39;d be happy to merge in code to<br>
&gt;&gt; support it. Some comments below.<br>
&gt;&gt;<br>
&gt;&gt; On Sat, Jan 21, 2012 at 8:38 AM, Myles C. Maxfield<br>
&gt;&gt; &lt;<a href="mailto:myles.maxfield@gmail.com">myles.maxfield@gmail.com</a>&gt; wrote:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; To: Michael Snoyman, author and maintainer of http-conduit<br>
&gt;&gt;&gt; CC: haskell-cafe<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Hello!<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; I am interested in contributing to the http-conduit library. I&#39;ve been<br>
&gt;&gt;&gt; using it for a little while and reading through its source, but have felt<br>
&gt;&gt;&gt; that it could be improved with two features:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Allowing the caller to know the final URL that ultimately resulted in the<br>
&gt;&gt;&gt; HTTP Source. Because httpRaw is not exported, the caller can&#39;t even<br>
&gt;&gt;&gt; re-implement the redirect-following code themselves. Ideally, the caller<br>
&gt;&gt;&gt; would be able to know not only the final URL, but also the entire chain of<br>
&gt;&gt;&gt; URLs that led to the final request. I was thinking that it would be even<br>
&gt;&gt;&gt; cooler if the caller could be notified of these redirects as they happen in<br>
&gt;&gt;&gt; another thread. There are a couple ways to implement this that I have been<br>
&gt;&gt;&gt; thinking about:<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; A straightforward way would be to add a [W.Ascii] to the type of<br>
&gt;&gt;&gt; Response, and getResponse can fill in this extra field. getResponse already<br>
&gt;&gt;&gt; knows about the Request so it can tell if the response should be gunzipped.<br>
&gt;&gt;<br>
&gt;&gt; What would be in the [W.Ascii], a list of all paths redirected to? Also,<br>
&gt;&gt; I&#39;m not sure what gunzipping has to do with here, can you clarify?<br>
&gt;&gt;<br>
&gt;<br>
&gt; Yes; my idea was to make the [W.Ascii] represent the list of all URLs<br>
&gt; redirected to, in order.<br>
&gt;<br>
&gt; My comment about gunzipping is only tangentially related. I meant that in<br>
&gt; the latest version of the code on GitHub, the getResponse function already<br>
&gt; takes a Request as an argument. This means that the getResponse function<br>
&gt; already knows what URL its data is coming from, so modifying the getResponse<br>
&gt; function to return that URL is simple. (I mentioned gunzip because, as far<br>
&gt; as I can tell, the reason that getResponse already takes a Request is so<br>
&gt; that the function can tell if the request should be gunzipped.)<br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; It would be nice for the caller to be able to know in real time what URLs<br>
&gt;&gt;&gt; the request is being redirected to. A possible way to do this would be for<br>
&gt;&gt;&gt; the &#39;http&#39; function to take an extra argument of type (Maybe<br>
&gt;&gt;&gt; (Control.Concurrent.Chan W.Ascii)) which httpRaw can push URLs into. If the<br>
&gt;&gt;&gt; caller doesn&#39;t want to use this variable, they can simply pass Nothing.<br>
&gt;&gt;&gt; Otherwise, the caller can create an IO thread which reads the Chan until<br>
&gt;&gt;&gt; some termination condition is met (Perhaps this will change the type of the<br>
&gt;&gt;&gt; extra argument to (Maybe (Chan (Maybe W.Ascii)))). I like this solution,<br>
&gt;&gt;&gt; though I can see how it could be considered too heavyweight.<br>
&gt;&gt;<br>
&gt;&gt;<br>
&gt;&gt; I do think it&#39;s too heavyweight. I think if people really want lower-level<br>
&gt;&gt; control of the redirects, they should turn off automatic redirect and allow<br>
&gt;&gt; 3xx responses.<br>
&gt;<br>
&gt; Yeah, that totally makes more sense. As it stands, however, httpRaw isn&#39;t<br>
&gt; exported, so a caller has no way of knowing about each individual HTTP<br>
&gt; transaction. Exporting httpRaw solves the problem I&#39;m trying to solve. If we<br>
&gt; export httpRaw, should we also make &#39;http&#39; return the URL chain? Doing both<br>
&gt; is probably the best solution, IMHO.<br>
<br>
</div></div>What&#39;s the difference between calling httpRaw and calling http with<br>
redirections turned off?<br>
<div><div class="h5"><br>
&gt;&gt;&gt;<br>
&gt;&gt;&gt; Making the redirection aware of cookies. There are redirects around the<br>
&gt;&gt;&gt; web where the first URL returns a Set-Cookie header and a 3xx code which<br>
&gt;&gt;&gt; redirects to another site that expects the cookie that the first HTTP<br>
&gt;&gt;&gt; transaction set. I propose to add an (IORef to a Data.Set of Cookies) to the<br>
&gt;&gt;&gt; Manager datatype, letting the Manager act as a cookie store as well as a<br>
&gt;&gt;&gt; repository of available TCP connections. httpRaw could deal with the cookie<br>
&gt;&gt;&gt; store. Network.HTTP.Types does not declare a Cookie datatype, so I would<br>
&gt;&gt;&gt; probably be adding one. I would probably take it directly from<br>
&gt;&gt;&gt; Network.HTTP.Cookie.<br>
&gt;&gt;<br>
&gt;&gt; Actually, we already have the cookie package for this. I&#39;m not sure if<br>
&gt;&gt; putting the cookie store in the manager is necessarily the right approach,<br>
&gt;&gt; since I can imagine wanting to have separate sessions while reusing the same<br>
&gt;&gt; connections. A different approach could be adding a list of Cookies to both<br>
&gt;&gt; the Request and Response.<br>
&gt;<br>
&gt; Ah, looks like you&#39;re the maintainer of that package as well! I didn&#39;t<br>
&gt; realize it existed. I should have, though; Yesod must need to know about<br>
&gt; cookies somehow.<br>
&gt;<br>
&gt; As the http-conduit package stands, the headers of the original Request can<br>
&gt; be set, and the headers of the last Response can be read. Because cookies<br>
&gt; are implemented on top of headers, the caller knows about the cookies before<br>
&gt; and after the redirection chain. I&#39;m more interested in the preservation of<br>
&gt; cookies within the redirection chain. As discussed earlier, exposing the<br>
&gt; httpRaw function allows the entire redirection chain to be handled by the<br>
&gt; caller, which alleviates the problem.<br>
&gt;<br>
&gt; That being said, however, the simpleHttp function (and all functions built<br>
&gt; upon &#39;http&#39; inside of http-conduit) should probably respect cookies inside<br>
&gt; redirection chains. Under the hood, Network.Browser does this by having the<br>
&gt; State monad keep track of these cookies (as well as the connection pool) and<br>
&gt; making HTTP requests mutate that State, but that&#39;s a pretty different<br>
&gt; architecture than Network.HTTP.Conduit.<br>
&gt;<br>
&gt; One way I can think to do this would be to let the user supply a CookieStore<br>
&gt; (probably implemented as a (Data.Set Web.Cookie.SetCookie)) and receive a<br>
&gt; (different) CookieStore from the &#39;http&#39; function. That way, the caller can<br>
&gt; manage the CookieStores independently from the connection pool. The downside<br>
&gt; is that it&#39;s one more bit of ugliness the caller has to deal with. How do<br>
&gt; you feel about this? You probably have a better idea :-)<br>
<br>
</div></div>The only idea was to implement an extra layer of cookie-away functions<br>
in a separate Browser module. That&#39;s been the running assumption for a<br>
while now, since HTTP does it, but I&#39;m not opposed to taking a<br>
different approach.<br>
<br>
It could be that the big mistake in all this was putting redirection<br>
at the layer of the API that I did. Yitz Gale pointed out that in<br>
Python, they have the low-level API and the high-level API, the latter<br>
dealing with both redirection and cookies.<br>
<br>
Anyway, here&#39;s one possible approach to the whole situation: `Request`<br>
could have an extra record on it of type `Maybe (IORef (Set<br>
SetCookie))`. When `http` is called, if the record is `Nothing`, a new<br>
value is created. Every time a request is made, the value is updated<br>
accordingly. That way, redirects will respect cookies for the current<br>
sessions, and if you want to keep a longer-term session, you can keep<br>
reusing the record in different `Request`s. We can also add some<br>
convenience functions to automatically reuse the cookie set.<br>
<span class="HOEnZb"><font color="#888888"><br>
Michael<br>
</font></span><div class="HOEnZb"><div class="h5"><br>
&gt;&gt; I&#39;d be happy to do both of these things, but I&#39;m hoping for your input on<br>
&gt;&gt; how to go about this endeavor. Are these features even good to be pursuing?<br>
&gt;&gt; Should I be going about this entirely differently?<br>
&gt;&gt;<br>
&gt;&gt; Thanks,<br>
&gt;&gt; Myles C. Maxfield<br>
&gt;&gt;<br>
&gt;&gt; P.S. I&#39;m curious about the lack of Network.URI throughout<br>
&gt;&gt; Network.HTTP.Conduit. Is there a particular design decision that led you to<br>
&gt;&gt; use raw ascii strings?<br>
&gt;<br>
&gt;<br>
&gt; Because there are plenty of URIs that are valid that we don&#39;t handle at all,<br>
&gt; e.g., ftp.<br>
&gt;<br>
&gt; I&#39;m a little surprised by this, since you can easily test for unhandled URIs<br>
&gt; because they&#39;re already parsed. Whatever; It doesn&#39;t really matter to me, I<br>
&gt; was just surprised by it.<br>
&gt;<br>
&gt; Michael<br>
&gt;<br>
&gt; Thanks again for the feedback! I&#39;m hoping to make a difference :]<br>
&gt;<br>
&gt; --Myles<br>
</div></div></blockquote></div><br></div>