//----------------------------------// /// <summary> /// Start a web site and server with the specified path as the root directory. /// </summary> public HttpSite(string path) { if (string.IsNullOrEmpty(path)) { Log.Warning("No path specified for site. A path should be set."); } // persist and clean the path Path = Fs.Combine(path); // get the configuration for the web site ManagerResources.LoadConfig(Fs.Combine(Path, "site.config"), new Act <Configuration>(OnConfiguration)); // create the authentication dictionary Authentication = new Dictionary <string, HttpRequirement>(); // create the redirects collection Redirects = new Dictionary <string, HttpRedirect>(); // create the cache _cache = new Cache <string, WebResource>(Global.Megabyte * 100, r => r.Size); _onAccessDenied = new ActionPop <HttpRequest>(); _onInvalidResource = new ActionPop <HttpRequest>(); _defaultSendOptions = new HttpSendOptions { ContentType = "text/html" }; }
//----------------------------------// /// <summary> /// Process the send options into headers for a following send operation. /// </summary> protected void ProcessOptions(HttpSendOptions options) { if (options == null) { SetCompression(_defaultCompression); return; } // was the content type specified? set the content type if (options.ContentType != null) { Headers.SetSingle(HttpResponseHeader.ContentType, options.ContentType); } // is the data to be cached? if (options.CacheTime.HasValue) { if (options.CacheTime.Value == 0) { Headers.SetSingle(HttpResponseHeader.CacheControl, "no-cache, no-store"); } else { Headers.SetSingle(HttpResponseHeader.CacheControl, "private, max-age=" + options.CacheTime); } } // assign the status code if different if (options.StatusCode != Headers.StatusCode) { Headers.StatusCode = options.StatusCode; } SetCompression(options.Compression); }
/// <summary> /// On the stream being compressed. /// </summary> protected void StartSendStream(MemoryStream stream, HttpSendOptions options, IAction onSent) { _lock.Take(); // process the options ProcessOptions(options); // send the headers SendHeaders((int)stream.Length); Log.Info("Sending stream of length '" + (int)stream.Length + "' to client " + this + "."); // send the stream Socket.Send(stream, (int)stream.Length); _lock.Release(); // is the callback set? yes, run it if (onSent != null) { onSent.Run(); } // dispose of the memory stream stream.Dispose(); }
/// <summary> /// Send the specified web resource. /// </summary> public void Send(WebResource resource, HttpSendOptions options = null, IAction onSent = null) { Log.Info("Sending web resource " + resource + " bytes to client " + this + "."); // get the resource stream then prepare to send the resource stream data resource.GetStream(ActionSet.New(SendResource, (Stream)null, (Lock)null, resource, options, onSent)); }
/// <summary> /// Send the file at the specified local path. /// </summary> public void SendLocal(string path, HttpSendOptions options = null, IAction onSent = null) { Log.Info("Sending local file '" + path + "' bytes to client " + this + "."); // get the extension from the path string extension; int extensionIndex = path.LastIndexOf(Chars.Stop); if (extensionIndex == -1) { extension = null; } else { extension = path.Substring(extensionIndex); } if (options == null) { options = new HttpSendOptions { ContentType = Mime.GetType(extension) } } ; // open a file stream to the resource var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); // start sending the file StartSendStream(fileStream, (int)fileStream.Length, options, onSent); }
/// <summary> /// Send the specified string to the client. /// </summary> public unsafe void Send(string str, HttpSendOptions options = null, IAction onSent = null) { Log.Info("Sending string '" + str + "' to client " + this + "."); // is the string empty? skip if (string.IsNullOrEmpty(str)) { return; } _lock.Take(); // process send options ProcessOptions(options); _onSent = onSent; var buffer = BufferCache.Get(str.Length + str.Length); int count; // get pointers to the string and the send byte buffer fixed(char *src = str) fixed(byte *dst = &buffer[0]) { // get the bytes that the string represents count = _encoder.GetBytes(src, str.Length, dst, buffer.Length, true); } SendBytes(buffer, 0, count); BufferCache.Set(buffer); }
/// <summary> /// Send only the headers with 0 content length. /// </summary> public void Send(HttpSendOptions options) { _lock.Take(); // process the send options ProcessOptions(options); SendHeaders(0); _lock.Release(); }
/// <summary> /// Send the specified byte collection to the client. This may take a number of buffers to complete /// and therefore not be synchronous. /// </summary> public void Send(byte[] buffer, int offset, int count, HttpSendOptions options = null, IAction onSent = null) { _lock.Take(); // process the options ProcessOptions(options); _onSent = onSent; // perform the send SendBytes(buffer, offset, count); }
/// <summary> /// Send the specified element to the client. /// </summary> public unsafe void Send(Element element, bool buildStyle = true, HttpSendOptions options = null, IAction onSent = null) { Log.Info("Sending element '" + element + "' to client " + this + "."); if (buildStyle) { // build the style var style = element.FindChild("style"); style.ContentString = element.BuildCss(); element.EncodeContent = false; } // build the element into a string string str = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" + element.Build(); _lock.Take(); // have the options been specified? yes, process them if (options == null || options.ContentType == null) { Headers[HttpResponseHeader.ContentType] = "text/html; charset=UTF-8"; } ProcessOptions(options); _onSent = onSent; // resize buffer as required var buffer = BufferCache.Get(str.Length + str.Length); // get pointers to the string and the send byte buffer int count; fixed(char *src = str) fixed(byte *dst = &buffer[0]) { // get the bytes that the string represents count = _encoder.GetBytes(src, str.Length, dst, buffer.Length, true); } // send the bytes SendBytes(buffer, 0, count); BufferCache.Set(buffer); }
/// <summary> /// Start sending the stream. The client is locked until the stream is complete. /// </summary> protected void StartSendStream(Stream stream, int length, HttpSendOptions options = null, IAction onSent = null) { // does the buffer need to be compressed? if (options != null && options.Compression == DecompressionMethods.None || _defaultCompression == DecompressionMethods.None) { // no, take the send lock _lock.Take(); // process the options ProcessOptions(options); // send the header SendHeaders(length); Log.Info("Sending stream of length '" + length + "' to client " + this + "."); // send the stream Socket.Send(stream, length); _lock.Release(); // is the callback set? yes, run it if (onSent != null) { onSent.Run(); } } else { // yes, compress the bytes StreamHelper.Compress(new Act <MemoryStream, HttpSendOptions, IAction>(StartSendStream, null, options, onSent), options == null ? _defaultCompression : options.Compression, System.IO.Compression.CompressionLevel.Fastest, stream, length, false); } }
/// <summary> /// Begin sending the stream of a web resource and prepare to release the resource lock on completion. /// </summary> protected void SendResource(Stream stream, Lock resourceLock, WebResource resource, HttpSendOptions options, IAction onSent) { // was the stream retrieved? if (stream == null) { // no, unlock the resource resourceLock.Release(); // callback _client.OnErrorRoll.Run(new Exception("A resource stream '" + resource.FullPath + "' was unable to be resolved.")); return; } _lock.Take(); if (onSent == null) { _onSent = new ActionSet(resourceLock.Release); } else { _onSent = new ActionPair(resourceLock.Release, onSent); } // set the content type ProcessOptions(options); if (options == null) { Headers[HttpResponseHeader.ContentType] = resource.MimeType; } int length = resource.Size == -1 ? (int)stream.Length : (int)resource.Size; // start sending the stream StartSendStream(stream, length, resource); }
/// <summary> /// Send the specified stream, optionally with a byte length. This method is /// asynchronous and will close the stream on completion. /// </summary> public void Send(Stream stream, int length = -1, HttpSendOptions options = null, IAction onSent = null) { // start sending the stream StartSendStream(stream, length == -1 ? (int)(stream.Length - stream.Position) : length, options, onSent); }