/// <summary> /// Generates a SPARQL Service Description Graph for the given Update Handler Configuration or uses the configuration supplied Description Graph /// </summary> /// <param name="context">HTTP Context</param> /// <param name="config">Update Handler Configuration</param> /// <param name="descripUri">Base URI of the Description</param> /// <returns></returns> public static IGraph GetServiceDescription(HttpContext context, BaseUpdateHandlerConfiguration config, Uri descripUri) { //Use user specified Service Description if present if (config.ServiceDescription != null) { return(config.ServiceDescription); } IGraph g = SparqlServiceDescriber.GetNewGraph(); //Add the Top Level Node representing the Service IUriNode descrip = g.CreateUriNode(descripUri); IUriNode rdfType = g.CreateUriNode(new Uri(RdfSpecsHelper.RdfType)); IUriNode service = g.CreateUriNode("sd:" + ClassService); g.Assert(descrip, rdfType, service); //Add its sd:url IUriNode url = g.CreateUriNode("sd:" + PropertyUrl); g.Assert(descrip, url, descrip); //Add the sd:supportedLanguage IUriNode supportedLang = g.CreateUriNode("sd:" + PropertySupportedLanguage); g.Assert(descrip, supportedLang, g.CreateUriNode("sd:" + InstanceSparql11Update)); //Add Features and Dataset Description //First add descriptions for Global Expression Factories IUriNode extensionFunction = g.CreateUriNode("sd:" + PropertyExtensionFunction); IUriNode extensionAggregate = g.CreateUriNode("sd:" + PropertyExtensionAggregate); foreach (ISparqlCustomExpressionFactory factory in SparqlExpressionFactory.Factories) { foreach (Uri u in factory.AvailableExtensionFunctions) { g.Assert(descrip, extensionFunction, g.CreateUriNode(u)); } foreach (Uri u in factory.AvailableExtensionAggregates) { g.Assert(descrip, extensionAggregate, g.CreateUriNode(u)); } } //Then get the Configuration Object to add any other Feature Descriptions it wishes to config.AddFeatureDescription(g, descrip); return(g); }
/// <summary> /// Processes SPARQL Update requests /// </summary> /// <param name="context">HTTP Context</param> public void ProcessRequest(HttpContext context) { this._config = this.LoadConfig(context); WebContext webContext = new WebContext(context); // Add our Standard Headers HandlerHelper.AddStandardHeaders(webContext, this._config); // Options we need to determine based on the HTTP Method used String[] updates; String updateText = null; List <String> userDefaultGraphs = new List <String>(); List <String> userNamedGraphs = new List <String>(); try { // Decide what to do based on the HTTP Method switch (context.Request.HttpMethod.ToUpper()) { case "OPTIONS": // OPTIONS requests always result in the Service Description document IGraph svcDescrip = SparqlServiceDescriber.GetServiceDescription(this._config, UriFactory.Create(context.Request.Url.AbsoluteUri)); HandlerHelper.SendToClient(webContext, svcDescrip, this._config); return; case "HEAD": // Just return from a HEAD request return; case "GET": // A GET with an update parameter is a Bad Request updates = context.Request.QueryString.GetValues("update"); if (updates != null && updates.Length > 0) { throw new ArgumentException("Updates cannot be submitted as GET requests"); } // Otherwise GET either results in the Service Description if appropriately conneg'd or // the update form if enabled try { // If we might show the Update Form only show the Description if the selected writer is // not a HTML writer MimeTypeDefinition definition = MimeTypesHelper.GetDefinitions(webContext.GetAcceptTypes()).FirstOrDefault(d => d.CanWriteRdf); if (definition != null) { IRdfWriter writer = definition.GetRdfWriter(); if (!(writer is IHtmlWriter)) { // If not a HTML Writer selected then show the Service Description Graph // unless an error occurs creating it IGraph serviceDescrip = SparqlServiceDescriber.GetServiceDescription(this._config, UriFactory.Create(context.Request.Url.AbsoluteUri)); context.Response.ContentType = definition.CanonicalMimeType; context.Response.ContentEncoding = definition.Encoding; writer.Save(serviceDescrip, new StreamWriter(context.Response.OutputStream, definition.Encoding)); return; } } } catch { // Ignore Exceptions - we'll just show the Query Form or return a 400 Bad Request instead } // If a Writer can't be selected then we'll either show the Update Form or return a 400 Bad Request if (this._config.ShowUpdateForm) { this.ShowUpdateForm(context); } else { throw new ArgumentException("Updates cannot be submitted as GET requests"); } return; case "POST": if (context.Request.ContentType != null) { MimeTypeSelector contentType = MimeTypeSelector.Create(context.Request.ContentType, 0); if (contentType.Type.Equals(MimeTypesHelper.WWWFormURLEncoded)) { // Form URL Encoded was declared type so expect an update parameter in the Form parameters updates = context.Request.Form.GetValues("update"); if (updates == null) { throw new ArgumentException("Required update parameter in POST body was missing"); } if (updates.Length == 0) { throw new ArgumentException("Required update parameter in POST body was missing"); } if (updates.Length > 1) { throw new ArgumentException("The update parameter was specified multiple times in the POST body"); } updateText = updates[0]; // For Form URL Encoded the Using/Using Named Graphs may be specified by Form parameters // Get the USING URIs (if any) if (context.Request.Form["using-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.Form.GetValues("using-graph-uri")); } // Get the USING NAMED URIs (if any) if (context.Request.Form["using-named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.Form.GetValues("using-named-graph-uri")); } break; } else if (contentType.Type.Equals(MimeTypesHelper.SparqlUpdate)) { // application/sparql-update was declared type so expect utf-8 charset (if present) if (contentType.Charset != null && !contentType.Charset.ToLower().Equals(MimeTypesHelper.CharsetUtf8)) { throw new ArgumentException("HTTP POST request was received with a " + MimeTypesHelper.SparqlUpdate + " Content-Type but a non UTF-8 charset parameter"); } using (StreamReader reader = new StreamReader(context.Request.InputStream)) { updateText = reader.ReadToEnd(); reader.Close(); } // For application/sparql-update the Using/Using Named Graphs may be specified by querystring parameters // Get the USING URIs (if any) if (context.Request.QueryString["using-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.QueryString.GetValues("using-graph-uri")); } // Get the USING NAMED URIs (if any) if (context.Request.QueryString["using-named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.QueryString.GetValues("using-named-graph-uri")); } break; } else { throw new ArgumentException("HTTP POST made to SPARQL update endpoint had an invalid Content-Type header, only " + MimeTypesHelper.WWWFormURLEncoded + " and " + MimeTypesHelper.SparqlUpdate + " are acceptable"); } } throw new ArgumentException("HTTP POST made to SPARQL Query endpoint was missing the required Content-Type header"); default: throw new NotSupportedException("HTTP " + context.Request.HttpMethod.ToUpper() + " is not supported by a SPARQL Update endpoint"); } // Clean up protocol provided dataset userDefaultGraphs.RemoveAll(g => String.IsNullOrEmpty(g)); userNamedGraphs.RemoveAll(g => String.IsNullOrEmpty(g)); // Now we're going to parse the Updates SparqlUpdateParser parser = new SparqlUpdateParser(); parser.DefaultBaseUri = context.Request.Url; parser.ExpressionFactories = this._config.ExpressionFactories; SparqlUpdateCommandSet commands = parser.ParseFromString(updateText); // Check whether we need to use authentication // If there are no user groups then no authentication is in use so we default to authenticated with no per-action authentication needed bool isAuth = true, requireActionAuth = false; if (this._config.UserGroups.Any()) { // If we have user isAuth = HandlerHelper.IsAuthenticated(webContext, this._config.UserGroups); requireActionAuth = true; } if (!isAuth) { return; } // First check actions to see whether they are all permissible and apply USING/USING NAMED parameters foreach (SparqlUpdateCommand cmd in commands.Commands) { // Authenticate each action bool actionAuth = true; if (requireActionAuth) { actionAuth = HandlerHelper.IsAuthenticated(webContext, this._config.UserGroups, this.GetPermissionAction(cmd)); } if (!actionAuth) { throw new SparqlUpdatePermissionException("You are not authorised to perform the " + this.GetPermissionAction(cmd) + " action"); } // Check whether we need to (and are permitted to) apply USING/USING NAMED parameters if (userDefaultGraphs.Count > 0 || userNamedGraphs.Count > 0) { BaseModificationCommand modify = cmd as BaseModificationCommand; if (modify != null) { if (modify.GraphUri != null || modify.UsingUris.Any() || modify.UsingNamedUris.Any()) { // Invalid if a command already has a WITH/USING/USING NAMED throw new SparqlUpdateMalformedException("A command in your update request contains a WITH/USING/USING NAMED clause but you have also specified one/both of the using-graph-uri or using-named-graph-uri parameters which is not permitted by the SPARQL Protocol"); } else { // Otherwise go ahead and apply userDefaultGraphs.ForEach(u => modify.AddUsingUri(UriFactory.Create(u))); userNamedGraphs.ForEach(u => modify.AddUsingNamedUri(UriFactory.Create(u))); } } } } // Then assuming we got here this means all our actions are permitted so now we can process the updates this.ProcessUpdates(commands); // Flush outstanding changes this._config.Processor.Flush(); // Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (RdfParseException parseEx) { HandleErrors(context, "Parsing Error", updateText, parseEx, (int)HttpStatusCode.BadRequest); } catch (SparqlUpdatePermissionException permEx) { HandleErrors(context, "Permissions Error", updateText, permEx, (int)HttpStatusCode.Forbidden); } catch (SparqlUpdateMalformedException malEx) { HandleErrors(context, "Malformed Update Error", updateText, malEx, (int)HttpStatusCode.BadRequest); } catch (SparqlUpdateException updateEx) { HandleErrors(context, "Update Error", updateText, updateEx); } catch (RdfException rdfEx) { HandleErrors(context, "RDF Error", updateText, rdfEx); } catch (NotSupportedException notSupEx) { HandleErrors(context, "HTTP Request Error", null, notSupEx, (int)HttpStatusCode.MethodNotAllowed); } catch (ArgumentException argEx) { HandleErrors(context, "HTTP Request Error", null, argEx, (int)HttpStatusCode.BadRequest); } catch (Exception ex) { HandleErrors(context, "Error", updateText, ex); } }
/// <summary> /// Processes SPARQL Update requests /// </summary> /// <param name="context">HTTP Context</param> public void ProcessRequest(HttpContext context) { this._config = this.LoadConfig(context); //Add our Standard Headers HandlerHelper.AddStandardHeaders(context, this._config); if (context.Request.HttpMethod.Equals("OPTIONS")) { //OPTIONS requests always result in the Service Description document IGraph svcDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, new Uri(context.Request.Url.AbsoluteUri)); HandlerHelper.SendToClient(context, svcDescrip, this._config); return; } //See if there has been an update submitted String updateText = context.Request.QueryString["update"]; if (updateText == null || updateText.Equals(String.Empty)) { updateText = context.Request.Form["update"]; } //If no Update sent either show Update Form or give a HTTP 400 response if (updateText == null || updateText.Equals(String.Empty)) { //If there is no Update we may return the SPARQL Service Description where appropriate try { //If we might show the Update Form only show the Description if the selected writer is //not a HTML writer MimeTypeDefinition definition = MimeTypesHelper.GetDefinitions(HandlerHelper.GetAcceptTypes(context)).FirstOrDefault(d => d.CanWriteRdf); if (definition != null) { IRdfWriter writer = definition.GetRdfWriter(); if (!this._config.ShowUpdateForm || !(writer is IHtmlWriter)) { //If not a HTML Writer selected OR not showing Update Form then show the Service Description Graph //unless an error occurs creating it IGraph serviceDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, new Uri(context.Request.Url.AbsoluteUri)); context.Response.ContentType = definition.CanonicalMimeType; context.Response.ContentEncoding = definition.Encoding; writer.Save(serviceDescrip, new StreamWriter(context.Response.OutputStream, definition.Encoding)); return; } } } catch { //Ignore Exceptions - we'll just show the Query Form or return a 400 Bad Request instead } //If a Writer can't be selected then we'll either show the Update Form or return a 400 Bad Request if (this._config.ShowUpdateForm) { this.ShowUpdateForm(context); } else { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } return; } try { //Now we're going to parse the Updates SparqlUpdateParser parser = new SparqlUpdateParser(); parser.ExpressionFactories = this._config.ExpressionFactories; SparqlUpdateCommandSet commands = parser.ParseFromString(updateText); //Check whether we need to use authentication //If there are no user groups then no authentication is in use so we default to authenticated with no per-action authentication needed bool isAuth = true, requireActionAuth = false; if (this._config.UserGroups.Any()) { //If we have user isAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups); requireActionAuth = true; } if (!isAuth) { return; } //First check actions to see whether they are all permissible foreach (SparqlUpdateCommand cmd in commands.Commands) { //Authenticate each action bool actionAuth = true; if (requireActionAuth) { actionAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups, this.GetPermissionAction(cmd)); } if (!actionAuth) { throw new SparqlUpdateException("You are not authorised to perform the " + this.GetPermissionAction(cmd) + " action"); } } //Then assuming we got here this means all our actions are permitted so now we can process the updates this.ProcessUpdates(commands); //Flush outstanding changes this._config.Processor.Flush(); //Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (RdfParseException parseEx) { HandleErrors(context, "Parsing Error", updateText, parseEx); } catch (SparqlUpdateException updateEx) { HandleErrors(context, "Update Error", updateText, updateEx); } catch (RdfException rdfEx) { HandleErrors(context, "RDF Error", updateText, rdfEx); } catch (Exception ex) { HandleErrors(context, "Error", updateText, ex); } }
/// <summary> /// Processes SPARQL Update requests /// </summary> /// <param name="context">HTTP Context</param> public void ProcessRequest(HttpContext context) { this._config = this.LoadConfig(context); //Add our Standard Headers HandlerHelper.AddStandardHeaders(context, this._config); if (context.Request.HttpMethod.Equals("OPTIONS")) { //OPTIONS requests always result in the Service Description document IGraph svcDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, UriFactory.Create(context.Request.Url.AbsoluteUri)); HandlerHelper.SendToClient(context, svcDescrip, this._config); return; } //See if there has been an update submitted String updateText = null; if (context.Request.ContentType != null) { if (context.Request.ContentType.Equals(MimeTypesHelper.WWWFormURLEncoded)) { updateText = context.Request.Form["update"]; } else if (context.Request.ContentType.Equals(MimeTypesHelper.SparqlUpdate)) { updateText = new StreamReader(context.Request.InputStream).ReadToEnd(); } } else { updateText = context.Request.Form["update"]; } //If no Update sent either show Update Form or give a HTTP 400 response if (updateText == null || updateText.Equals(String.Empty)) { //If there is no Update we may return the SPARQL Service Description where appropriate try { //If we might show the Update Form only show the Description if the selected writer is //not a HTML writer MimeTypeDefinition definition = MimeTypesHelper.GetDefinitions(HandlerHelper.GetAcceptTypes(context)).FirstOrDefault(d => d.CanWriteRdf); if (definition != null) { IRdfWriter writer = definition.GetRdfWriter(); if (!this._config.ShowUpdateForm || !(writer is IHtmlWriter)) { //If not a HTML Writer selected OR not showing Update Form then show the Service Description Graph //unless an error occurs creating it IGraph serviceDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, UriFactory.Create(context.Request.Url.AbsoluteUri)); context.Response.ContentType = definition.CanonicalMimeType; context.Response.ContentEncoding = definition.Encoding; writer.Save(serviceDescrip, new StreamWriter(context.Response.OutputStream, definition.Encoding)); return; } } } catch { //Ignore Exceptions - we'll just show the Query Form or return a 400 Bad Request instead } //If a Writer can't be selected then we'll either show the Update Form or return a 400 Bad Request if (this._config.ShowUpdateForm) { this.ShowUpdateForm(context); } else { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } return; } //Get Other options associated with this update List <String> userDefaultGraphs = new List <String>(); List <String> userNamedGraphs = new List <String>(); //Get the USING URIs (if any) if (context.Request.QueryString["using-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.QueryString.GetValues("using-graph-uri")); } else if (context.Request.Form["using-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.Form.GetValues("using-graph-uri")); } //Get the USING NAMED URIs (if any) if (context.Request.QueryString["using-named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.QueryString.GetValues("using-named-graph-uri")); } else if (context.Request.Form["using-named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.Form.GetValues("using-named-graph-uri")); } try { //Now we're going to parse the Updates SparqlUpdateParser parser = new SparqlUpdateParser(); parser.ExpressionFactories = this._config.ExpressionFactories; SparqlUpdateCommandSet commands = parser.ParseFromString(updateText); //Check whether we need to use authentication //If there are no user groups then no authentication is in use so we default to authenticated with no per-action authentication needed bool isAuth = true, requireActionAuth = false; if (this._config.UserGroups.Any()) { //If we have user isAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups); requireActionAuth = true; } if (!isAuth) { return; } //First check actions to see whether they are all permissible and apply USING/USING NAMED parameters foreach (SparqlUpdateCommand cmd in commands.Commands) { //Authenticate each action bool actionAuth = true; if (requireActionAuth) { actionAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups, this.GetPermissionAction(cmd)); } if (!actionAuth) { throw new SparqlUpdatePermissionException("You are not authorised to perform the " + this.GetPermissionAction(cmd) + " action"); } //Check whether we need to (and are permitted to) apply USING/USING NAMED parameters if (userDefaultGraphs.Count > 0 || userNamedGraphs.Count > 0) { BaseModificationCommand modify = cmd as BaseModificationCommand; if (modify != null) { if (modify.GraphUri != null || modify.UsingUris.Any() || modify.UsingNamedUris.Any()) { //Invalid if a command already has a WITH/USING/USING NAMED throw new SparqlUpdateMalformedException("A command in your update request contains a WITH/USING/USING NAMED clause but you have also specified one/both of the using-graph-uri or using-named-graph-uri parameters which is not permitted by the SPARQL Protocol"); } else { //Otherwise go ahead and apply userDefaultGraphs.ForEach(u => modify.AddUsingUri(UriFactory.Create(u))); userNamedGraphs.ForEach(u => modify.AddUsingNamedUri(UriFactory.Create(u))); } } } } //Then assuming we got here this means all our actions are permitted so now we can process the updates this.ProcessUpdates(commands); //Flush outstanding changes this._config.Processor.Flush(); //Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (RdfParseException parseEx) { HandleErrors(context, "Parsing Error", updateText, parseEx, (int)HttpStatusCode.BadRequest); } catch (SparqlUpdatePermissionException permEx) { HandleErrors(context, "Permissions Error", updateText, permEx, (int)HttpStatusCode.Forbidden); } catch (SparqlUpdateMalformedException malEx) { HandleErrors(context, "Malformed Update Error", updateText, malEx, (int)HttpStatusCode.BadRequest); } catch (SparqlUpdateException updateEx) { HandleErrors(context, "Update Error", updateText, updateEx); } catch (RdfException rdfEx) { HandleErrors(context, "RDF Error", updateText, rdfEx); } catch (Exception ex) { HandleErrors(context, "Error", updateText, ex); } }