-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Switch from HttpListener to TcpListener to make calls from LAN possible
- Loading branch information
1 parent
9bd5a4c
commit a92d00b
Showing
6 changed files
with
68 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,136 +1,89 @@ | ||
using System; | ||
using System.Configuration; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Net.Sockets; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
using Newtonsoft.Json; | ||
|
||
namespace TelemetryJsonService | ||
{ | ||
// Source: http://www.gabescode.com/dotnet/2018/11/01/basic-HttpListener-web-service.html | ||
internal static class DataService | ||
{ | ||
private static volatile bool _keepGoing = true; | ||
|
||
private static HttpListener Listener; | ||
private static Task _mainLoop; | ||
|
||
public static string Address { get; private set; } | ||
public static int Port { get; private set; } | ||
public static string JsonData { get; set; } | ||
|
||
public static void StartWebServer() | ||
{ | ||
if (_mainLoop != null && !_mainLoop.IsCompleted) return; | ||
|
||
Address = ConfigurationManager.AppSettings["address"]; | ||
Port = int.Parse(ConfigurationManager.AppSettings["port"]); | ||
Listener = new HttpListener { Prefixes = { $"http://{Address}:{Port}/" } }; | ||
_mainLoop = MainLoop(); | ||
Task.Run(() => StartTcpListener(Port)); | ||
} | ||
|
||
/// <summary> | ||
/// Call this to stop the web server. It will not kill any requests currently being processed. | ||
/// </summary> | ||
public static void StopWebServer() | ||
static async Task StartTcpListener(int port) | ||
{ | ||
_keepGoing = false; | ||
lock (Listener) | ||
TcpListener tcpListener = new TcpListener(IPAddress.Any, port); | ||
tcpListener.Start(); | ||
Console.WriteLine($"Listening on port {port}"); | ||
|
||
try | ||
{ | ||
//Use a lock so we don't kill a request that's currently being processed | ||
Listener.Stop(); | ||
while (true) | ||
{ | ||
TcpClient client = await tcpListener.AcceptTcpClientAsync(); | ||
_ = HandleClientAsync(client); | ||
} | ||
} | ||
try | ||
catch (Exception ex) | ||
{ | ||
_mainLoop.Wait(); | ||
Console.WriteLine($"Exception: {ex.Message}"); | ||
} | ||
catch { /* ¯\_(ツ)_/¯ */ } | ||
} | ||
|
||
/// <summary> | ||
/// The main loop to handle requests into the HttpListener | ||
/// </summary> | ||
/// <returns></returns> | ||
private static async Task MainLoop() | ||
{ | ||
Listener.Start(); | ||
while (_keepGoing) | ||
finally | ||
{ | ||
try | ||
{ | ||
//GetContextAsync() returns when a new request come in | ||
var context = await Listener.GetContextAsync(); | ||
lock (Listener) | ||
{ | ||
if (_keepGoing) ProcessRequest(context); | ||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
if (e is HttpListenerException) return; //this gets thrown when the listener is stopped | ||
//TODO: Log the exception | ||
} | ||
tcpListener.Stop(); | ||
Console.WriteLine("Listener stopped."); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Handle an incoming request | ||
/// </summary> | ||
/// <param name="context">The context of the incoming request</param> | ||
private static void ProcessRequest(HttpListenerContext context) | ||
static async Task HandleClientAsync(TcpClient tcpClient) | ||
{ | ||
using (var response = context.Response) | ||
using (NetworkStream stream = tcpClient.GetStream()) | ||
{ | ||
try | ||
{ | ||
var handled = false; | ||
|
||
// CORS | ||
response.AppendHeader("Access-Control-Allow-Origin", "*"); | ||
byte[] buffer = new byte[1024]; | ||
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); | ||
string request = Encoding.UTF8.GetString(buffer, 0, bytesRead); | ||
Console.WriteLine($"Received request: {request}"); | ||
|
||
switch (context.Request.Url.AbsolutePath) | ||
{ | ||
// Define routes | ||
case "/": | ||
switch (context.Request.HttpMethod) | ||
{ | ||
case "OPTIONS": | ||
response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); | ||
response.AddHeader("Access-Control-Allow-Methods", "OPTIONS, GET"); | ||
response.AddHeader("Access-Control-Max-Age", "1728000"); | ||
response.StatusCode = 200; | ||
handled = true; | ||
break; | ||
// Parse the HTTP method | ||
string httpMethod = "GET"; | ||
string[] requestLines = request.Split('\n'); | ||
string firstLine = requestLines.FirstOrDefault(); | ||
string[] tokens = firstLine?.Split(' '); | ||
|
||
case "GET": | ||
response.ContentType = "application/json"; | ||
var buffer = Encoding.UTF8.GetBytes(JsonData); | ||
response.ContentLength64 = buffer.Length; | ||
response.OutputStream.Write(buffer, 0, buffer.Length); | ||
handled = true; | ||
break; | ||
|
||
} | ||
break; | ||
} | ||
if (!handled) | ||
{ | ||
response.StatusCode = 404; | ||
} | ||
} | ||
catch (Exception e) | ||
if (tokens != null && tokens.Length >= 2) | ||
{ | ||
//Return the exception details to the client | ||
response.StatusCode = 500; | ||
response.ContentType = "application/json"; | ||
var buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(e)); | ||
response.ContentLength64 = buffer.Length; | ||
response.OutputStream.Write(buffer, 0, buffer.Length); | ||
httpMethod = tokens[0]; | ||
Console.WriteLine($"HTTP Method: {httpMethod}"); | ||
} | ||
|
||
string responseText = "HTTP/1.1 200 OK\r\n" + | ||
"Content-Type: application/json\r\n" + | ||
"Access-Control-Allow-Origin: *\r\n" + // Allow requests from any origin | ||
"Access-Control-Allow-Methods: GET, OPTIONS\r\n" + // Specify allowed methods | ||
"Access-Control-Allow-Headers: Content-Type, Accept, X-Requested-With\r\n\r\n"; // Specify allowed headers | ||
|
||
//TODO: Log the exception | ||
if ( httpMethod == "GET" ) | ||
{ | ||
responseText += JsonData; | ||
} | ||
|
||
byte[] responseBytes = Encoding.UTF8.GetBytes(responseText); | ||
await stream.WriteAsync(responseBytes, 0, responseBytes.Length); | ||
} | ||
|
||
tcpClient.Close(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters