void IHttpCwsHandler.ProcessRequest(HttpCwsContext context) { try { if (context.Request.RouteData == null) { return; } else { string action = context.Request.HttpMethod + context.Request.RouteData.Route.Name.ToUpper(); switch (action) { case "GETVARIABLES": context.Response.ContentType = "application/json"; context.Response.Write(MvMain.getMagicVariablesSerialized(), true); break; } } } catch (Exception ex) { context.Response.ContentType = "text/html"; context.Response.StatusCode = 500; context.Response.Write("<HTML><HEAD>ERROR</HEAD><BODY>Internal server error</BODY></HEAD></HTML>", true); } }
/// <summary> /// Send an HTTP error response by specifying the status code and message. /// In the case of a 405 response, the caller should also include the allowed /// HTTP methods for the requested resource /// </summary> /// <param name="context"></param> /// <param name="code"></param> /// <param name="errorMsg"></param> /// <param name="allowedMethods"></param> void SendError(HttpCwsContext context, int code, string errorMsg, params string[] allowedMethods) { context.Response.StatusCode = code; context.Response.StatusDescription = statusDescriptions[code]; if (code == 405) { string allowHeader = ""; // Add each allowed method in a comma-separated list for (int i = 0; i < allowedMethods.Length; i++) { allowHeader += allowedMethods[i].ToUpper(); if (i != allowedMethods.Length - 1) { allowHeader += ", "; } } // Add the finished header to the HTTP response and to the response body context.Response.AppendHeader("Allow", allowHeader); } // Send the error response context.Response.ContentType = "text/html"; context.Response.Write(CreateHtmlBody(errorHtml, new NameValueCollection() { { "error", errorMsg } }), true); }
/// <summary> /// Provides processing of HTTP requests by a HttpHandler that implements /// the <see cref="T:Crestron.SimplSharp.WebScripting.IHttpCwsHandler"/> interface. /// </summary> /// <param name="context">The object encapsulating the HTTP request.</param> /// <returns> /// true is the request was processed successfully; otherwise, false. /// </returns> public virtual void ProcessRequest(HttpCwsContext context) { try { _context = context; switch (_context.Request.HttpMethod) { case "GET": try { Get(); } catch (Exception e) { if (e is NotImplementedException) { Error(405, "Method not allowed"); } else { Error(e); } } break; case "POST": try { Post(); } catch (Exception e) { if (e is NotImplementedException) { Error(405, "Method not allowed"); } else { Error(e); } } break; default: Error(405, "Method not allowed"); break; } if (Response.IsClientConnected) { Response.Write("", true); } } catch (Exception e) { Error(e); } }
protected virtual void HandleError(HttpCwsContext context, int statusCode, string status, string customMessage) { context.Response.Clear(); context.Response.ClearHeaders(); context.Response.StatusCode = statusCode; context.Response.StatusDescription = status; var content = string.Format("<H1>Error {0} <span>{1}</span></H1><P>{2}</P>", statusCode, status, customMessage); context.Response.Write(content, true); }
/// <summary> /// Processes the CWS request. /// </summary> /// <param name="context">The context of the request.</param> void IHttpCwsHandler.ProcessRequest(HttpCwsContext context) { switch (context.Request.HttpMethod.ToUpper()) { case "GET": ProcessGet(context); break; default: break; } }
void IHttpCwsHandler.ProcessRequest(HttpCwsContext context) { try { context.Response.ContentType = "application/json"; context.Response.AppendHeader("Access-Control-Allow-Origin", "*"); context.Response.AppendHeader("Access-Control-Allow-Headers", "Content-Type"); context.Response.AppendHeader("Access-Control-Allow-Methods", "GET, PUT, POST"); //CrestronConsole.PrintLine("HttpMethod: " + context.Request.HttpMethod); //CrestronConsole.PrintLine("HttpPath: " + context.Request.Path); //CrestronConsole.PrintLine("Httpname: " + context.Request.RouteData.Route.Name); if (context.Request.Path.ToUpper() == "/CWS/API/" + this.slotNumber + "/CONFIGURATION/") { context.Response.StatusCode = 200; switch (context.Request.HttpMethod) { case ("GET"): Configuration.Reader(); context.Response.Write(JsonConvert.SerializeObject(Configuration.Obj), true); break; case ("PUT"): string JsonString; StreamReader TheFile = new StreamReader(context.Request.InputStream); JsonString = TheFile.ReadToEnd(); TheFile.Close(); //CrestronConsole.PrintLine("JsonString: " + JsonString); Configuration.Obj = JsonConvert.DeserializeObject <RootObject>(JsonString); Configuration.Writer(); context.Response.StatusCode = 204; context.Response.Write(JsonConvert.SerializeObject(Configuration.Obj), true); break; default: context.Response.StatusCode = 200; context.Response.Write(JsonConvert.SerializeObject(GetApiHelp()), true); break; } } else { context.Response.StatusCode = 200; context.Response.Write(JsonConvert.SerializeObject(GetApiHelp()), true); } } catch (Exception ex) { context.Response.ContentType = "text/html"; context.Response.StatusCode = 401; context.Response.Write(_Server.HtmlEncode(String.Format("<HTML><HEAD>ERROR</HEAD><BODY>The following error occurred: {0}. Stacktrace: {1}</BODY></HEAD></HTML>", ex.Message, ex.StackTrace)), true); } }
public override void ProcessRequest(HttpCwsContext context) { string resourceUrl = ""; int rlyID; try { resourceUrl = GetResourceUrl(context); rlyID = int.Parse((string)context.Request.RouteData.Values["id"]); } catch (Exception e) { CrestronConsole.PrintLine("Error while parsing {id} of individual relay from URL: " + e.Message); RespondWithJsonError(context, 404, "Not Found", resourceUrl, "The requested Relay ID does not parse as an integer"); return; } try { string subID = context.Request.RouteData.Values["subid"].ToString(); if (server.sublists[rlyID].ContainsKey(subID)) { if (context.Request.HttpMethod == "DELETE") { // remove the internal subscription resource server.sublists[rlyID].Remove(subID); CrestronConsole.PrintLine("subscription " + subID + " has been deleted by " + context.Request.UserHostAddress); // send the response context.Response.StatusCode = 204; context.Response.StatusDescription = "No Content"; context.Response.End(); } else { RespondWithJsonError(context, 405, "Method Not Allowed", resourceUrl, "allowed methods: " + allowedMethods); } } else { RespondWithJsonError(context, 404, "Not Found", resourceUrl, null); } } catch (Exception e) { CrestronConsole.PrintLine("Could not process request to " + context.Request.Url.AbsolutePath + ": " + e.Message); RespondWithJsonError(context, 500, "Internal Server Error", resourceUrl, null); } finally { CrestronConsole.PrintLine("Served response for " + context.Request.Url.AbsolutePath); } }
// Write a Collection+JSON error message to the client. Put a non-empty string in msg to provide a specific // error description. public void RespondWithJsonError(HttpCwsContext context, int code, string title, string resourceUrl, string msg) { try { context.Response.StatusCode = code; context.Response.StatusDescription = title; context.Response.ContentType = "application/vnd.collection+json"; if (code == 405) { context.Response.AppendHeader("Allow", allowedMethods); } // Since the client is expecting a Collection+JSON response, send the "error" object // detailing the error JObject err = new JObject { { "collection", new JObject { { "version", "1.0" }, { "href", resourceUrl }, { "error", new JObject { { "title", title }, { "code", code } } } } } }; // Optionally include a specific error message to the client. Automated clients // would be able to print this message in an error log, for example if (msg != null && msg != "") { err["collection"]["error"]["message"] = msg; } string json = JsonConvert.SerializeObject(err); context.Response.Write(json, true); } catch (Exception e) { CrestronConsole.PrintLine("Exception in RespondWithJsonError: " + e.Message); } finally { CrestronConsole.PrintLine("Served a Collection+JSON error resposne"); } }
public override void ProcessRequest(HttpCwsContext context) { string resourceUrl = ""; try { resourceUrl = GetResourceUrl(context); RespondWithJsonError(context, 404, "Not Found", resourceUrl, "Requested resource does not exist"); } catch (Exception e) { CrestronConsole.PrintLine("Error in DefaultHandler: " + e.Message); RespondWithJsonError(context, 500, "Internal Server Error", resourceUrl, null); } }
public void ProcessRequest(HttpCwsContext context) { var method = context.Request.HttpMethod; switch (method) { case "GET": context.Response.StatusCode = 200; // OK context.Response.ContentType = "application/json"; var temp = new Dictionary <string, string>(); if (context.Request.RouteData.Values.ContainsKey("NAME")) { temp["name"] = context.Request.RouteData.Values["NAME"].ToString(); context.Response.Write(JsonConvert.SerializeObject(temp), true); } else { temp["name"] = _name; context.Response.Write(JsonConvert.SerializeObject(temp), true); } break; case "PUT": context.Response.StatusCode = 200; // OK context.Response.ContentType = "application/json"; using (var reader = new StreamReader(context.Request.InputStream)) { var obj = JsonConvert.DeserializeObject <Dictionary <string, string> >(reader.ReadToEnd()); if (obj.ContainsKey("name")) { _name = obj["name"]; context.Response.Write("{ \"status\": \"OK\" }", true); } } break; default: context.Response.StatusCode = 501; // Not implemented context.Response.Write( String.Format("{0} method not implemented!", method), true); break; } }
public override void ProcessRequest(HttpCwsContext context) { string resourceUrl = ""; try { resourceUrl = GetResourceUrl(context); if (context.Request.HttpMethod == "GET" || context.Request.HttpMethod == "HEAD") // HTTP methods are case-sensitive { context.Response.StatusCode = 200; context.Response.StatusDescription = "OK"; context.Response.AppendHeader("Content-Type", "application/vnd.collection+json"); context.Response.AppendHeader("Link", resourceUrl + "/web-hooks" + ", rel=\"Subscriptions\""); // requirement of RESTful WebHooks standard if (context.Request.HttpMethod == "GET") // add the body for the GET request { // return the Collection+JSON representation of the Relay list // Populate template.json with the Collection+JSON response. // This response will not include the "template" property, since // clients may not PUT or POST to the relay collection itself string template = String.Copy(server.CjTemplate); template = template.Replace("{@resource}", resourceUrl); template = template.Replace("{@list}", FormatCollection(server.Relays, resourceUrl)); template = template.Replace("{@includetemplate}", ""); template = template.Replace("{@template}", ""); context.Response.Write(template, false); // write the populated template as a response } context.Response.End(); } else { RespondWithJsonError(context, 405, "Method Not Allowed", resourceUrl, allowedMethods); } } catch (Exception e) { CrestronConsole.PrintLine("Could not process request to " + context.Request.Url.AbsolutePath + ": " + e.Message); RespondWithJsonError(context, 500, "Internal Server Error", resourceUrl, null); } finally { CrestronConsole.PrintLine("Served response for " + context.Request.Url.AbsolutePath); } }
// Return a string representing the URL of the requested resource with no trailing // The returned URL won't end in a '/' public string GetResourceUrl(HttpCwsContext context) { // Constructing the URL from the HTTP request object avoids hard-coding the protocol (http/https) // into the URL, and it allows one to change the route name without breaking the code string resourceUrl = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + context.Request.Url.AbsolutePath; // remove trailing '/'s if present resourceUrl = resourceUrl.TrimEnd('/'); //while (resourceUrl.EndsWith("/")) //{ // resourceUrl = resourceUrl.Substring(0, resourceUrl.Length - 1); //} return(resourceUrl); }
protected sealed override void HandleError(HttpCwsContext context, int statusCode, string status, string customMessage) { context.Response.Clear(); context.Response.ClearHeaders(); context.Response.ContentType = "application/json"; context.Response.StatusCode = statusCode; context.Response.StatusDescription = status; var error = new { statusCode, status, message = customMessage }; var data = JObject.FromObject(error); context.Response.Write(data.ToString(Formatting.Indented), true); }
public override void ProcessRequest(HttpCwsContext context) { string resourceUrl = ""; try { resourceUrl = GetResourceUrl(context); string subID = context.Request.RouteData.Values["subid"].ToString(); if (server.sublists[COLLECTION_SUBLIST].ContainsKey(subID)) { if (context.Request.HttpMethod == "DELETE") { // remove the internal subscription resource server.sublists[COLLECTION_SUBLIST].Remove(subID); CrestronConsole.PrintLine("subscription " + subID + " has been deleted by " + context.Request.UserHostAddress); // send the response context.Response.StatusCode = 204; context.Response.StatusDescription = "No Content"; context.Response.End(); } else { RespondWithJsonError(context, 405, "Method Not Allowed", resourceUrl, "allowed methods: " + allowedMethods); } } else { RespondWithJsonError(context, 404, "Not Found", resourceUrl, null); } } catch (Exception e) { CrestronConsole.PrintLine("Could not process request to " + context.Request.Url.AbsolutePath + ": " + e.Message); RespondWithJsonError(context, 500, "Internal Server Error", resourceUrl, null); } finally { CrestronConsole.PrintLine("Served response for " + context.Request.Url.AbsolutePath); } }
/// <summary> /// Processes a GET request. /// </summary> /// <param name="context">The context of the request.</param> private void ProcessGet(HttpCwsContext context) { Debug.WriteLine("Processing GET."); try { var html = string.Empty; var css = string.Empty; using (var stream = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Evands.Pellucid.Resources.CwsConsoleTemplate.html")) { using (var reader = new Crestron.SimplSharp.CrestronIO.StreamReader(stream, true)) { html = reader.ReadToEnd(); } } using (var stream = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Evands.Pellucid.Resources.default.css")) { using (var reader = new Crestron.SimplSharp.CrestronIO.StreamReader(stream, true)) { css = reader.ReadToEnd(); } } context.Response.StatusCode = 200; var host = context.Request.Url.Host; var protocol = useSecureWebsocket ? "wss://" : "ws://"; html = html.Replace("{{ CSS }}", css).Replace("{{ PROTOCOL }}", protocol).Replace("{{ PORT }}", socketPort).Replace("{{ HOST }}", host); context.Response.Write(html, true); } catch (Exception ex) { Debug.WriteException(this, ex, "Exception while processing GET."); context.Response.StatusCode = 500; context.Response.StatusDescription = string.Format("Internal Server Error - Exception encountered while retrieving Console Endpoint.<br/><br/>{0}", ex.ToString()); context.Response.Write(context.Response.StatusDescription, true); } }
void IHttpCwsHandler.ProcessRequest(HttpCwsContext context) { try { // handle requests if (context.Request.RouteData != null) { switch (context.Request.RouteData.Route.Name.ToUpper()) { case "ROOMNAME": context.Response.StatusCode = 200; context.Response.Write(InitialParametersClass.RoomName, true); break; default: break; } } } catch (Exception e) { ErrorLog.Error("CWS Handler: {0}", e.Message); } }
public override void ProcessRequest(HttpCwsContext context) { uint rlyID; string resourceUrl = ""; try { resourceUrl = GetResourceUrl(context); rlyID = uint.Parse((string)context.Request.RouteData.Values["id"]); } catch (Exception e) { CrestronConsole.PrintLine("Error while parsing {id} of individual relay from URL: " + e.Message); RespondWithJsonError(context, 404, "Not Found", resourceUrl, "The requested Relay ID does not parse as an integer"); return; } try { if (rlyID >= 1 && rlyID <= server.NumberOfRelays) { context.Response.StatusCode = 200; context.Response.StatusDescription = "OK"; // server will respond with 200 OK for a successful GET, HEAD, or PUT if (context.Request.HttpMethod == "GET" || context.Request.HttpMethod == "HEAD") { // add method-specific response headers context.Response.AppendHeader("Content-Type", "application/vnd.collection+json"); context.Response.AppendHeader("Link", resourceUrl + "/web-hooks" + ", rel=\"Subscriptions\""); // requirement of RESTful WebHooks standard if (context.Request.HttpMethod == "GET") // add the body for the GET request { // return the Collection+JSON representation of the individual Relay // Populate template.json with the Collection+JSON response. // This response will include the "template" property, since // clients may PUT to the relay to update its state string template = String.Copy(server.CjTemplate); template = template.Replace("{@resource}", resourceUrl); template = template.Replace("{@list}", FormatItem(server.Relays[rlyID], resourceUrl)); template = template.Replace("{@includetemplate}", ", \"template\": "); template = template.Replace("{@template}", templateString); context.Response.Write(template, false); // write the populated template as a response } context.Response.End(); } else if (context.Request.HttpMethod == "PUT") { if (context.Request.Headers.Get("Content-Type") == "application/vnd.collection+json") { string json; using (StreamReader s = new StreamReader(context.Request.InputStream)) { json = s.ReadToEnd(); } JObject body = JsonConvert.DeserializeObject <JObject>(json); if (body.IsValid(putRequestBodySchema)) { string state = ((string)body["template"]["data"][0]["name"]).ToLower(); string newValue = ((string)body["template"]["data"][0]["value"]).ToLower(); if (state == "state" && (newValue == "close" || newValue == "open")) { // update internal Relay resource (true is Close, false is Open) server.Relays[rlyID].State = (newValue == "close") ? true : false; // the collection itself is 0-based, but the ID in the URL is 1-based // send the 200 OK response. No additional headers or entity-body is included in the // PUT response, but other applications may return a representation of the updated // resource context.Response.End(); } else { RespondWithJsonError(context, 400, "Bad Request", resourceUrl, "Bad name or value property in Collection+JSON template"); } } else { RespondWithJsonError(context, 400, "Bad Request", resourceUrl, "malformed Collection+JSON template provided"); } } else { RespondWithJsonError(context, 415, "Unsupported Media Type", resourceUrl, "supported media types: " + supportedMediaTypes); } } else { RespondWithJsonError(context, 405, "Method Not Allowed", resourceUrl, "allowed methods: " + allowedMethods); } } else { RespondWithJsonError(context, 404, "Not Found", resourceUrl, "The requested relay ID does not map to an available relay"); } } catch (Exception e) { CrestronConsole.PrintLine("Could not process request to " + context.Request.Url.AbsolutePath + ": " + e.Message); RespondWithJsonError(context, 500, "Internal Server Error", resourceUrl, null); } finally { CrestronConsole.PrintLine("Served response for " + context.Request.Url.AbsolutePath); } }
public void ProcessRequest(HttpCwsContext context) { try { // Only respond to POST requests made to the listen URL's path if (parent.listenUrl.Path == context.Request.Url.AbsolutePath) { if (context.Request.HttpMethod == "POST") { // GET the individual relay to observe its updated state HttpClientRequest req = new HttpClientRequest(); string linkString = context.Request.Headers["Link"]; // The notification POST request contains a link to the updated resource in the Link header. // in compliance with the RESTful WebHooks standard at // https://webhooks.pbworks.com/w/page/13385128/RESTful%20WebHooks // This URL will be nested between the '<' and '>' characters, // as described in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link int startIndex = linkString.IndexOf('<') + 1; int endIndex = linkString.IndexOf('>'); string linkUrlString = linkString.Substring(startIndex, endIndex - startIndex); UrlParser relayUrl = new UrlParser(linkUrlString); req.Url = relayUrl; req.RequestType = RequestType.Get; HttpHeader acceptHeader = new HttpHeader("Accept", "application/vnd.collection+json"); req.Header.AddHeader(acceptHeader); req.FinalizeHeader(); HttpClientResponse res = parent.client.Dispatch(req); int rlyID; string newState = ""; if (res.Code == 200) { JObject item = JsonConvert.DeserializeObject <JObject>(res.ContentString); rlyID = (int)item["collection"]["items"][0]["data"][0]["value"]; newState = (string)item["collection"]["items"][0]["data"][1]["value"]; } else { CrestronConsole.PrintLine("Failed to get individual relay"); return; } // log the notification message to console CrestronConsole.PrintLine("NOTIFICATION: Relay #" + rlyID + "'s state changed to " + newState); // respond to notification with 200 OK context.Response.StatusCode = 200; context.Response.StatusDescription = "OK"; context.Response.End(); } else { context.Response.StatusCode = 405; context.Response.StatusDescription = "Method Not Allowed"; context.Response.End(); } } else // ignore all other URLs besides the listener URL { CrestronConsole.PrintLine("Sending back a 404"); context.Response.StatusCode = 404; context.Response.StatusDescription = "Not Found"; context.Response.End(); } } catch (Exception e) { try { // Respond with error message context.Response.StatusCode = 500; context.Response.StatusDescription = "Internal Server Error"; context.Response.End(); CrestronConsole.PrintLine("Error in ProcessRequest: " + e.Message); } catch (Exception ex) { CrestronConsole.PrintLine("ProcessRequest unable to send error response: " + ex.Message); } } finally { ErrorLog.Notice("Served response to " + context.Request.UserHostName); } }
public override void ProcessRequest(HttpCwsContext context) { // Example request/response: // => // POST http://control-system/cws/api/relays/web-hooks // Content-Type : text/uri-list // Notification-Type : UPDATED // // http://www.webscripts.com/myaccount/mylistener // // <= // 201 Created // Location : http://control-system/cws/api/relays/web-hooks/46da87b3-7997-4ca1-8203-29ae68f01a6f // Link : <http://control-system/cws/api/relays/web-hooks>, rel="Subscriptions" string resourceUrl = ""; int rlyID; try { resourceUrl = GetResourceUrl(context); rlyID = int.Parse((string)context.Request.RouteData.Values["id"]); } catch (Exception e) { CrestronConsole.PrintLine("Error while parsing {id} of individual relay from URL: " + e.Message); RespondWithJsonError(context, 404, "Not Found", resourceUrl, "The requested Relay ID does not parse as an integer"); return; } try { if (context.Request.HttpMethod == "POST") { if (context.Request.Headers.Get("Content-Type") == "text/uri-list") { string callbackUrl; using (StreamReader s = new StreamReader(context.Request.InputStream)) { callbackUrl = s.ReadLine(); // the first line of the request's entity body must be the callback URL } if (context.Request.Headers.Get("Notification-Type") == "UPDATED") { // Create an internal subscription resource. Subscription constructor will validate the URL // remove "/web-hooks" so the subscription is linked to the individual relay resource itself, not the webhook Subscription newSub = new Subscription(resourceUrl.Replace("/web-hooks", ""), callbackUrl, "UPDATED", server.notifier); server.sublists[rlyID].Add(newSub.SubID.ToString(), newSub); CrestronConsole.PrintLine("Created new subscription resource with ID: " + newSub.SubID); // send response context.Response.StatusCode = 201; context.Response.StatusDescription = "Created"; context.Response.AppendHeader("Location", resourceUrl + "/" + newSub.SubID); context.Response.AppendHeader("Link", "<" + resourceUrl + ">" + ", rel=\"Subscriptions\""); context.Response.End(); } // Other notification types include "ACCESSED," "CREATED," and "DELETED," but this // server's WebHooks resources will not allow them else { RespondWithJsonError(context, 400, "Bad Request", resourceUrl, "supported Notification-Types: UPDATED"); } } else { RespondWithJsonError(context, 415, "Unsupported Media Type", resourceUrl, "supported Content-Types: " + supportedMediaTypes); } } else { RespondWithJsonError(context, 405, "Method Not Allowed", resourceUrl, "allowed methods: " + allowedMethods); } } catch (System.UriFormatException e) { CrestronConsole.PrintLine("Malformed client request to " + context.Request.Url.AbsolutePath + ": " + e.Message); RespondWithJsonError(context, 400, "Bad Request", resourceUrl, "Malformed URL provided in request body"); } catch (System.ArgumentNullException e) { CrestronConsole.PrintLine("Malformed client request to " + context.Request.Url.AbsolutePath + ": " + e.Message); RespondWithJsonError(context, 400, "Bad Request", resourceUrl, "No URL provided in request body"); } catch (Exception e) { CrestronConsole.PrintLine("Could not process request to " + context.Request.Url.AbsolutePath + ": " + e.Message); RespondWithJsonError(context, 500, "Internal Server Error", resourceUrl, null); } finally { CrestronConsole.PrintLine("Served response for " + context.Request.Url.AbsolutePath); } }
/// <summary> /// Provides processing of HTTP requests by a HttpHandler that implements /// the <see cref="T:Crestron.SimplSharp.WebScripting.IHttpCwsHandler"/> interface. /// </summary> /// <param name="context">The object encapsulating the HTTP request.</param> /// <returns> /// true is the request was processed successfully; otherwise, false. /// </returns> public override void ProcessRequest(HttpCwsContext context) { context.Response.ContentType = "application/json"; base.ProcessRequest(context); }
public void ProcessRequest(HttpCwsContext context) { try { Uri requestUri = context.Request.Url; string method = context.Request.HttpMethod; string path = context.Request.Path.ToLower(); switch (path) { case "/cws/": if (method == "GET" || method == "HEAD") { context.Response.StatusCode = 200; context.Response.StatusDescription = "OK"; context.Response.ContentType = "text/html"; // include the body for the GET request, but not HEAD if (method == "GET") { context.Response.Write(CreateHtmlBody(startHtml, // Use the current state to decide whether to enable the buttons or not new NameValueCollection() { { "enable", Registered ? "" : "disabled" }, { "enable-forget-access", (Registered && hasAccessToken) ? "" : "disabled" }, { "enable-forget-refresh", (Registered && HasRefreshToken) ? "" : "disabled" } }), false); } context.Response.End(); } else { SendError(context, 405, context.Request.HttpMethod + " not allowed", "GET", "HEAD"); } break; case "/cws/register": if (method == "GET" || method == "HEAD") { context.Response.StatusCode = 200; context.Response.StatusDescription = "OK"; context.Response.ContentType = "text/html"; // include the body for the GET request, but not HEAD if (method == "GET") { string body = Registered ? File.ReadToEnd(registeredHtml, Encoding.UTF8) : File.ReadToEnd(unregisteredHtml, Encoding.UTF8); body = CreateHtmlBody(registerHtml, new NameValueCollection() { { "register", body } }); if (!Registered) { // Make two additional variable evaluations on the "unregistered" HTML page string scheme = context.Request.Url.Scheme; body = body.Replace("{@callbackUrl}", CallbackUrl); body = body.Replace("{@httpsAlert}", (scheme == "https") ? "" : "<p>WARNING: It looks like you got here via http, not https." + " Check that SSL is enabled" + " on the control system. Don't submit anything until it is!</p>"); } context.Response.Write(body, false); } context.Response.End(); } else if (method == "POST") { // Get the entity-body, which should contain the domain, client_id, and client_secret // parameters if (context.Request.ContentType != "application/x-www-form-urlencoded") { SendError(context, 415, "Content-Type must be application/x-www-form-urlencoded"); return; } // POST requests from HTML forms put their parameters in the request body // using the application/x-www-form-urlencoded content type. An example can be found // at https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST string requestBody; // Parse the entity-body of the request and check for correctness using (StreamReader sr = new StreamReader(context.Request.InputStream)) { requestBody = sr.ReadToEnd(); } NameValueCollection keyValuePairs = ParseQueryParams(requestBody); string[] keys = keyValuePairs.AllKeys; // Handler for the "Unregister and Return to Home Page" button if (keys.Contains("unregister")) { Registered = false; context.Response.Redirect("/cws/", true); return; } // Check that all necessary registration fields (client_id, client_secret, and domain) // have been provided if (String.IsNullOrEmpty(keyValuePairs["domain"]) || String.IsNullOrEmpty(keyValuePairs["client_id"]) || String.IsNullOrEmpty(keyValuePairs["client_secret"])) { // Error, bad request SendError(context, 400, "Registration submission is missing a necessary field"); return; } // Save the values provided to the datastore Domain = keyValuePairs["domain"]; ClientID = keyValuePairs["client_id"]; ClientSecret = keyValuePairs["client_secret"]; // Send OK response context.Response.StatusCode = 200; context.Response.StatusDescription = statusDescriptions[200]; context.Response.Write(CreateHtmlBody(regsuccessHtml, new NameValueCollection() { { "domain", Domain }, { "client_id", ClientID } }), true); // Now the client is registered with the Authorization Server Registered = true; } else { SendError(context, 405, context.Request.HttpMethod + " not allowed", "GET", "HEAD", "POST"); } break; case "/cws/authorize": if (!Registered) { SendError(context, 404, "The client must be registered with the Auth0 Authorization server" + " before it can get authorized"); return; } if (method == "GET" || method == "HEAD") { // First, discard the old set of tokens accessToken = Empty; hasAccessToken = false; RefreshToken = Empty; HasRefreshToken = false; // Generate a random state parameter, which the callback URL will check for // consistency stateString = Guid.NewGuid().ToString("N"); NameValueCollection queries = new NameValueCollection() { // necessary scopes to get full user info and to get a Refresh Token { "scope", "offline_access%20openid%20email%20profile" }, // Authorization code flow { "response_type", "code" }, { "client_id", ClientID }, { "redirect_uri", CallbackUrl }, { "state", stateString }, // Always show the login and consent pages upon redirecting { "prompt", "login%20consent" } }; context.Response.StatusCode = 302; context.Response.StatusDescription = statusDescriptions[302]; // Contruct the redirect URL and send the user to Auth0's authorization endpoint context.Response.AppendHeader("Location", BuildAuthorizationUrl(queries)); context.Response.End(); } else { SendError(context, 405, context.Request.HttpMethod + " not allowed", "GET", "HEAD"); } break; case "/cws/callback": if (method == "HEAD" || method == "GET") { context.Response.StatusCode = 200; context.Response.StatusDescription = statusDescriptions[200]; if (method == "GET") { // Check if there's a query string if (context.Request.Url.Query == null || context.Request.Url.Query == "") { SendError(context, 400, "No Query parameters provided"); return; } NameValueCollection queryParams = null; // Parse query string and check state try { queryParams = ParseQueryParams(context.Request.Url.Query); } catch (Exception e) { SendError(context, 400, "Query string parsing error: " + e.Message); return; } if (queryParams["state"] != stateString) { CrestronConsole.PrintLine("State parameter mismatch: expected " + stateString + " but got " + queryParams["state"]); SendError(context, 400, "State parameter did not match."); return; } authorizationCode = queryParams["code"]; if (String.IsNullOrEmpty(authorizationCode)) { // Authorization was denied by the user SendError(context, 400, "Authorization was denied. The OAuth Client has no Access or Refresh token now"); return; } // Using an HttpsClient, exchange the received authorization code for an Access Token using (HttpsClient client = new HttpsClient()) { HttpsClientRequest req = new HttpsClientRequest(); req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Post; req.Url = new UrlParser(TokenEndpoint); CrestronConsole.PrintLine("GETting {0}", req.Url); HttpsHeaders headers = new HttpsHeaders(); // Auth0's token endpoint expects all the necessary information to be // placed in the entity-body of the request headers.SetHeaderValue("Content-Type", "application/x-www-form-urlencoded"); req.ContentString = BuildQueryString( new NameValueCollection { { "grant_type", "authorization_code" }, { "client_id", ClientID }, { "client_secret", ClientSecret }, { "code", authorizationCode }, { "redirect_uri", CallbackUrl } }); // Always set the Content-Length of your POST request to indicate the length of the body, // or else the Content-Length will be set to 0 by default! headers.SetHeaderValue("Content-Length", req.ContentString.Length.ToString()); req.Header = headers; // Send the POST request to the token endpoint and wait for a response... HttpsClientResponse tokenResponse = client.Dispatch(req); if (tokenResponse.Code >= 200 && tokenResponse.Code < 300) { // Parse JSON response and securely store the tokens JObject resBody = JsonConvert.DeserializeObject <JObject>(tokenResponse.ContentString); accessToken = (string)resBody["access_token"]; hasAccessToken = true; RefreshToken = (string)resBody["refresh_token"]; HasRefreshToken = true; CrestronConsole.PrintLine("Received \"{0}\" Access/Refresh Tokens. They expire in {1} hours", (string)resBody["token_type"], (int)resBody["expires_in"] / 60.0 / 60.0); // Respond context.Response.Write(CreateHtmlBody(authsuccessHtml, null), false); } else { SendError(context, tokenResponse.Code, "Could not get access/refresh tokens. Token endpoint returned code " + "<strong>" + tokenResponse.Code + "</strong>" + "<br /><br />Error Message:<br /><br /><strong>" + tokenResponse.ContentString + "</strong>"); return; } } } context.Response.End(); } else { SendError(context, 405, context.Request.HttpMethod + " not allowed", "GET", "HEAD"); } break; case "/cws/test": if (method == "GET" || method == "HEAD") { if (method == "GET") { if (!Registered) { SendError(context, 404, "The client must be registered before it can try to access" + " a protected resource"); return; } // Send a GET request to the protected resource using (HttpsClient client = new HttpsClient()) { HttpsClientRequest req = new HttpsClientRequest(); req.RequestType = Crestron.SimplSharp.Net.Https.RequestType.Get; req.Url = new UrlParser(ResourceEndpoint); CrestronConsole.PrintLine("GETting {0}", req.Url); // Put the Access Token in the Authorization header, if it's present if (hasAccessToken) { req.Header.SetHeaderValue("Authorization", "Bearer " + accessToken); } var res = client.Dispatch(req); if (res.Code >= 200 && res.Code < 300) { JObject resBody = JsonConvert.DeserializeObject <JObject>(res.ContentString); string name, email, picUrl; bool verified; // Try to get the username from any of these three JSON properties, // if they are defined. If none of them are defined, put "undefined" as a // placeholder name name = (string)resBody["given_name"] ?? (string)resBody["nickname"] ?? (string)resBody["email"] ?? "undefined"; email = (string)resBody["email"] ?? "undefined"; picUrl = (string)resBody["picture"] ?? "undefined"; verified = (bool?)resBody["email_verified"] ?? false; context.Response.Write(CreateHtmlBody(testHtml, new NameValueCollection() { { "name", name }, { "email", email }, { "verified", "<strong>" + (verified ? "verified" : "not verified") + "</strong>" }, { "pic_url", picUrl }, }), false); } else { // If RefreshAccessToken succeeds, accessToken will contain the new token // If not, display the body of the error response returned from the // authorization server string errMsg; int errCode; if (RefreshAccessToken(out errMsg, out errCode)) { CrestronConsole.PrintLine("Successfully refreshed the Access Token." + " Redirecting user to /cws/Test..."); context.Response.Redirect("/cws/Test", true); return; } else { SendError(context, errCode, "Failed to refresh Access Token." + " Resource endpoint returned code <strong>" + errCode + "</strong>" + "<br /><br />Error Message:<br /><br /><strong>" + errMsg + "</strong>"); return; } } context.Response.End(); } } } else { SendError(context, 405, context.Request.HttpMethod + " not allowed", "GET", "HEAD"); } break; case "/cws/forgetaccess": if (method == "GET" || method == "HEAD") { context.Response.StatusCode = 200; context.Response.StatusDescription = statusDescriptions[200]; if (method == "GET") { if (hasAccessToken) { accessToken = Empty; hasAccessToken = false; context.Response.Write(CreateHtmlBody(tokenDisposedHtml, new NameValueCollection { { "token_kind", "Access" } }), false); } else { SendError(context, 404, "There is currently no Access Token to be disposed of"); } } context.Response.End(); } else { SendError(context, 405, context.Request.HttpMethod + " not allowed", "GET", "HEAD"); } break; case "/cws/forgetrefresh": if (method == "GET" || method == "HEAD") { context.Response.StatusCode = 200; context.Response.StatusDescription = statusDescriptions[200]; if (method == "GET") { if (HasRefreshToken) { RefreshToken = Empty; HasRefreshToken = false; context.Response.Write(CreateHtmlBody(tokenDisposedHtml, new NameValueCollection { { "token_kind", "Refresh" } }), false); } else { SendError(context, 404, "There is currently no Refresh Token to be disposed of"); } } context.Response.End(); } else { SendError(context, 405, context.Request.HttpMethod + " not allowed", "GET", "HEAD"); } break; default: SendError(context, 404, path + " not found"); break; } } catch (Exception e) { CrestronConsole.PrintLine("Error in ProcessRequest(): {0}", e); try { SendError(context, 500, "An error occurred in the CWS server: " + e.Message); CrestronConsole.PrintLine("Sent " + context.Response.StatusCode + " response to " + context.Request.UserHostName); } catch (Exception ex) { CrestronConsole.PrintLine("Could not send Error response: {0}", ex); } } finally { CrestronConsole.PrintLine("Sent " + context.Response.StatusCode + " response to " + context.Request.UserHostName); } }
public void ProcessRequest(HttpCwsContext context) { var method = context.Request.HttpMethod; switch (method) { case "GET": context.Response.StatusCode = 200; // OK context.Response.ContentType = "application/json"; if (context.Request.RouteData.Values.ContainsKey("PROPERTY")) { var prop = context.Request.RouteData.Values["PROPERTY"]; context.Response.Write(GetPropertyJSON(prop.ToString()), true); } else { try { context.Response.Write(JsonConvert.SerializeObject(_settings), true); } catch (Exception e) { ErrorLog.Error("Exception in RoomRequest.ProcessRequest: {0}", e.Message); } } break; case "PUT": context.Response.StatusCode = 200; // OK context.Response.ContentType = "application/json"; SystemSettings newSettings; try { using (var reader = new StreamReader(context.Request.InputStream)) { newSettings = JsonConvert.DeserializeObject <SystemSettings> (reader.ReadToEnd()); CrestronConsole.PrintLine("Inputs: {0}", newSettings.inputs.Length); CrestronConsole.PrintLine("Outputs: {0}", newSettings.outputs.Length); } _settings.Copy(newSettings); } catch (Exception e) { ErrorLog.Error("Exception in RoomRequest.ProcessRequest: {0}", e.Message); } context.Response.Write("{ \"status\": \"OK\" }", true); break; default: context.Response.StatusCode = 501; // Not implemented context.Response.Write( String.Format("{0} method not implemented!", method), true); break; } }
protected RestfulRelayServer server; // reference to the server, the handler's "parent" public abstract void ProcessRequest(HttpCwsContext context);