public void Rendering_etag_leaves_single_quoted_value_alone() { var etag = "dsfsdfsdfsdfsdf"; var headers = new DreamHeaders { ETag = "'" + etag + "'" }; var httpRequest = (HttpWebRequest)WebRequest.Create("http://localhost"); HttpUtil.AddHeader(httpRequest, DreamHeaders.ETAG, headers.ETag); Assert.AreEqual("'" + etag + "'", httpRequest.Headers[DreamHeaders.ETAG]); }
private Yield HandleInvoke(Action <string> activity, Plug plug, string verb, XUri uri, DreamMessage request, Result <DreamMessage> response) { Result <IAsyncResult> async; // remove internal headers request.Headers.DreamTransport = null; // set request headers request.Headers.Host = uri.Host; if (request.Headers.UserAgent == null) { request.Headers.UserAgent = "Dream/" + DreamUtil.DreamVersion; } // add cookies to request if (request.HasCookies) { request.Headers[DreamHeaders.COOKIE] = DreamCookie.RenderCookieHeader(request.Cookies); } // initialize request activity("pre WebRequest.Create"); var httpRequest = (HttpWebRequest)WebRequest.Create(uri.ToUri()); activity("post WebRequest.Create"); httpRequest.Method = verb; httpRequest.Timeout = System.Threading.Timeout.Infinite; httpRequest.ReadWriteTimeout = System.Threading.Timeout.Infinite; // Note (arnec): httpRequest AutoRedirect is disabled because Plug is responsible for it (this allows redirects to follow // the appropriate handler instead staying stuck in http end point land httpRequest.AllowAutoRedirect = false; // Note from http://support.microsoft.com/kb/904262 // The HTTP request is made up of the following parts: // 1. Sending the request is covered by using the HttpWebRequest.Timeout method. // 2. Getting the response header is covered by using the HttpWebRequest.Timeout method. // 3. Reading the body of the response is not covered by using the HttpWebResponse.Timeout method. In ASP.NET 1.1 and in later versions, reading the body of the response // is covered by using the HttpWebRequest.ReadWriteTimeout method. The HttpWebRequest.ReadWriteTimeout method is used to handle cases where the response headers are // retrieved in a timely manner but where the reading of the response body times out. httpRequest.KeepAlive = false; httpRequest.ProtocolVersion = System.Net.HttpVersion.Version10; // TODO (steveb): set default proxy //httpRequest.Proxy = WebProxy.GetDefaultProxy(); //httpRequest.Proxy.Credentials = CredentialCache.DefaultCredentials; // set credentials if (plug.Credentials != null) { httpRequest.Credentials = plug.Credentials; httpRequest.PreAuthenticate = true; } else if (!string.IsNullOrEmpty(uri.User) || !string.IsNullOrEmpty(uri.Password)) { httpRequest.Credentials = new NetworkCredential(uri.User ?? string.Empty, uri.Password ?? string.Empty); httpRequest.PreAuthenticate = true; // Note (arnec): this manually adds the basic auth header, so it can authorize // in a single request without requiring challenge var authbytes = Encoding.ASCII.GetBytes(string.Concat(uri.User ?? string.Empty, ":", uri.Password ?? string.Empty)); var base64 = Convert.ToBase64String(authbytes); httpRequest.Headers.Add(DreamHeaders.AUTHORIZATION, "Basic " + base64); } // add request headres foreach (KeyValuePair <string, string> header in request.Headers) { HttpUtil.AddHeader(httpRequest, header.Key, header.Value); } // send message stream if ((request.ContentLength != 0) || (verb == Verb.POST)) { async = new Result <IAsyncResult>(); try { activity("pre BeginGetRequestStream"); httpRequest.BeginGetRequestStream(async.Return, null); activity("post BeginGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 1"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetRequestStream"); yield return(async.Catch()); activity("post yield BeginGetRequestStream"); // send request Stream outStream; try { activity("pre EndGetRequestStream"); outStream = httpRequest.EndGetRequestStream(async.Value); activity("pre EndGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 2"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } // copy data using (outStream) { Result <long> res; activity("pre yield CopyStream"); yield return(res = request.ToStream().CopyToStream(outStream, request.ContentLength, new Result <long>(TimeSpan.MaxValue)).Catch()); activity("post yield CopyStream"); if (res.HasException) { activity("pre HandleResponse 3"); if (!HandleResponse(activity, res.Exception, null, response)) { _log.ErrorExceptionMethodCall(res.Exception, "*****@*****.**", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } } request = null; // wait for response async = new Result <IAsyncResult>(response.Timeout); try { activity("pre BeginGetResponse"); httpRequest.BeginGetResponse(async.Return, null); activity("post BeginGetResponse"); } catch (Exception e) { activity("pre HandleResponse 4"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetResponse"); yield return(async.Catch()); activity("post yield BeginGetResponse"); // check if an error occurred if (async.HasException) { activity("pre HandleResponse 5"); if (!HandleResponse(activity, async.Exception, null, response)) { _log.ErrorExceptionMethodCall(async.Exception, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } else { // handle response try { HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(async.Value); activity("pre HandleResponse 6"); if (!HandleResponse(activity, null, httpResponse, response)) { try { httpRequest.Abort(); } catch { } } } catch (Exception e) { activity("pre HandleResponse 7"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } }
private Yield ResponseHandler(DreamMessage request, Result <DreamMessage> response, HttpListenerContext httpContext, Action <string> activity, Result result) { DreamMessage item = null; request.Close(); try { activity("begin ResponseHandler"); item = response.HasException ? DreamMessage.InternalError(response.Exception) : response.Value; // set status _log.TraceMethodCall("ResponseHandler: Status", item.Status, httpContext.Request.HttpMethod, httpContext.Request.Url); httpContext.Response.StatusCode = (int)item.Status; // remove internal headers item.Headers.DreamTransport = null; item.Headers.DreamPublicUri = null; // add out-going headers if (item.Headers.Server == null) { item.Headers.Server = ServerSignature; } // create stream for response (this will force the creation of the 'Content-Length' header as well) Stream stream = item.ToStream(); // copy headers httpContext.Response.Headers.Clear(); foreach (KeyValuePair <string, string> pair in item.Headers) { _log.TraceMethodCall("SendHttpResponse: Header", pair.Key, pair.Value); HttpUtil.AddHeader(httpContext.Response, pair.Key, pair.Value); } // add set-cookie headers to response if (item.HasCookies) { foreach (DreamCookie cookie in item.Cookies) { httpContext.Response.Headers.Add(DreamHeaders.SET_COOKIE, cookie.ToSetCookieHeader()); } } // disable keep alive behavior httpContext.Response.KeepAlive = false; // send message stream long size = item.ContentLength; if (((size == -1) || (size > 0)) && (stream != Stream.Null)) { activity(string.Format("pre CopyStream ({0} bytes)", size)); yield return(CopyStream(activity, stream, httpContext.Response.OutputStream, size, new Result <long>(DreamHostService.MAX_REQUEST_TIME)).CatchAndLog(_log)); activity("post CopyStream"); } activity("pre Flush"); httpContext.Response.OutputStream.Flush(); activity("post Flush"); result.Return(); activity("end ResponseHandler"); } finally { activity(null); if (item != null) { item.Close(); } httpContext.Response.Close(); } }
private void RequestHandler(IAsyncResult ar) { HttpListenerContext httpContext = null; Action <string> activity = null; HttpListener listener = (HttpListener)ar.AsyncState; // try to finish getting the current context try { httpContext = listener.EndGetContext(ar); } catch (Exception e) { _log.WarnExceptionFormat(e, "unable to finish acquiring the request context, unable to handle request"); } // start listening for next request if (!listener.IsListening) { _log.Debug("dropping out of request handler, since the listener is no longer listening"); return; } try { listener.BeginGetContext(RequestHandler, listener); } catch (Exception e) { _log.WarnExceptionFormat(e, "unable to re-aquire context, dropping out of request handler"); return; } // if we didn't succeed in ending the GetContext call, drop out if (httpContext == null) { return; } DreamMessage request = null; try { // finish listening for current context string[] prefixes = new string[listener.Prefixes.Count]; listener.Prefixes.CopyTo(prefixes, 0); XUri requestUri = HttpUtil.FromHttpContext(httpContext); _log.DebugMethodCall("RequestHandler", httpContext.Request.HttpMethod, requestUri); // create request message request = new DreamMessage(DreamStatus.Ok, new DreamHeaders(httpContext.Request.Headers), MimeType.New(httpContext.Request.ContentType), httpContext.Request.ContentLength64, httpContext.Request.InputStream); DreamUtil.PrepareIncomingMessage(request, httpContext.Request.ContentEncoding, prefixes[0], httpContext.Request.RemoteEndPoint.ToString(), httpContext.Request.UserAgent); requestUri = requestUri.AuthorizeDreamInParams(request, _dreamInParamAuthtoken); // check if the request was forwarded through Apache mod_proxy string hostname = requestUri.GetParam(DreamInParam.HOST, null) ?? request.Headers.ForwardedHost ?? request.Headers.Host ?? requestUri.HostPort; activity = new ActivityState(_env, httpContext.Request.HttpMethod, httpContext.Request.Url.ToString(), hostname).Message; activity("RequestHandler"); // process message _env.UpdateInfoMessage(_sourceExternal, null); string verb = httpContext.Request.HttpMethod; _env.SubmitRequestAsync(verb, requestUri, httpContext.User, request, new Result <DreamMessage>(TimeSpan.MaxValue)) .WhenDone(result => Coroutine.Invoke(ResponseHandler, request, result, httpContext, activity, new Result(TimeSpan.MaxValue))); } catch (Exception ex) { _log.ErrorExceptionMethodCall(ex, "RequestHandler"); if (request != null) { request.Close(); } try { DreamMessage response = DreamMessage.InternalError(ex); httpContext.Response.StatusCode = (int)response.Status; Stream stream = response.ToStream(); httpContext.Response.Headers.Clear(); foreach (KeyValuePair <string, string> pair in response.Headers) { HttpUtil.AddHeader(httpContext.Response, pair.Key, pair.Value); } httpContext.Response.KeepAlive = false; long size = response.ContentLength; if (((size == -1) || (size > 0)) && (stream != Stream.Null)) { CopyStream(delegate { }, stream, httpContext.Response.OutputStream, size, new Result <long>(DreamHostService.MAX_REQUEST_TIME)).Block(); } httpContext.Response.OutputStream.Flush(); } catch { httpContext.Response.StatusCode = (int)DreamStatus.InternalError; } httpContext.Response.Close(); if (activity != null) { activity(null); } } }
private Yield HandleInvoke(Action <string> activity, Plug plug, string verb, XUri uri, DreamMessage request, Result <DreamMessage> response) { Result <IAsyncResult> async; // remove internal headers request.Headers.DreamTransport = null; // set request headers request.Headers.Host = uri.Host; if (request.Headers.UserAgent == null) { request.Headers.UserAgent = "Dream/" + DreamUtil.DreamVersion; } // add cookies to request if (request.HasCookies) { request.Headers[DreamHeaders.COOKIE] = DreamCookie.RenderCookieHeader(request.Cookies); } // check if we can pool the request with an existing one if ((plug.Credentials == null) && StringUtil.ContainsInvariantIgnoreCase(verb, "GET")) { // create the request hashcode StringBuilder buffer = new StringBuilder(); buffer.AppendLine(uri.ToString()); foreach (KeyValuePair <string, string> header in request.Headers) { buffer.Append(header.Key).Append(": ").Append(header.Value).AppendLine(); } Guid hash = new Guid(StringUtil.ComputeHash(buffer.ToString())); // check if an active connection exists Result <DreamMessage> relay = null; lock (_requests) { List <Result <DreamMessage> > pending; if (_requests.TryGetValue(hash, out pending)) { relay = new Result <DreamMessage>(response.Timeout); pending.Add(relay); } else { pending = new List <Result <DreamMessage> >(); pending.Add(response); _requests[hash] = pending; } } // check if we're pooling a request if (relay != null) { // wait for the relayed response yield return(relay); response.Return(relay); yield break; } else { // NOTE (steveb): we use TaskEnv.Instantaneous so that we don't exit the current stack frame before we've executed the continuation; // otherwise, we'll trigger an exception because our result object may not be set. // create new handler to multicast the response to the relays response = new Result <DreamMessage>(response.Timeout, TaskEnv.Instantaneous); response.WhenDone(_ => { List <Result <DreamMessage> > pending; lock (_requests) { _requests.TryGetValue(hash, out pending); _requests.Remove(hash); } // this check should never fail! if (response.HasException) { // send the exception to all relays foreach (Result <DreamMessage> result in pending) { result.Throw(response.Exception); } } else { DreamMessage original = response.Value; // only memorize the message if it needs to be cloned if (pending.Count > 1) { // clone the message to all relays original.Memorize(new Result()).Wait(); foreach (var result in pending) { result.Return(original.Clone()); } } else { // relay the original message pending[0].Return(original); } } }); } } // initialize request activity("pre WebRequest.Create"); var httpRequest = (HttpWebRequest)WebRequest.Create(uri.ToUri()); activity("post WebRequest.Create"); httpRequest.Method = verb; httpRequest.Timeout = System.Threading.Timeout.Infinite; httpRequest.ReadWriteTimeout = System.Threading.Timeout.Infinite; // Note (arnec): httpRequest AutoRedirect is disabled because Plug is responsible for it (this allows redirects to follow // the appropriate handler instead staying stuck in http end point land httpRequest.AllowAutoRedirect = false; // Note from http://support.microsoft.com/kb/904262 // The HTTP request is made up of the following parts: // 1. Sending the request is covered by using the HttpWebRequest.Timeout method. // 2. Getting the response header is covered by using the HttpWebRequest.Timeout method. // 3. Reading the body of the response is not covered by using the HttpWebResponse.Timeout method. In ASP.NET 1.1 and in later versions, reading the body of the response // is covered by using the HttpWebRequest.ReadWriteTimeout method. The HttpWebRequest.ReadWriteTimeout method is used to handle cases where the response headers are // retrieved in a timely manner but where the reading of the response body times out. httpRequest.KeepAlive = false; httpRequest.ProtocolVersion = System.Net.HttpVersion.Version10; // TODO (steveb): set default proxy //httpRequest.Proxy = WebProxy.GetDefaultProxy(); //httpRequest.Proxy.Credentials = CredentialCache.DefaultCredentials; // set credentials if (plug.Credentials != null) { httpRequest.Credentials = plug.Credentials; httpRequest.PreAuthenticate = true; } else if (!string.IsNullOrEmpty(uri.User) || !string.IsNullOrEmpty(uri.Password)) { httpRequest.Credentials = new NetworkCredential(uri.User ?? string.Empty, uri.Password ?? string.Empty); httpRequest.PreAuthenticate = true; // Note (arnec): this manually adds the basic auth header, so it can authorize // in a single request without requiring challenge var authbytes = Encoding.ASCII.GetBytes(string.Concat(uri.User ?? string.Empty, ":", uri.Password ?? string.Empty)); var base64 = Convert.ToBase64String(authbytes); httpRequest.Headers.Add(DreamHeaders.AUTHORIZATION, "Basic " + base64); } // add request headres foreach (KeyValuePair <string, string> header in request.Headers) { HttpUtil.AddHeader(httpRequest, header.Key, header.Value); } // send message stream if ((request.ContentLength != 0) || (verb == Verb.POST)) { async = new Result <IAsyncResult>(); try { activity("pre BeginGetRequestStream"); httpRequest.BeginGetRequestStream(async.Return, null); activity("post BeginGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 1"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetRequestStream"); yield return(async.Catch()); activity("post yield BeginGetRequestStream"); // send request Stream outStream; try { activity("pre EndGetRequestStream"); outStream = httpRequest.EndGetRequestStream(async.Value); activity("pre EndGetRequestStream"); } catch (Exception e) { activity("pre HandleResponse 2"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetRequestStream", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } // copy data using (outStream) { Result <long> res; activity("pre yield CopyStream"); yield return(res = request.ToStream().CopyTo(outStream, request.ContentLength, new Result <long>(TimeSpan.MaxValue)).Catch()); activity("post yield CopyStream"); if (res.HasException) { activity("pre HandleResponse 3"); if (!HandleResponse(activity, res.Exception, null, response)) { _log.ErrorExceptionMethodCall(res.Exception, "*****@*****.**", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } } request = null; // wait for response async = new Result <IAsyncResult>(response.Timeout); try { activity("pre BeginGetResponse"); httpRequest.BeginGetResponse(async.Return, null); activity("post BeginGetResponse"); } catch (Exception e) { activity("pre HandleResponse 4"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } activity("pre yield BeginGetResponse"); yield return(async.Catch()); activity("post yield BeginGetResponse"); // check if an error occurred if (async.HasException) { activity("pre HandleResponse 5"); if (!HandleResponse(activity, async.Exception, null, response)) { _log.ErrorExceptionMethodCall(async.Exception, "HandleInvoke@BeginGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } else { // handle response try { HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(async.Value); activity("pre HandleResponse 6"); if (!HandleResponse(activity, null, httpResponse, response)) { try { httpRequest.Abort(); } catch { } } } catch (Exception e) { activity("pre HandleResponse 7"); if (!HandleResponse(activity, e, null, response)) { _log.ErrorExceptionMethodCall(e, "HandleInvoke@EndGetResponse", verb, uri); try { httpRequest.Abort(); } catch { } } yield break; } } }