private void SendResponse(HeyHttpResponse response) { try { // Set default content. response.ContentStream = CreateDefaultResponseStream(); // Do authentication. HeyHttpAuthentication.AddAuthenticateHeaderIfNeeded(request, response); // Custom headers. if (request.QueryStringHasTrueValue("custom")) { response.Headers.Add("X-Header: Value1"); response.Headers.Add("X-Header: Value2"); response.Headers.Add("X-Header: Value3"); } // Show the 'Save As' dialog. string filename; if (request.QueryStringHas("filename", out filename)) { response.Headers.Add(String.Format("Content-Disposition: Attachment; filename={0}", filename)); } // 201 Created for AtomPub. if (request.QueryStringHasTrueValue("create")) { response.Status = "201 Created"; response.Headers.Add("Location: http://heyhttp.org/Data/first-post.atom"); response.Headers.Add("Content-Location: http://heyhttp.org/Data/first-post.atom"); response.Headers.Add("ETag: \"e180ee84f0671b1\""); } // 301 Moved Permanently. string redirect; if (request.QueryStringHas("redirect", out redirect)) { response.Status = "301 Moved Permanently"; response.Headers.Add("Location: " + redirect); // Do not send any data back. request.Path = ""; } // 503 Service Unavailable, retry after N seconds. if (request.QueryStringHasTrueValue("retry")) { if (!retryReceivedBefore) { response.Status = "503 Service Unavailable"; response.Headers.Add("Retry-After: 5"); response.ContentStream = GetStringStream(String.Empty); } retryReceivedBefore = !retryReceivedBefore; } // Set cache headers. if (request.QueryStringHasTrueValue("cache")) { response.Headers.Add("Cache-Control: max-age=3600"); // One hour (60 minutes * 60 seconds) //additionalHeaders.Add("Expires: " + DateTime.UtcNow.AddHours(24).ToString("R")); } else if (request.QueryStringHasTrueValue("nocache")) { response.Headers.Add("Cache-Control: no-cache"); // HTTP 1.1 } // Set cookie header. if (request.QueryStringHasTrueValue("setcookie")) { response.Headers.Add("Set-Cookie: sessionTestCookie=X"); response.Headers.Add("Set-Cookie: persistentTestCookie=Y; expires=Wednesday, 09-Nov-2020 23:12:40 GMT"); response.Headers.Add("Set-Cookie: httpOnlyTestCookie=X; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; HttpOnly"); response.Headers.Add("Set-Cookie: subdomainTestCookie=ghi; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; domain=foo.heyhttp.org"); response.Headers.Add("Set-Cookie: slashEndingCookie=b; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; domain=heyhttp.org; path=/foo/"); response.Headers.Add("Set-Cookie: nonSlashEndingCookie=a; expires=Wednesday, 09-Nov-2020 23:12:40 GMT; domain=heyhttp.org; path=/foo"); } // Return an specific HTTP status. int requestedStatus; if (request.QueryStringHas("status", out requestedStatus)) { response.Status = String.Format("{0} Foo Foo Bar", requestedStatus); } // Set arbitrary header. string headerName; string headerValue; if (request.QueryStringHas("name", out headerName) && request.QueryStringHas("value", out headerValue)) { response.Headers.Add(String.Format("{0}: {1}", headerName, headerValue)); } // Introduce a long delay. int delay; if (request.QueryStringHas("delay", out delay)) { Thread.Sleep(delay); } // Get trace content stream. if (request.QueryStringHasTrueValue("trace")) { response.ContentStream = GetTraceStream(); // Do not send any data back. request.Path = ""; } // Get file. if (!String.IsNullOrEmpty(request.Path) && !String.IsNullOrEmpty(response.Status) && request.Path != "/" && !response.Status.StartsWith("404")) { try { response.ContentStream = GetFileStream(); } catch (FileNotFoundException ex) { response.Status = "404 Not Found"; response.ContentStream = GetStringStream(ex.Message); } catch (DirectoryNotFoundException ex) { response.Status = "404 Not Found"; response.ContentStream = GetStringStream(ex.Message); } } // Create a response of the length given. long sizeInBytes; if (request.QueryStringHas("length", out sizeInBytes)) { response.ContentStream = CreateBigStream(sizeInBytes); } // This check must be done after the response coontent has been selected. string eTag; bool useETag = request.QueryStringHas("etag", out eTag); if (useETag) { //// 304 Not Modified with ETag. //if (request.GetHeader("If-None-Match") == eTag) //{ // response.Status = "304 Not Modified"; // response.ContentStream = GetStringStream(String.Empty); //} //else //{ //} // This 'if' is to manually change the flow when debugging. if (DateTime.Now.Second == -1) { eTag = String.Empty; response.Status = "404 NOT FOUND"; response.ContentStream = GetStringStream("Oops!"); } else { response.Headers.Add(String.Format("ETag: \"{0}\"", eTag)); response.Headers.Add("Accept-Ranges: bytes"); } } bool useLastModified = request.QueryStringHasTrueValue("lastModified"); if (useLastModified) { response.Headers.Add("Last-Modified: Thu, 21 Aug 2014 21:34:57 GMT"); response.Headers.Add("Accept-Ranges: bytes"); } HttpRangeHeader rangeHeader = request.GetRange(); if (rangeHeader != null && (useETag || useLastModified)) { response.Status = "206 Partial Content"; response.Headers.Add(rangeHeader.GetContentRange(response.ContentStream.Length)); response.FirstPosition = rangeHeader.FirstPosition; response.LastPosition = rangeHeader.LastPosition; } ForkSendResponse(response); } finally { // 'finally' block is executed even if a 'try' or 'catch' block contains a 'return'. if (response.ContentStream != null) { response.ContentStream.Dispose(); } } }
public static void AddAuthenticateHeaderIfNeeded(HeyHttpRequest request, HeyHttpResponse response) { // Basic Server: // GET ---------> // // <--------- 401 Unauthorized // WWW-Authenticate: Basic realm="xyz" // // GET ---------> // Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== // // <--------- 200 OK // Basic Proxy: // // GET ---------> // <-------- 407 Proxy Authentication Required // Proxy-Authenticate: Basic realm="xyz" // // GET ---------> // Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== // // <--------- 200 OK // NTLM: // // GET ---------> // <--------- 401 Unauthorized // negotiate message ---------> // <--------- challenge message // authenticate message ---------> // <--------- 200 OK if (IsNtlmMessage(request, NtlmMessageType.NegotiatieMessage)) { response.Status = "401 Unauthorized"; response.Headers.Add("WWW-Authenticate: NTLM TlRMTVNTUAACAAAADgAOADgAAAAFgomiBTEwAGt4s6QAAAAAAAAAAPwA/ABGAAAABgLwIwAAAA9SAEUARABNAE8ATgBEAAIADgBSAEUARABNAE8ATgBEAAEAHgBXAEkATgAtAEQARgBHADEAMABFADIAOABLADEANgAEADQAcgBlAGQAbQBvAG4AZAAuAGMAbwByAHAALgBtAGkAYwByAG8AcwBvAGYAdAAuAGMAbwBtAAMAVABXAEkATgAtAEQARgBHADEAMABFADIAOABLADEANgAuAHIAZQBkAG0AbwBuAGQALgBjAG8AcgBwAC4AbQBpAGMAcgBvAHMAbwBmAHQALgBjAG8AbQAFACQAYwBvAHIAcAAuAG0AaQBjAHIAbwBzAG8AZgB0AC4AYwBvAG0ABwAIADJX/d1Cjc0BAAAAAA=="); } // Require basic access authentication. if (request.QueryStringHasTrueValue("basic") && !IsAuthorizationValid(request)) { response.Status = "401 Unauthorized"; response.Headers.Add("WWW-Authenticate: Basic realm=\"Secure Area\""); request.Path = ""; } // Require digest access authentication. if (request.QueryStringHasTrueValue("digest") && !IsAuthorizationValid(request)) { response.Status = "401 Unauthorized"; response.Headers.Add(String.Format( "WWW-Authenticate: Digest realm=\"{0}\", qop=\"{1}\", nonce=\"{2}\", opaque=\"{3}\"", digestRealm, digestQop, digestNonce, digestOpaque)); request.Path = ""; } // Require NTLM credentials. if (request.QueryStringHasTrueValue("negotiate") && !IsAuthorizationValid(request)) { response.Status = "401 Unauthorized"; response.Headers.Add("WWW-Authenticate: Negotiate"); request.Path = ""; } // NTLM authentication. if (request.QueryStringHasTrueValue("ntlm") && !IsAuthorizationValid(request)) { response.Status = "401 Unauthorized"; response.Headers.Add("WWW-Authenticate: NTLM"); request.Path = ""; } }