你的位置:首页 > ASP.net教程

[ASP.net教程]利用HttpListener创建简单的HTTP服务

using System;using System.Collections.Generic;using System.IO;using System.IO.Compression;using System.Linq;using System.Net;using System.Reflection;using System.Text;using System.Text.RegularExpressions;using System.Threading;using System.Threading.Tasks;public class HttpServer : IDisposable{  private const string NotFoundResponse = "<!doctype html><html><body>Resource not found</body></html>";  private readonly HttpListener httpListener;  private readonly CancellationTokenSource cts = new CancellationTokenSource();  private readonly string prefixPath;  private Task processingTask;  public HttpServer(string listenerUriPrefix)  {    this.prefixPath = ParsePrefixPath(listenerUriPrefix);    this.httpListener = new HttpListener();    this.httpListener.Prefixes.Add(listenerUriPrefix);  }  private static string ParsePrefixPath(string listenerUriPrefix)  {    var match = Regex.Match(listenerUriPrefix, @"http://(?:[^/]*)(?:\:\d+)?/(.*)");    if (match.Success)    {      return match.Groups[1].Value.ToLowerInvariant();    }    else    {      return string.Empty;    }  }  public void Start()  {    this.httpListener.Start();    this.processingTask = Task.Factory.StartNew(async () => await ProcessRequests(), TaskCreationOptions.LongRunning);  }  private async Task ProcessRequests()  {    while (!this.cts.IsCancellationRequested)    {      try      {        var context = await this.httpListener.GetContextAsync();        try        {          await ProcessRequest(context).ConfigureAwait(false);          context.Response.Close();        }        catch (Exception ex)        {          context.Response.StatusCode = 500;          context.Response.StatusDescription = "Internal Server Error";          context.Response.Close();          Console.WriteLine("Error processing HTTP request\n{0}", ex);        }      }      catch (ObjectDisposedException ex)      {        if ((ex.ObjectName == this.httpListener.GetType().FullName) && (this.httpListener.IsListening == false))        {          return; // listener is closed/disposed        }        Console.WriteLine("Error processing HTTP request\n{0}", ex);      }      catch (Exception ex)      {        HttpListenerException httpException = ex as HttpListenerException;        if (httpException == null || httpException.ErrorCode != 995)// IO operation aborted        {          Console.WriteLine("Error processing HTTP request\n{0}", ex);        }      }    }  }  private Task ProcessRequest(HttpListenerContext context)  {    if (context.Request.HttpMethod.ToUpperInvariant() != "GET")    {      return WriteNotFound(context);    }    var urlPath = context.Request.RawUrl.Substring(this.prefixPath.Length)      .ToLowerInvariant();    switch (urlPath)    {      case "/":        if (!context.Request.Url.ToString().EndsWith("/"))        {          context.Response.Redirect(context.Request.Url + "/");          context.Response.Close();          return Task.FromResult(0);        }        else        {          return WriteString(context, "Hello World!", "text/plain");        }      case "/favicon.ico":        return WriteFavIcon(context);      case "/ping":        return WritePong(context);    }    return WriteNotFound(context);  }  private static Task WritePong(HttpListenerContext context)  {    return WriteString(context, "pong", "text/plain");  }  private static async Task WriteFavIcon(HttpListenerContext context)  {    context.Response.ContentType = "image/png";    context.Response.StatusCode = 200;    context.Response.StatusDescription = "OK";    using (var stream = File.Open("icon.png", FileMode.Open))    {      var output = context.Response.OutputStream;      await stream.CopyToAsync(output);    }  }  private static Task WriteNotFound(HttpListenerContext context)  {    return WriteString(context, NotFoundResponse, "text/plain", 404, "NOT FOUND");  }  private static async Task WriteString(HttpListenerContext context, string data, string contentType,    int httpStatus = 200, string httpStatusDescription = "OK")  {    AddCORSHeaders(context.Response);    AddNoCacheHeaders(context.Response);    context.Response.ContentType = contentType;    context.Response.StatusCode = httpStatus;    context.Response.StatusDescription = httpStatusDescription;    var acceptsGzip = AcceptsGzip(context.Request);    if (!acceptsGzip)    {      using (var writer = new StreamWriter(context.Response.OutputStream, Encoding.UTF8, 4096, true))      {        await writer.WriteAsync(data).ConfigureAwait(false);      }    }    else    {      context.Response.AddHeader("Content-Encoding", "gzip");      using (GZipStream gzip = new GZipStream(context.Response.OutputStream, CompressionMode.Compress, true))      using (var writer = new StreamWriter(gzip, Encoding.UTF8, 4096, true))      {        await writer.WriteAsync(data).ConfigureAwait(false);      }    }  }  private static bool AcceptsGzip(HttpListenerRequest request)  {    string encoding = request.Headers["Accept-Encoding"];    if (string.IsNullOrEmpty(encoding))    {      return false;    }    return encoding.Contains("gzip");  }  private static void AddNoCacheHeaders(HttpListenerResponse response)  {    response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");    response.Headers.Add("Pragma", "no-cache");    response.Headers.Add("Expires", "0");  }  private static void AddCORSHeaders(HttpListenerResponse response)  {    response.Headers.Add("Access-Control-Allow-Origin", "*");    response.Headers.Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");  }  private void Stop()  {    cts.Cancel();    if (processingTask != null && !processingTask.IsCompleted)    {      processingTask.Wait();    }    if (this.httpListener.IsListening)    {      this.httpListener.Stop();      this.httpListener.Prefixes.Clear();    }  }  public void Dispose()  {    this.Stop();    this.httpListener.Close();    using (this.cts) { }    using (this.httpListener) { }  }}