/// <summary> /// Converts an object to an HTTP response message body. /// content may be null. /// If the content is null, the string "null" is produced as /// its representation. Otherwise, the object's ToString /// method is called for producing the representation. /// Preconditions /// context != null /// Postconditions /// "context.RequestContent contains serialized representation of /// content object" /// context.ResponseContentType == "text/plain" /// </summary> public static void Serialize(RequestHandlerContext context, object content) { string s = (content != null) ? content.ToString() : "null"; context.SetResponse(s, "text/plain"); }
public void HandleRequest(RequestHandlerContext context) { Contract.Requires(context != null); if (!IsOpen) { Open(); } context.ResponseMaxAge = 0; // no caching (default is -1, i.e., no cache header) var s = ""; // Service Uptime TimeSpan delta = Diagnostics.ServiceUptime; s = s + "Service Uptime\t" + delta + "\r\n"; // accept errors (e.g., timeouts) s = s + "Accept Errors\t" + Diagnostics.AcceptErrors + "\r\n"; // accept failures (unknown exceptions) s = s + "Accept Failures\t" + Diagnostics.AcceptFailures + "\r\n"; // requests total s = s + "Requests Total\t" + Diagnostics.RequestsTotal + "\r\n"; // request handler errors (e.g., timeouts) s = s + "Request Handler Errors\t" + Diagnostics.RequestHandlerErrors + "\r\n"; // request handler failures (unknown exceptions) s = s + "Request Handler Failures\t" + Diagnostics.RequestHandlerFailures + "\r\n"; // free memory s = s + "Available Heap Space\t" + Debug.GC(true) + "\r\n"; context.SetResponse(s, "text/plain"); }
static void HandleOn(RequestHandlerContext context) { lightWrite(true); string s = "<h1>ON " + DateTime.Now.ToString(); context.SetResponse(s, "text/html"); }
/// <summary> /// Interprets an HTTP request and produces an appropriate HTTP /// response. /// If the manipulated variable is not open, Open is called first. /// Preconditions /// context != null /// </summary> /// <param name="context">Provides access to the received request, /// the server, and to the (empty) response.</param> public void HandleRequest(RequestHandlerContext context) { Contract.Requires(context != null); if (!initialized) { Open(); } if (context.RequestMethod == "PUT") { object setpoint; if (FromHttpRequest(context, out setpoint)) { // setpoint may be null ToActuator(setpoint); lastSetpoint = setpoint; context.ResponseStatusCode = 200; // OK } else { context.ResponseStatusCode = 400; // Bad Request } } else if (context.RequestMethod == "GET") { // lastSetpoint may be null ToHttpResponse(context, lastSetpoint); context.ResponseStatusCode = 200; // OK } else { context.ResponseStatusCode = 405; // Method Not Allowed } }
static void HandleLedTargetHtml(RequestHandlerContext context) { string requestUri = context.BuildRequestUri("/led/target"); var script = @"<html> <head> <script type=""text/javascript""> var r; try { r = new XMLHttpRequest(); } catch (e) { r = new ActiveXObject('Microsoft.XMLHTTP'); } function put (content) { r.open('PUT', '" + requestUri + @"'); r.setRequestHeader('Content-Type', 'text/plain'); r.send(content); } </script> </head> <body> <p> <input type=""button"" value=""Switch LED on"" onclick=""put('true')""/> <input type=""button"" value=""Switch LED off"" onclick=""put('false')""/> <input type=""button"" value=""Bah"" onclick=""put('bah')""/> </p> </body> </html>"; context.SetResponse(script, "text/html"); }
static void HandleBlinkTargetHtml(RequestHandlerContext context) { string requestUri = context.BuildRequestUri("/blinkingPeriod/target"); var script = @"<html> <head> <script type=""text/javascript""> var r; try { r = new XMLHttpRequest(); } catch (e) { r = new ActiveXObject('Microsoft.XMLHTTP'); } function put (content) { r.open('PUT', '" + requestUri + @"'); r.setRequestHeader(""Content-Type"", ""text/plain""); r.send(document.getElementById(""period"").value); } </script> </head> <body> <p> <input type=""text"" value=""500"" id=""period""> <input type=""button"" value=""Set"" onclick=""put()""/> </p> </body> </html>"; context.SetResponse(script, "text/html"); }
/// <summary> /// Converts an HTTP request message body to a double object. /// content is not null if conversion was successful. /// The return value indicates whether conversion was successful. /// Preconditions /// context != null /// Postconditions /// Result => /// content != null /// content is double /// "content contains deserialized representation of /// context.RequestContent" /// !Result => /// content == null /// "context.RequestContent could not be converted to a /// C# object" /// </summary> public static bool TryDeserializeDouble(RequestHandlerContext context, out object content) { string s = context.RequestContent; double x = 0; int decimalDiv = 0; content = null; foreach (char c in s) { if ((c == '.') && (decimalDiv == 0)) { decimalDiv = 1; } else if ((c < '0') || (c > '9')) { return(false); } else { x = x * 10; decimalDiv = decimalDiv * 10; x = x + (int)(c - '0'); } } if (decimalDiv != 0) { x = x / decimalDiv; } content = x; return(true); }
/// <summary> /// Converts an HTTP request message body to a bool object. /// content is not null if conversion was successful. /// The return value indicates whether conversion was successful. /// Preconditions /// context != null /// Postconditions /// Result => /// content != null /// content is bool /// "content contains deserialized representation of /// context.RequestContent" /// !Result => /// content == null /// "context.RequestContent could not be converted to a /// C# object" /// </summary> public static bool TryDeserializeBool( RequestHandlerContext context, out object content) { string s = context.RequestContent; if (s == "true") { content = true; return true; } else if (s == "false") { content = false; return true; } else { content = null; return false; } }
static void HandleGetAboutHtml(RequestHandlerContext context) { string s = "<html>\r\n" + "\t<body>\r\n" + "\t\t<h1>About</h1>\r\n" + "\t\t<p>This is a Netduino Plus 2 running .net Micro Framework</p>\r\n" + "\t</body>\r\n" + "</html>"; context.SetResponse(s, "text/html"); }
static void HandleGetHelloHtml(RequestHandlerContext context) { string s = "<html>\r\n" + "\t<body>\r\n" + "\t\tHello <strong>Web</strong> at " + DateTime.Now + "\r\n" + "\t</body>\r\n" + "</html>"; context.SetResponse(s, "text/html"); }
/// <summary> /// Interprets an HTTP request and produces an appropriate HTTP /// response. /// If the measured variable is not open, Open is called first. /// Preconditions /// context != null /// </summary> /// <param name="context">Provides access to the received request, /// the server, and to the (empty) response.</param> public void HandleRequest(RequestHandlerContext context) { Contract.Requires(context != null); if (!initialized) { Open(); } context.ResponseMaxAge = 0; // no caching (default is -1, i.e., no cache header) object sample = FromSensor(); // sample may be null ToHttpResponse(context, sample); }
static void HandleHello(RequestHandlerContext context) { onboardLedBlink = !onboardLedBlink; var a1Uri = "/" + context.RequestUri.Split('/')[1] + "/a1"; var d12Uri = "/" + context.RequestUri.Split('/')[1] + "/d12"; string s = "<h1>Hello via Relay<h2>Led Blink is " + onboardLedBlink.ToString() + "<p> RequestUri is: " + context.RequestUri + "<p> Try <a href='" + a1Uri + "'>Analog Input A1</a> and <a href='" + d12Uri + "'>Digital Input D12</a> for sensors as well<p> PUT to /d13 for led Actuator on Pin 13!"; s = s + "<p> use a PUT generator like <a href='http://www.hurl.it/hurls/90adc685db37c71ccb9576b0d115fbc8809faa1c/aea0240d19dc34535db35a9e64ed00b195d3bb53'>this to turn off,</a>"; s = s + "<p> or like <a href='http://www.hurl.it/hurls/1bf8d43ca68c6a17ebdf77409c437031fe21d8e5/76fe79f73b808d69dddbb8caffd070ee9e90137b'>this to turn on.</a>"; s = s + "<p>In order to use this fuctionality in a trigger to D11 from Xively you must create POST requests for <a href='http://www.hurl.it/hurls/3703de296f48844bef801e59e3486229e805f462/f5b7a99f9e6ec038f0196f4945f539ebbbdcc9cb#'>on</a> and <a href='http://www.hurl.it/hurls/8a8463b4d5900d51fe8a34cbf4d51d20ebd16c50/3211ff2d1b035f914f9af5431f5e133c5b1d5001'>off</a>."; context.SetResponse(s, "text/html"); }
/// <summary> /// Converts an HTTP request message body to an int object. /// content is not null if conversion was successful. /// The return value indicates whether conversion was successful. /// Preconditions /// context != null /// Postconditions /// Result => /// content != null /// content is int /// "content contains deserialized representation of /// context.RequestContent" /// !Result => /// content == null /// "context.RequestContent could not be converted to a /// C# object" /// </summary> public static bool TryDeserializeInt(RequestHandlerContext context, out object content) { string s = context.RequestContent; int x; if (Utilities.TryParseUInt32(s, out x)) { content = x; return(true); } else { content = null; return(false); } }
/// <summary> /// Converts an HTTP request message body to a bool object. /// content is not null if conversion was successful. /// The return value indicates whether conversion was successful. /// Preconditions /// context != null /// Postconditions /// Result => /// content != null /// content is bool /// "content contains deserialized representation of /// context.RequestContent" /// !Result => /// content == null /// "context.RequestContent could not be converted to a /// C# object" /// </summary> public static bool TryDeserializeBool( RequestHandlerContext context, out object content) { string s = context.RequestContent; if (s == "true") { content = true; return(true); } else if (s == "false") { content = false; return(true); } else { content = null; return(false); } }
public void HandleRequest(RequestHandlerContext context) { Contract.Requires(context != null); if (!IsOpen) { Open(); } context.ResponseMaxAge = 0; // no caching (default is -1, i.e., no cache header) var s = ""; // Service Uptime TimeSpan delta = Diagnostics.ServiceUptime; s = s + "Service Uptime\t" + delta + "\r\n"; // accept errors (e.g., timeouts) s = s + "Accept Errors\t" + Diagnostics.AcceptErrors + "\r\n"; // accept failures (unknown exceptions) s = s + "Accept Failures\t" + Diagnostics.AcceptFailures + "\r\n"; // requests total s = s + "Requests Total\t" + Diagnostics.RequestsTotal + "\r\n"; // request handler errors (e.g., timeouts) s = s + "Request Handler Errors\t" + Diagnostics.RequestHandlerErrors + "\r\n"; // request handler failures (unknown exceptions) s = s + "Request Handler Failures\t" + Diagnostics.RequestHandlerFailures + "\r\n"; // free memory s = s + "Available Heap Space\t" + Debug.GC(true) + "\r\n"; context.SetResponse(s, "text/plain"); }
static void HandleTemp(RequestHandlerContext context) { string s = "<h1>Temp " + voltagePort.Read().ToString(); context.SetResponse(s, "text/html"); }
static void HandleToggle(RequestHandlerContext context) { lightWrite(!lOn); string s = "<h1>Toggle -- was on: " +(!lOn).ToString() + " " + DateTime.Now.ToString(); context.SetResponse(s, "text/html"); }
/// <summary> /// Private method that handles an incoming request. /// It sets up a RequestHandlerContext instance with the data from /// the incoming HTTP request, finds a suitable request handler to /// produce the response, and then sends the response as an HTTP /// response back to the client. /// Preconditions /// "connection is open" /// serviceRoot != null /// serviceRoot.Length > 8 /// "serviceRoot starts with 'http://' and ends with '/'" /// requestRouting != null /// </summary> /// <param name="connection">Open TCP/IP connection</param> /// <param name="serviceRoot">The absolute URI that is a prefix of /// all request URIs that this web service supports. It must start /// with "http://" and must end with "/".</param> /// <param name="relayDomain">Host name or Internet address of the /// relay to be used, or null if no relay is used</param> /// <param name="requestRouting">Collection of /// { request pattern, request handler} /// pairs</param> /// <param name="connectionClose">Return parameter that indicates /// that the connection should be closed after this call. This may /// be because the incoming request has a "Connection: close" /// header, because the request handler has set the /// ConnectionClose property, or because some error occurred. /// </param> internal static void ConsumeRequest(Stream connection, string serviceRoot, string relayDomain, RequestRouting requestRouting, ref bool connectionClose) { Contract.Requires(connection != null); Contract.Requires(serviceRoot != null); Contract.Requires(serviceRoot.Length > 8); Contract.Requires(serviceRoot.Substring(0, 7) == "http://"); Contract.Requires(serviceRoot[serviceRoot.Length - 1] != '/'); Contract.Requires(requestRouting != null); // initialization -------------------------------------------- HttpReader reader = new HttpReader(); HttpWriter writer = new HttpWriter(); var context = new RequestHandlerContext(serviceRoot, relayDomain); // Both client and server may request closing the connection. // Initially, we assume that neither one wants to close the // connection. context.ConnectionClose = false; // receive request ------------------------------------------- reader.Attach(connection); // request line string httpMethod; string requestUri; string httpVersion; reader.ReadStringToBlank(out httpMethod); reader.ReadStringToBlank(out requestUri); reader.ReadFieldValue(out httpVersion); if (reader.Status != HttpStatus.BeforeContent) // error { reader.Detach(); connectionClose = true; return; } context.RequestMethod = httpMethod; context.RequestUri = requestUri; // ignore version // headers string fieldName; string fieldValue; int requestContentLength = -1; reader.ReadFieldName(out fieldName); while (reader.Status == HttpStatus.BeforeContent) { reader.ReadFieldValue(out fieldValue); if (fieldValue != null) { Contract.Assert(reader.Status == HttpStatus.BeforeContent); if (fieldName == "Connection") { context.ConnectionClose = ((fieldValue == "close") || (fieldValue == "Close")); } else if (fieldName == "Content-Type") { context.RequestContentType = fieldValue; } else if (fieldName == "Content-Length") { if (Utilities.TryParseUInt32(fieldValue, out requestContentLength)) { // content length is now known } else { reader.Status = HttpStatus.SyntaxError; reader.Detach(); connectionClose = true; return; } } } else { // it's ok to skip header whose value is too long } Contract.Assert(reader.Status == HttpStatus.BeforeContent); reader.ReadFieldName(out fieldName); } if (reader.Status != HttpStatus.InContent) { reader.Detach(); connectionClose = true; return; } // content if (requestContentLength > 0) { // receive content var requestContent = new byte[requestContentLength]; int toRead = requestContentLength; var read = 0; while ((toRead > 0) && (read >= 0)) { // already read: requestContentLength - toRead read = reader.ReadContent(requestContent, requestContentLength - toRead, toRead); if (read < 0) { break; } // timeout or shutdown toRead = toRead - read; } try { char[] chars = Encoding.UTF8.GetChars(requestContent); context.RequestContent = new string(chars); } catch (Exception) { context.RequestContentType = "text/plain"; context.RequestContent = "request content is not in UTF8 format"; Debug.Print(context.RequestContent); // TODO signal wrong content format through HTTP status code 400 (Bad Request)? } } reader.Detach(); if (reader.Status != HttpStatus.InContent) { connectionClose = true; return; } // delegate request processing to a request handler ---------- var match = false; foreach (Element e in requestRouting) { if (context.RequestMatch(e)) { bool connClose = context.ConnectionClose; context.ResponseStatusCode = -1; // undefined Contract.Requires(context.ResponseContentType != null); Contract.Requires(context.ResponseContentType == "text/plain"); // default try { e.Handler(context); } catch (Exception h) { Debug.Print("exception in request handler: " + h); // TODO how to better handle handler exceptions? throw; // rethrow, to avoid masking of errors } Contract.Ensures(context.ResponseStatusCode >= 100); Contract.Ensures(context.ResponseStatusCode < 600); Contract.Ensures(context.ResponseContentType != null); Contract.Ensures(context.ResponseContentType.Length > 0); // make sure that handler has not reset connectionClose flag: Contract.Ensures((!connClose) || (context.ConnectionClose)); match = true; break; } } if (!match) { context.ResponseStatusCode = 404; // Not Found context.ResponseContentType = "text/plain"; context.ResponseContent = null; } uint availableMemory = Debug.GC(true); Debug.Print(context.RequestMethod + " " + context.RequestUri + " -> " + context.ResponseStatusCode + " [" + availableMemory + "]"); // send response --------------------------------------------- writer.Attach(connection); // status line writer.WriteString("HTTP/1.1 "); writer.WriteString(context.ResponseStatusCode.ToString()); writer.WriteLine(" "); // omit optional reason phrase // headers if (connectionClose) // TODO (context.ConnectionClose) { writer.WriteLine("Connection: close"); } byte[] responseBuffer = null; var responseLength = 0; if (context.ResponseContent != null) { responseBuffer = Encoding.UTF8.GetBytes(context.ResponseContent); responseLength = responseBuffer.Length; writer.WriteString("Content-Type: "); writer.WriteLine(context.ResponseContentType); } else { responseLength = 0; } writer.WriteString("Content-Length: "); writer.WriteLine(responseLength.ToString()); if (context.ResponseMaxAge > 0) { writer.WriteLine("Cache-Control: max-age=" + context.ResponseMaxAge); } else if (context.ResponseMaxAge == 0) { writer.WriteLine("Cache-Control: no-cache"); } // content writer.WriteBeginOfContent(); if (context.ResponseContent != null) // send content { writer.WriteContent(responseBuffer, 0, responseLength); } writer.Detach(); connectionClose = context.ConnectionClose; }
static void HandleHello( RequestHandlerContext context) { string s = "<h1> hello webber " + DateTime.Now.ToString(); context.SetResponse(s, "text/html"); }
static void HandlePostOff(RequestHandlerContext context) { context.ResponseStatusCode = 200; lightWrite(false); }
/// <summary> /// Private method that handles an incoming request. /// It sets up a RequestHandlerContext instance with the data from /// the incoming HTTP request, finds a suitable request handler to /// produce the response, and then sends the response as an HTTP /// response back to the client. /// Preconditions /// "connection is open" /// serviceRoot != null /// serviceRoot.Length > 8 /// "serviceRoot starts with 'http://' and ends with '/'" /// requestRouting != null /// </summary> /// <param name="connection">Open TCP/IP connection</param> /// <param name="serviceRoot">The absolute URI that is a prefix of /// all request URIs that this web service supports. It must start /// with "http://" and must end with "/".</param> /// <param name="relayDomain">Host name or Internet address of the /// relay to be used, or null if no relay is used</param> /// <param name="requestRouting">Collection of /// { request pattern, request handler} /// pairs</param> /// <param name="connectionClose">Return parameter that indicates /// that the connection should be closed after this call. This may /// be because the incoming request has a "Connection: close" /// header, because the request handler has set the /// ConnectionClose property, or because some error occurred. /// </param> internal static void ConsumeRequest(Stream connection, string serviceRoot, string relayDomain, RequestRouting requestRouting, ref bool connectionClose) { Contract.Requires(connection != null); Contract.Requires(serviceRoot != null); Contract.Requires(serviceRoot.Length > 8); Contract.Requires(serviceRoot.Substring(0, 7) == "http://"); Contract.Requires(serviceRoot[serviceRoot.Length - 1] != '/'); Contract.Requires(requestRouting != null); // initialization -------------------------------------------- HttpReader reader = new HttpReader(); HttpWriter writer = new HttpWriter(); var context = new RequestHandlerContext(serviceRoot, relayDomain); // Both client and server may request closing the connection. // Initially, we assume that neither one wants to close the // connection. context.ConnectionClose = false; // receive request ------------------------------------------- reader.Attach(connection); // request line string httpMethod; string requestUri; string httpVersion; reader.ReadStringToBlank(out httpMethod); reader.ReadStringToBlank(out requestUri); reader.ReadFieldValue(out httpVersion); if (reader.Status != HttpStatus.BeforeContent) // error { reader.Detach(); connectionClose = true; return; } context.RequestMethod = httpMethod; context.RequestUri = requestUri; // ignore version // headers string fieldName; string fieldValue; int requestContentLength = -1; reader.ReadFieldName(out fieldName); while (reader.Status == HttpStatus.BeforeContent) { reader.ReadFieldValue(out fieldValue); if (fieldValue != null) { Contract.Assert(reader.Status == HttpStatus.BeforeContent); if (fieldName == "Connection") { context.ConnectionClose = ((fieldValue == "close") || (fieldValue == "Close")); } else if (fieldName == "Content-Type") { context.RequestContentType = fieldValue; } else if (fieldName == "Content-Length") { if (Utilities.TryParseUInt32(fieldValue, out requestContentLength)) { // content length is now known } else { reader.Status = HttpStatus.SyntaxError; reader.Detach(); connectionClose = true; return; } } } else { // it's ok to skip header whose value is too long } Contract.Assert(reader.Status == HttpStatus.BeforeContent); reader.ReadFieldName(out fieldName); } if (reader.Status != HttpStatus.InContent) { reader.Detach(); connectionClose = true; return; } // content if (requestContentLength > 0) { // receive content var requestContent = new byte[requestContentLength]; int toRead = requestContentLength; var read = 0; while ((toRead > 0) && (read >= 0)) { // already read: requestContentLength - toRead read = reader.ReadContent(requestContent, requestContentLength - toRead, toRead); if (read < 0) { break; } // timeout or shutdown toRead = toRead - read; } try { char[] chars = Encoding.UTF8.GetChars(requestContent); context.RequestContent = new string(chars); } catch (Exception) { context.RequestContentType = "text/plain"; context.RequestContent = "request content is not in UTF8 format"; Debug.Print(context.RequestContent); // TODO signal wrong content format through HTTP status code 400 (Bad Request)? } } reader.Detach(); if (reader.Status != HttpStatus.InContent) { connectionClose = true; return; } // delegate request processing to a request handler ---------- var match = false; foreach (Element e in requestRouting) { if (context.RequestMatch(e)) { bool connClose = context.ConnectionClose; context.ResponseStatusCode = -1; // undefined Contract.Requires(context.ResponseContentType != null); Contract.Requires(context.ResponseContentType == "text/plain"); // default try { e.Handler(context); } catch (Exception h) { Debug.Print("exception in request handler: " + h); // TODO how to better handle handler exceptions? throw; // rethrow, to avoid masking of errors } Contract.Ensures(context.ResponseStatusCode >= 100); Contract.Ensures(context.ResponseStatusCode < 600); Contract.Ensures(context.ResponseContentType != null); Contract.Ensures(context.ResponseContentType.Length > 0); // make sure that handler has not reset connectionClose flag: Contract.Ensures((!connClose) || (context.ConnectionClose)); match = true; break; } } if (!match) { context.ResponseStatusCode = 404; // Not Found context.ResponseContentType = "text/plain"; context.ResponseContent = null; } uint availableMemory = Debug.GC(true); Debug.Print(context.RequestMethod + " " + context.RequestUri + " -> " + context.ResponseStatusCode + " [" + availableMemory + "]"); // send response --------------------------------------------- writer.Attach(connection); // status line writer.WriteString("HTTP/1.1 "); writer.WriteString(context.ResponseStatusCode.ToString()); writer.WriteLine(" "); // omit optional reason phrase // headers if (connectionClose) // TODO (context.ConnectionClose) { writer.WriteLine("Connection: close"); } byte[] responseBuffer = null; var responseLength = 0; if (context.ResponseContent != null) { responseBuffer = Encoding.UTF8.GetBytes(context.ResponseContent); responseLength = responseBuffer.Length; writer.WriteString("Content-Type: "); writer.WriteLine(context.ResponseContentType); } else { responseLength = 0; } writer.WriteString("Content-Length: "); writer.WriteLine(responseLength.ToString()); if (context.ResponseMaxAge > 0) { writer.WriteLine("Cache-Control: max-age=" + context.ResponseMaxAge); } else if (context.ResponseMaxAge == 0) { writer.WriteLine("Cache-Control: no-cache"); } // content writer.WriteBeginOfContent(); if (context.ResponseContent != null) // send content { writer.WriteContent(responseBuffer, 0, responseLength); } writer.Detach(); connectionClose = context.ConnectionClose; }
/// <summary> /// Interprets an HTTP request and produces an appropriate HTTP /// response. /// If the manipulated variable is not open, Open is called first. /// Preconditions /// context != null /// </summary> /// <param name="context">Provides access to the received request, /// the server, and to the (empty) response.</param> public void HandleRequest(RequestHandlerContext context) { Contract.Requires(context != null); if (!initialized) { Open(); } if (context.RequestMethod == "PUT") { object setpoint; if (FromHttpRequest(context, out setpoint)) { // setpoint may be null ToActuator(setpoint); lastSetpoint = setpoint; context.ResponseStatusCode = 200; // OK } else { context.ResponseStatusCode = 400; // Bad Request } } else if (context.RequestMethod == "GET") { // lastSetpoint may be null ToHttpResponse(context, lastSetpoint); context.ResponseStatusCode = 200; // OK } else { context.ResponseStatusCode = 405; // Method Not Allowed } }
/// <summary> /// Interprets an HTTP request and produces an appropriate HTTP /// response. /// If the measured variable is not open, Open is called first. /// Preconditions /// context != null /// </summary> /// <param name="context">Provides access to the received request, /// the server, and to the (empty) response.</param> public void HandleRequest(RequestHandlerContext context) { Contract.Requires(context != null); if (!initialized) { Open(); } context.ResponseMaxAge = 0; // no caching (default is -1, i.e., no cache header) object sample = FromSensor(); // sample may be null ToHttpResponse(context, sample); }
/// <summary> /// Converts an HTTP request message body to a double object. /// content is not null if conversion was successful. /// The return value indicates whether conversion was successful. /// Preconditions /// context != null /// Postconditions /// Result => /// content != null /// content is double /// "content contains deserialized representation of /// context.RequestContent" /// !Result => /// content == null /// "context.RequestContent could not be converted to a /// C# object" /// </summary> public static bool TryDeserializeDouble(RequestHandlerContext context, out object content) { string s = context.RequestContent; double x = 0; int decimalDiv = 0; content = null; foreach (char c in s) { if ((c == '.') && (decimalDiv == 0)) { decimalDiv = 1; } else if ((c < '0') || (c > '9')) { return false; } else { x = x * 10; decimalDiv = decimalDiv * 10; x = x + (int)(c - '0'); } } if (decimalDiv != 0) { x = x / decimalDiv; } content = x; return true; }
/// <summary> /// Converts an HTTP request message body to a double object. /// content is not null if conversion was successful. /// The return value indicates whether conversion was successful. /// Preconditions /// context != null /// Postconditions /// Result => /// content != null /// content is double /// "content contains deserialized representation of /// context.RequestContent" /// !Result => /// content == null /// "context.RequestContent could not be converted to a /// C# object" /// </summary> public static bool TryDeserializeDouble(RequestHandlerContext context, out object content) { // TODO remove if (context == null) Microsoft.SPOT.Debug.Print("context is null"); if (context.RequestContent == null) Microsoft.SPOT.Debug.Print("RequestContent is null"); string s = context.RequestContent; double x = 0; int decimalDiv = 0; content = null; foreach (char c in s) { if ((c == '.') && (decimalDiv == 0)) { decimalDiv = 1; } else if ((c < '0') || (c > '9')) { return false; } else { x = x * 10; decimalDiv = decimalDiv * 10; x = x + (int)(c - '0'); } } if (decimalDiv != 0) { x = x / decimalDiv; } content = x; return true; }
/// <summary> /// Converts an object to an HTTP response message body. /// content may be null. /// If the content is null, the string "null" is produced as /// its representation. Otherwise, the object's ToString /// method is called for producing the representation. /// Preconditions /// context != null /// Postconditions /// "context.RequestContent contains serialized representation of /// content object" /// context.ResponseContentType == "text/plain" /// </summary> public static void Serialize(RequestHandlerContext context, object content) { string s = (content != null) ? content.ToString() : "null"; context.SetResponse(s, "text/plain"); }
static void HandlePostOn(RequestHandlerContext context) { context.ResponseStatusCode = 200; remoteLight.Write(true); }
/// <summary> /// Converts an HTTP request message body to an int object. /// content is not null if conversion was successful. /// The return value indicates whether conversion was successful. /// Preconditions /// context != null /// Postconditions /// Result => /// content != null /// content is int /// "content contains deserialized representation of /// context.RequestContent" /// !Result => /// content == null /// "context.RequestContent could not be converted to a /// C# object" /// </summary> public static bool TryDeserializeInt(RequestHandlerContext context, out object content) { string s = context.RequestContent; int x; if (Utilities.TryParseUInt32(s, out x)) { content = x; return true; } else { content = null; return false; } }
static void HandleOn(RequestHandlerContext context) { onboardLedBlink = true; string s = "<h1>HANDLING LED<h2>Led Blink is ON now"; context.SetResponse(s, "text/html"); }
/// <summary> /// Private method that handles an incoming request. /// It sets up a RequestHandlerContext instance with the data from /// the incoming HTTP request, finds a suitable request handler to /// produce the response, and then sends the response as an HTTP /// response back to the client. /// Preconditions /// "connection is open" /// serviceRoot != null /// serviceRoot.Length > 8 /// "serviceRoot starts with 'http://' and ends with '/'" /// requestRouting != null /// </summary> /// <param name="connection">Open TCP/IP connection</param> /// <param name="serviceRoot">The absolute URI that is a prefix of /// all request URIs that this web service supports. It must start /// with "http://" and must end with "/".</param> /// <param name="relayDomain">Host name or Internet address of the /// relay to be used, or null if no relay is used</param> /// <param name="requestRouting">Collection of /// { request pattern, request handler} /// pairs</param> /// <param name="connectionClose">Return parameter that indicates /// that the connection should be closed after this call. This may /// be because the incoming request has a "Connection: close" /// header, because the request handler has set the /// ConnectionClose property, or because some error occurred. /// </param> internal static void ConsumeRequest(Stream connection, string serviceRoot, string relayDomain, RequestRouting requestRouting, bool catchRequestFailures, ref bool connectionClose) { Contract.Requires(connection != null); Contract.Requires(serviceRoot != null); Contract.Requires(serviceRoot.Length > 8); Contract.Requires(serviceRoot.Substring(0, 7) == "http://"); Contract.Requires(serviceRoot[serviceRoot.Length - 1] != '/'); Contract.Requires(requestRouting != null); // initialization -------------------------------------------- DebugPrint("HttpServer: ConsumeRequest - initialize"); HttpReader reader = new HttpReader(); HttpWriter writer = new HttpWriter(); var context = new RequestHandlerContext(serviceRoot, relayDomain); // receive request ------------------------------------------- reader.Attach(connection); // read request line DebugPrint("HttpServer: ConsumeRequest - read request line"); string httpMethod; string requestUri; string httpVersion; reader.ReadStringToBlank(out httpMethod); reader.ReadStringToBlank(out requestUri); if (reader.Status == HttpStatus.RequestUriTooLong) { context.ResponseStatusCode = 414; // Request-URI Too Long context.ResponseContentType = "text/plain"; context.ResponseContent = "request URI too long"; reader.Detach(); connectionClose = true; DebugPrint("HttpServer: ConsumeRequest - request URI too long!"); return; } reader.ReadFieldValue(out httpVersion); if (reader.Status != HttpStatus.BeforeContent) // error { reader.Detach(); connectionClose = true; DebugPrint("HttpServer: ConsumeRequest - could not read HTTP version!"); return; } context.RequestMethod = httpMethod; context.RequestUri = requestUri; // ignore version // headers DebugPrint("HttpServer: ConsumeRequest - read headers"); string fieldName; string fieldValue; int requestContentLength = -1; reader.ReadFieldName(out fieldName); while (reader.Status == HttpStatus.BeforeContent) { reader.ReadFieldValue(out fieldValue); if (fieldValue != null) { Contract.Assert(reader.Status == HttpStatus.BeforeContent); if (fieldName == "Connection") { connectionClose = (connectionClose || (fieldValue == "close") || (fieldValue == "Close")); } else if (fieldName == "Content-Type") { context.RequestContentType = fieldValue; DebugPrint("HttpServer: ConsumeRequest - request Content-Type: " + fieldValue); } else if (fieldName == "Content-Length") { if (Utilities.TryParseUInt32(fieldValue, out requestContentLength)) { // content length is now known DebugPrint("HttpServer: ConsumeRequest - request Content-Length: " + requestContentLength); } else { DebugPrint("HttpServer: ConsumeRequest - request syntax error"); //reader.Status = HttpStatus.SyntaxError; reader.Detach(); connectionClose = true; return; } } } else { // it's ok to skip header whose value is too long } Contract.Assert(reader.Status == HttpStatus.BeforeContent); reader.ReadFieldName(out fieldName); } if (reader.Status != HttpStatus.InContent) { reader.Detach(); connectionClose = true; return; } // content DebugPrint("HttpServer: ConsumeRequest - read content"); context.RequestContentBytes = null; if (requestContentLength > 0) { // receive content context.RequestContentBytes = new byte[requestContentLength]; int toRead = requestContentLength; var read = 0; while ((toRead > 0) && (read >= 0)) { // already read: requestContentLength - toRead read = reader.ReadContent(context.RequestContentBytes, requestContentLength - toRead, toRead); if (read < 0) { break; } // timeout or shutdown toRead = toRead - read; } } reader.Detach(); if (reader.Status != HttpStatus.InContent) { connectionClose = true; return; } // delegate request processing to a request handler ---------- DebugPrint("HttpServer: ConsumeRequest - call request handler"); var match = false; foreach (RoutingElement e in requestRouting) { if (context.RequestMatch(e)) { context.ConnectionClose = false; context.ResponseStatusCode = -1; // undefined Contract.Requires(context.ResponseContentType != null); Contract.Requires(context.ResponseContentType == "text/plain"); // default e.Handler(context); if (catchRequestFailures) { try { e.Handler(context); } catch (Exception h) { DebugPrint("HttpServer: exception in request handler: " + h); } } else { e.Handler(context); } Contract.Ensures(context.ResponseStatusCode >= 100); Contract.Ensures(context.ResponseStatusCode < 600); Contract.Ensures(context.ResponseContentType != null); Contract.Ensures(context.ResponseContentType.Length > 0); connectionClose = connectionClose || context.ConnectionClose; match = true; break; } } if (!match) { context.ResponseStatusCode = 404; // Not Found context.ResponseContentType = "text/plain"; context.ResponseContent = "404 error - resource not found"; DebugPrint("HttpServer: no matching request handler found"); } Contract.Assert(context.ResponseContentType != null); // send response --------------------------------------------- DebugPrint("HttpServer: ConsumeRequest - send response"); writer.Attach(connection); // status line DebugPrint("HttpServer: ConsumeRequest - send status line"); writer.WriteString("HTTP/1.1 "); writer.WriteString(context.ResponseStatusCode.ToString()); writer.WriteLine(" "); // omit optional reason phrase // headers DebugPrint("HttpServer: ConsumeRequest - send headers"); if (connectionClose) { writer.WriteLine("Connection: close"); } if (context.ResponseMaxAge > 0) { writer.WriteLine("Cache-Control: max-age=" + context.ResponseMaxAge); } else if (context.ResponseMaxAge == 0) { writer.WriteLine("Cache-Control: no-cache"); } writer.WriteString("Content-Type: "); writer.WriteLine(context.ResponseContentType); DebugPrint("HttpServer: ConsumeRequest - response Content-Type: " + context.ResponseContentType); if (context.ResponseContent != null) { DebugPrint("HttpServer: ConsumeRequest - response content is text"); context.ResponseContentBytes = Encoding.UTF8.GetBytes(context.ResponseContent); Contract.Assert(context.ResponseContentBytes != null); } if (context.ResponseContentBytes != null) { DebugPrint("HttpServer: ConsumeRequest - content available as binary"); if (context.ResponseContentLength == -1) { context.ResponseContentLength = context.ResponseContentBytes.Length; // default is to take the entire buffer } writer.WriteString("Content-Length: "); writer.WriteLine(context.ResponseContentLength.ToString()); DebugPrint("HttpServer: ConsumeRequest - response Content-Length: " + context.ResponseContentLength); } else { writer.WriteString("Content-Length: 0"); DebugPrint("HttpServer: ConsumeRequest - response Content-Length: 0"); } // content DebugPrint("HttpServer: ConsumeRequest - send content"); writer.WriteBeginOfContent(); if (context.ResponseContentBytes != null) // send content { writer.WriteContent(context.ResponseContentBytes, 0, context.ResponseContentLength); } DebugPrint("HttpServer: response sent"); writer.Detach(); uint availableMemory = Microsoft.SPOT.Debug.GC(true); // TODO define portable abstraction for free memory Trace.TraceInformation(context.RequestMethod + " " + context.RequestUri + " -> " + context.ResponseStatusCode + " [" + availableMemory + "]"); }