/// <summary> /// Generates a SPARQL Service Description Graph for the given Protocol Handler Configuration or uses the configuration supplied Description Graph /// </summary> /// <param name="context">HTTP Context</param> /// <param name="config">Protocol Handler Configuration</param> /// <param name="descripUri">Base URI of the Description</param> /// <returns></returns> public static IGraph GetServiceDescription(HttpContext context, BaseProtocolHandlerConfiguration 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 Input Formats IUriNode inputFormat = g.CreateUriNode("sd:" + PropertyInputFormat); foreach (MimeTypeDefinition definition in MimeTypesHelper.Definitions) { if (definition.CanParseRdf) { if (definition.FormatUri != null) { g.Assert(descrip, inputFormat, g.CreateUriNode(new Uri(definition.FormatUri))); } } } //Add the Result Formats IUriNode resultFormat = g.CreateUriNode("sd:" + PropertyResultFormat); foreach (MimeTypeDefinition definition in MimeTypesHelper.Definitions) { if (definition.CanWriteRdf) { if (definition.FormatUri != null) { g.Assert(descrip, resultFormat, g.CreateUriNode(new Uri(definition.FormatUri))); } } } config.AddFeatureDescription(g, descrip); return(g); }
/// <summary> /// Processes Service Description requests /// </summary> /// <param name="context">HTTP Context</param> public void ProcessDescriptionRequest(HttpContext context) { try { //Get the Service Description Graph IGraph serviceDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, new Uri(context.Request.Url.AbsoluteUri), ServiceDescriptionType.All); HandlerHelper.SendToClient(context, serviceDescrip, this._config); } catch { //If any errors occur then return a 500 Internal Server Error context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } }
/// <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 requests /// </summary> /// <param name="context">HTTP Context</param> public void ProcessRequest(HttpContext context) { this._config = this.LoadConfig(context, out this._basePath); //Add our Standard Headers HandlerHelper.AddStandardHeaders(context, this._config); String path = context.Request.Path; if (path.StartsWith(this._basePath)) { path = path.Substring(this._basePath.Length); } //OPTIONS requests always result in the Service Description document if (context.Request.HttpMethod.Equals("OPTIONS")) { IGraph svcDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, new Uri(new Uri(context.Request.Url.AbsoluteUri), this._basePath + "description"), ServiceDescriptionType.All); HandlerHelper.SendToClient(context, svcDescrip, this._config); } else { //Otherwise determine the type of request based on the Path switch (path) { case "query": this.ProcessQueryRequest(context); break; case "update": this.ProcessUpdateRequest(context); break; case "description": this.ProcessDescriptionRequest(context); break; default: this.ProcessProtocolRequest(context); break; } } }
/// <summary> /// Processes requests made to the Graph Store HTTP Protocol endpoint and invokes the appropriate methods on the Protocol Processor that is in use /// </summary> /// <param name="context">HTTP Context</param> /// <remarks> /// <para> /// Implementations may override this if necessary - if the implementation is only providing additional logic such as authentication, ACLs etc. then it is recommended that the override applies its logic and then calls the base method since this base method will handle much of the error handling and sending of appropriate HTTP Response Codes. /// </para> /// </remarks> public virtual void ProcessRequest(HttpContext context) { this._config = this.LoadConfig(context, out this._basePath); WebContext webContext = new WebContext(context); // Add our Standard Headers HandlerHelper.AddStandardHeaders(webContext, this._config); if (context.Request.HttpMethod.Equals("OPTIONS")) { // OPTIONS requests always result in the Service Description document IGraph svcDescrip = SparqlServiceDescriber.GetServiceDescription(this._config, new Uri(UriFactory.Create(context.Request.Url.AbsoluteUri), this._basePath)); HandlerHelper.SendToClient(webContext, svcDescrip, this._config); return; } // Check whether we need to use authentication if (!HandlerHelper.IsAuthenticated(webContext, this._config.UserGroups, context.Request.HttpMethod)) { return; } try { // Invoke the appropriate method on our protocol processor switch (context.Request.HttpMethod) { case "GET": this._config.Processor.ProcessGet(webContext); break; case "PUT": this._config.Processor.ProcessPut(webContext); break; case "POST": Uri serviceUri = new Uri(UriFactory.Create(context.Request.Url.AbsoluteUri), this._basePath); if (context.Request.Url.AbsoluteUri.Equals(serviceUri.AbsoluteUri)) { // If there is a ?graph parameter or ?default parameter then this is a normal Post // Otherwise it is a PostCreate if (context.Request.QueryString["graph"] != null) { this._config.Processor.ProcessPost(webContext); } else if (context.Request.QueryString.AllKeys.Contains("default") || Regex.IsMatch(context.Request.QueryString.ToString(), BaseProtocolProcessor.DefaultParameterPattern)) { this._config.Processor.ProcessPost(webContext); } else { this._config.Processor.ProcessPostCreate(webContext); } } else { this._config.Processor.ProcessPost(webContext); } break; case "DELETE": this._config.Processor.ProcessDelete(webContext); break; case "HEAD": this._config.Processor.ProcessHead(webContext); break; case "PATCH": this._config.Processor.ProcessPatch(webContext); break; default: // For any other HTTP Verb we send a 405 Method Not Allowed context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed; break; } // Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (SparqlHttpProtocolUriResolutionException) { // If URI Resolution fails we send a 400 Bad Request context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } catch (SparqlHttpProtocolUriInvalidException) { // If URI is invalid we send a 400 Bad Request context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } catch (NotSupportedException) { // If Not Supported we send a 405 Method Not Allowed context.Response.StatusCode = (int)HttpStatusCode.MethodNotAllowed; } catch (NotImplementedException) { // If Not Implemented we send a 501 Not Implemented context.Response.StatusCode = (int)HttpStatusCode.NotImplemented; } catch (RdfWriterSelectionException) { // If we can't select a valid Writer when returning content we send a 406 Not Acceptable context.Response.StatusCode = (int)HttpStatusCode.NotAcceptable; } catch (RdfParserSelectionException) { // If we can't select a valid Parser when receiving content we send a 415 Unsupported Media Type context.Response.StatusCode = (int)HttpStatusCode.UnsupportedMediaType; } catch (RdfParseException) { // If we can't parse the received content successfully we send a 400 Bad Request context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } catch (Exception) { // For any other error we'll send a 500 Internal Server Error context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; } }
/// <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> /// Generates a SPARQL Service Description Graph for the specified portion of the SPARQL Server Handler Configuration or uses the configuration supplied Description Graph /// </summary> /// <param name="context">HTTP Context</param> /// <param name="config">SPARQL Server Configuration</param> /// <param name="descripUri">Base URI of the Description</param> /// <param name="type">Portion of the SPARQL Server to describe</param> /// <returns></returns> public static IGraph GetServiceDescription(HttpContext context, BaseSparqlServerConfiguration config, Uri descripUri, ServiceDescriptionType type) { //Use user specified Service Description if present if (config.ServiceDescription != null) { return(config.ServiceDescription); } IGraph g = SparqlServiceDescriber.GetNewGraph(); IUriNode rdfType = g.CreateUriNode(new Uri(RdfSpecsHelper.RdfType)); IUriNode service = g.CreateUriNode("sd:" + ClassService); INode queryNode, updateNode, protocolNode; //Query Service Description if (config.QueryProcessor != null && (type == ServiceDescriptionType.Query || type == ServiceDescriptionType.All)) { //Add the Top Level Node representing the Query Service queryNode = g.CreateUriNode(new Uri(descripUri, "query")); g.Assert(queryNode, rdfType, service); //Add its sd:url IUriNode url = g.CreateUriNode("sd:" + PropertyUrl); g.Assert(queryNode, url, queryNode); //Add the sd:supportedLanguage IUriNode supportedLang = g.CreateUriNode("sd:" + PropertySupportedLanguage); IUriNode lang; switch (config.QuerySyntax) { case SparqlQuerySyntax.Extended: case SparqlQuerySyntax.Sparql_1_1: lang = g.CreateUriNode("sd:" + InstanceSparql11Query); break; default: lang = g.CreateUriNode("sd:" + InstanceSparql10Query); break; } g.Assert(queryNode, supportedLang, lang); //Add the Result Formats IUriNode resultFormat = g.CreateUriNode("sd:" + PropertyResultFormat); foreach (MimeTypeDefinition definition in MimeTypesHelper.Definitions) { if (definition.CanWriteRdf || definition.CanWriteSparqlResults) { if (definition.FormatUri != null) { g.Assert(queryNode, resultFormat, g.CreateUriNode(new Uri(definition.FormatUri))); } } } //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(queryNode, extensionFunction, g.CreateUriNode(u)); } foreach (Uri u in factory.AvailableExtensionAggregates) { g.Assert(queryNode, extensionAggregate, g.CreateUriNode(u)); } } } else { queryNode = null; } //Update Service Description if (config.UpdateProcessor != null && (type == ServiceDescriptionType.Update || type == ServiceDescriptionType.All)) { //Add the Top Level Node representing the Update Service updateNode = g.CreateUriNode(new Uri(descripUri, "update")); g.Assert(updateNode, rdfType, service); //Add its sd:url IUriNode url = g.CreateUriNode("sd:" + PropertyUrl); g.Assert(updateNode, url, updateNode); //Add the sd:supportedLanguage IUriNode supportedLang = g.CreateUriNode("sd:" + PropertySupportedLanguage); g.Assert(updateNode, 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(updateNode, extensionFunction, g.CreateUriNode(u)); } foreach (Uri u in factory.AvailableExtensionAggregates) { g.Assert(updateNode, extensionAggregate, g.CreateUriNode(u)); } } } else { updateNode = null; } //Graph Store HTTP Protocol Description if (config.ProtocolProcessor != null && (type == ServiceDescriptionType.Protocol || type == ServiceDescriptionType.All)) { //Add the Top Level Node representing the Service if (descripUri.ToString().EndsWith("/description")) { String actualUri = descripUri.ToString(); actualUri = actualUri.Substring(0, actualUri.LastIndexOf("/description") + 1); protocolNode = g.CreateUriNode(new Uri(actualUri)); } else { protocolNode = g.CreateUriNode(descripUri); } g.Assert(protocolNode, rdfType, service); //Add its sd:url IUriNode url = g.CreateUriNode("sd:" + PropertyUrl); g.Assert(protocolNode, url, protocolNode); //Add the Input Formats IUriNode inputFormat = g.CreateUriNode("sd:" + PropertyInputFormat); foreach (MimeTypeDefinition definition in MimeTypesHelper.Definitions) { if (definition.CanParseRdf) { if (definition.FormatUri != null) { g.Assert(protocolNode, inputFormat, g.CreateUriNode(new Uri(definition.FormatUri))); } } } //Add the Result Formats IUriNode resultFormat = g.CreateUriNode("sd:" + PropertyResultFormat); foreach (MimeTypeDefinition definition in MimeTypesHelper.Definitions) { if (definition.CanWriteRdf) { if (definition.FormatUri != null) { g.Assert(protocolNode, resultFormat, g.CreateUriNode(new Uri(definition.FormatUri))); } } } } else { protocolNode = null; } //Finally get the Configuration Node to add additional feature and dataset descriptions config.AddFeatureDescription(g, queryNode, updateNode, protocolNode); return(g); }
/// <summary> /// Generates a SPARQL Service Description Graph for the given Query Handler Configuration or uses the configuration supplied Description Graph /// </summary> /// <param name="context">HTTP Context</param> /// <param name="config">Query Handler Configuration</param> /// <param name="descripUri">Base URI of the Description</param> /// <returns></returns> public static IGraph GetServiceDescription(HttpContext context, BaseQueryHandlerConfiguration 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 - Requires Query Language to be configurable through the Configuration API IUriNode supportedLang = g.CreateUriNode("sd:" + PropertySupportedLanguage); IUriNode lang; switch (config.Syntax) { case SparqlQuerySyntax.Extended: case SparqlQuerySyntax.Sparql_1_1: lang = g.CreateUriNode("sd:" + InstanceSparql11Query); break; default: lang = g.CreateUriNode("sd:" + InstanceSparql10Query); break; } g.Assert(descrip, supportedLang, lang); //Add the Result Formats IUriNode resultFormat = g.CreateUriNode("sd:" + PropertyResultFormat); foreach (MimeTypeDefinition definition in MimeTypesHelper.Definitions) { if (definition.CanWriteRdf || definition.CanWriteSparqlResults) { if (definition.FormatUri != null) { g.Assert(descrip, resultFormat, g.CreateUriNode(new Uri(definition.FormatUri))); } } } //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 Update requests /// </summary> /// <param name="context">HTTP Context</param> public void ProcessUpdateRequest(HttpContext context) { if (this._config.UpdateProcessor == null) { context.Response.StatusCode = (int)HttpStatusCode.NotImplemented; return; } 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), ServiceDescriptionType.Update); 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 (this._config.ShowUpdateForm) { this.ShowUpdateForm(context); return; } 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.GetUpdatePermissionAction(cmd)); } if (!actionAuth) { throw new SparqlUpdateException("You are not authorised to perform the " + this.GetUpdatePermissionAction(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.UpdateProcessor.Flush(); //Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (RdfParseException parseEx) { HandleUpdateErrors(context, "Parsing Error", updateText, parseEx); } catch (SparqlUpdateException updateEx) { HandleUpdateErrors(context, "Update Error", updateText, updateEx); } catch (RdfException rdfEx) { HandleUpdateErrors(context, "RDF Error", updateText, rdfEx); } catch (Exception ex) { HandleUpdateErrors(context, "Error", updateText, ex); } }
/// <summary> /// Processes Query requests /// </summary> /// <param name="context">HTTP Context</param> public void ProcessQueryRequest(HttpContext context) { if (this._config.QueryProcessor == null) { context.Response.StatusCode = (int)HttpStatusCode.NotImplemented; return; } 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), ServiceDescriptionType.Query); HandlerHelper.SendToClient(context, svcDescrip, this._config); return; } //See if there has been an query submitted String queryText = context.Request.QueryString["query"]; if (queryText == null || queryText.Equals(String.Empty)) { queryText = context.Request.Form["query"]; } //If no Query sent either show Query Form or give a HTTP 400 response if (queryText == null || queryText.Equals(String.Empty)) { if (this._config.ShowQueryForm) { this.ShowQueryForm(context); return; } else { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; return; } } //Get Other options associated with this query List <String> userDefaultGraphs = new List <String>(); List <String> userNamedGraphs = new List <String>(); long timeout = 0; bool partialResults = this._config.DefaultPartialResults; //Get the Default Graph URIs (if any) if (context.Request.QueryString["default-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.QueryString.GetValues("default-graph-uri")); } else if (context.Request.Form["default-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.Form.GetValues("default-graph-uri")); } //Get the Named Graph URIs (if any) if (context.Request.QueryString["named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.QueryString.GetValues("named-graph-uri")); } else if (context.Request.Form["named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.Form.GetValues("named-graph-uri")); } //Get Timeout setting (if any) if (context.Request.QueryString["timeout"] != null) { if (!Int64.TryParse(context.Request.QueryString["timeout"], out timeout)) { timeout = this._config.DefaultTimeout; } } else if (context.Request.Form["timeout"] != null) { if (!Int64.TryParse(context.Request.Form["timeout"], out timeout)) { timeout = this._config.DefaultTimeout; } } //Get Partial Results Setting (if any); if (context.Request.QueryString["partialResults"] != null) { if (!Boolean.TryParse(context.Request.QueryString["partialResults"], out partialResults)) { partialResults = this._config.DefaultPartialResults; } } else if (context.Request.Form["partialResults"] != null) { if (!Boolean.TryParse(context.Request.Form["partialResults"], out partialResults)) { partialResults = this._config.DefaultPartialResults; } } try { //Now we're going to parse the Query SparqlQueryParser parser = new SparqlQueryParser(this._config.QuerySyntax); parser.ExpressionFactories = this._config.ExpressionFactories; parser.QueryOptimiser = this._config.QueryOptimiser; SparqlQuery query = parser.ParseFromString(queryText); query.AlgebraOptimisers = this._config.AlgebraOptimisers; //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; } //Is this user allowed to make this kind of query? if (requireActionAuth) { HandlerHelper.IsAuthenticated(context, this._config.UserGroups, this.GetQueryPermissionAction(query)); } //Set the Default Graph URIs (if any) if (userDefaultGraphs.Count > 0) { //Default Graph Uri specified by default-graph-uri parameter or Web.config settings foreach (String userDefaultGraph in userDefaultGraphs) { if (!userDefaultGraph.Equals(String.Empty)) { query.AddDefaultGraph(new Uri(userDefaultGraph)); } } } else if (!this._config.DefaultGraphURI.Equals(String.Empty)) { //Only applies if the Query doesn't specify any Default Graph if (!query.DefaultGraphs.Any()) { query.AddDefaultGraph(new Uri(this._config.DefaultGraphURI)); } } //Set the Named Graph URIs (if any) if (userNamedGraphs.Count > 0) { foreach (String userNamedGraph in userNamedGraphs) { if (!userNamedGraph.Equals(String.Empty)) { query.AddNamedGraph(new Uri(userNamedGraph)); } } } //Set Timeout setting if (timeout > 0) { query.Timeout = timeout; } else { query.Timeout = this._config.DefaultTimeout; } //Set Partial Results Setting query.PartialResultsOnTimeout = partialResults; //Set Describe Algorithm query.Describer = this._config.DescribeAlgorithm; //Now we can finally make the query and return the results Object result = this.ProcessQuery(query); this.ProcessQueryResults(context, result); //Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (RdfParseException parseEx) { HandleQueryErrors(context, "Parsing Error", queryText, parseEx, (int)HttpStatusCode.BadRequest); } catch (RdfQueryTimeoutException timeoutEx) { HandleQueryErrors(context, "Query Timeout Error", queryText, timeoutEx); } catch (RdfQueryException queryEx) { HandleQueryErrors(context, "Query Error", queryText, queryEx); } catch (RdfWriterSelectionException writerSelEx) { HandleQueryErrors(context, "Output Selection Error", queryText, writerSelEx, (int)HttpStatusCode.NotAcceptable); } catch (RdfException rdfEx) { HandleQueryErrors(context, "RDF Error", queryText, rdfEx); } catch (Exception ex) { HandleQueryErrors(context, "Error", queryText, 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); } }
/// <summary> /// Processes a SPARQL Query Request /// </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 query submitted String queryText = context.Request.QueryString["query"]; if (queryText == null || queryText.Equals(String.Empty)) { queryText = context.Request.Form["query"]; } //If no Query sent either show Query Form or give a HTTP 400 response if (queryText == null || queryText.Equals(String.Empty)) { //If there is no Query we may return the SPARQL Service Description where appropriate try { //If we might show the Query 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.ShowQueryForm || !(writer is IHtmlWriter)) { //If not a HTML Writer selected OR not showing Query 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 Query Form or return a 400 Bad Request if (this._config.ShowQueryForm) { this.ShowQueryForm(context); } else { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } return; } //Get Other options associated with this query List <String> userDefaultGraphs = new List <String>(); List <String> userNamedGraphs = new List <String>(); long timeout = 0; bool partialResults = this._config.DefaultPartialResults; //Get the Default Graph URIs (if any) if (context.Request.QueryString["default-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.QueryString.GetValues("default-graph-uri")); } else if (context.Request.Form["default-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.Form.GetValues("default-graph-uri")); } //Get the Named Graph URIs (if any) if (context.Request.QueryString["named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.QueryString.GetValues("named-graph-uri")); } else if (context.Request.Form["named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.Form.GetValues("named-graph-uri")); } //Get Timeout setting (if any) if (context.Request.QueryString["timeout"] != null) { if (!Int64.TryParse(context.Request.QueryString["timeout"], out timeout)) { timeout = this._config.DefaultTimeout; } } else if (context.Request.Form["timeout"] != null) { if (!Int64.TryParse(context.Request.Form["timeout"], out timeout)) { timeout = this._config.DefaultTimeout; } } //Get Partial Results Setting (if any); if (context.Request.QueryString["partialResults"] != null) { if (!Boolean.TryParse(context.Request.QueryString["partialResults"], out partialResults)) { partialResults = this._config.DefaultPartialResults; } } else if (context.Request.Form["partialResults"] != null) { if (!Boolean.TryParse(context.Request.Form["partialResults"], out partialResults)) { partialResults = this._config.DefaultPartialResults; } } try { //Now we're going to parse the Query SparqlQueryParser parser = new SparqlQueryParser(this._config.Syntax); parser.ExpressionFactories = this._config.ExpressionFactories; parser.QueryOptimiser = this._config.QueryOptimiser; SparqlQuery query = parser.ParseFromString(queryText); query.AlgebraOptimisers = this._config.AlgebraOptimisers; //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; } //Is this user allowed to make this kind of query? if (requireActionAuth) { HandlerHelper.IsAuthenticated(context, this._config.UserGroups, this.GetPermissionAction(query)); } //Set the Default Graph URIs (if any) if (userDefaultGraphs.Count > 0) { //Default Graph Uri specified by default-graph-uri parameter or Web.config settings foreach (String userDefaultGraph in userDefaultGraphs) { if (!userDefaultGraph.Equals(String.Empty)) { query.AddDefaultGraph(new Uri(userDefaultGraph)); } } } else if (!this._config.DefaultGraphURI.Equals(String.Empty)) { //Only applies if the Query doesn't specify any Default Graph if (!query.DefaultGraphs.Any()) { query.AddDefaultGraph(new Uri(this._config.DefaultGraphURI)); } } //Set the Named Graph URIs (if any) if (userNamedGraphs.Count > 0) { foreach (String userNamedGraph in userNamedGraphs) { if (!userNamedGraph.Equals(String.Empty)) { query.AddNamedGraph(new Uri(userNamedGraph)); } } } //Set Timeout setting if (timeout > 0) { query.Timeout = timeout; } else { query.Timeout = this._config.DefaultTimeout; } //Set Partial Results Setting query.PartialResultsOnTimeout = partialResults; //Set Describe Algorithm query.Describer = this._config.DescribeAlgorithm; //Now we can finally make the query and return the results Object result = this.ProcessQuery(query); this.ProcessResults(context, result); //Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (RdfParseException parseEx) { HandleErrors(context, "Parsing Error", queryText, parseEx, (int)HttpStatusCode.BadRequest); } catch (RdfQueryTimeoutException timeoutEx) { HandleErrors(context, "Query Timeout Error", queryText, timeoutEx); } catch (RdfQueryException queryEx) { HandleErrors(context, "Update Error", queryText, queryEx); } catch (RdfWriterSelectionException writerSelEx) { HandleErrors(context, "Output Selection Error", queryText, writerSelEx, (int)HttpStatusCode.NotAcceptable); } catch (RdfException rdfEx) { HandleErrors(context, "RDF Error", queryText, rdfEx); } catch (Exception ex) { HandleErrors(context, "Error", queryText, ex); } }
/// <summary> /// Processes a SPARQL Query Request /// </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[] queries; String queryText = 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": //GET expects a query parameter in the querystring queries = context.Request.QueryString.GetValues("query"); if (queries != null) { if (queries.Length > 1) { throw new ArgumentException("The query parameter was specified multiple times in the querystring"); } queryText = queries.Length == 1 ? queries[0] : null; } //If no Query sent either show Query Form or give a HTTP 400 response if (String.IsNullOrEmpty(queryText)) { //If there is no Query we may return the SPARQL Service Description where appropriate try { //If we might show the Query Form only show the Description if the selected writer is //not a HTML writer MimeTypeDefinition definition = MimeTypesHelper.GetDefinitions(HandlerHelper.GetAcceptTypes(webContext)).FirstOrDefault(d => d.CanWriteRdf); if (definition != null) { IRdfWriter writer = definition.GetRdfWriter(); if (!this._config.ShowQueryForm || !(writer is IHtmlWriter)) { //If not a HTML Writer selected OR not showing Query Form 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 } //Otherwise we'll either show the Query Form or return a 400 Bad Request if (this._config.ShowQueryForm) { this.ShowQueryForm(context); } else { throw new ArgumentException("Missing required query parameter"); } return; } //Get the Default Graph URIs (if any) if (context.Request.QueryString["default-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.QueryString.GetValues("default-graph-uri")); } //Get the Named Graph URIs (if any) if (context.Request.QueryString["named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.QueryString.GetValues("named-graph-uri")); } break; case "POST": //POST requires a valid content type 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 a query parameter in the Form parameters queries = context.Request.Form.GetValues("query"); if (queries == null) { throw new ArgumentException("Required query parameter in POST body was missing"); } if (queries.Length == 0) { throw new ArgumentException("Required query parameter in POST body was missing"); } if (queries.Length > 1) { throw new ArgumentException("The query parameter was specified multiple times in the POST body"); } queryText = queries[0]; //For Form URL Encoded the Default/Named Graphs may be specified by Form parameters //Get the Default Graph URIs (if any) if (context.Request.Form["default-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.Form.GetValues("default-graph-uri")); } //Get the Named Graph URIs (if any) if (context.Request.Form["named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.Form.GetValues("named-graph-uri")); } break; } else if (contentType.Type.Equals(MimeTypesHelper.SparqlQuery)) { //application/sparql-query 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.SparqlQuery + " Content-Type but a non UTF-8 charset parameter"); } //Read the query from the request body using (StreamReader reader = new StreamReader(context.Request.InputStream)) { queryText = reader.ReadToEnd(); reader.Close(); } //For application/sparql-query the Default/Named Graphs may be specified by querystring parameters //Get the Default Graph URIs (if any) if (context.Request.QueryString["default-graph-uri"] != null) { userDefaultGraphs.AddRange(context.Request.QueryString.GetValues("default-graph-uri")); } //Get the Named Graph URIs (if any) if (context.Request.QueryString["named-graph-uri"] != null) { userNamedGraphs.AddRange(context.Request.QueryString.GetValues("named-graph-uri")); } break; } else { throw new ArgumentException("HTTP POST made to SPARQL query endpoint had an invalid Content-Type header, only " + MimeTypesHelper.WWWFormURLEncoded + " and " + MimeTypesHelper.SparqlQuery + " 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 Query endpoint"); } //Get non-standard options associated with the query long timeout = 0; bool partialResults = this._config.DefaultPartialResults; //Get Timeout setting (if any) if (context.Request.QueryString["timeout"] != null) { if (!Int64.TryParse(context.Request.QueryString["timeout"], out timeout)) { timeout = this._config.DefaultTimeout; } } else if (context.Request.Form["timeout"] != null) { if (!Int64.TryParse(context.Request.Form["timeout"], out timeout)) { timeout = this._config.DefaultTimeout; } } //Get Partial Results Setting (if any); if (context.Request.QueryString["partialResults"] != null) { if (!Boolean.TryParse(context.Request.QueryString["partialResults"], out partialResults)) { partialResults = this._config.DefaultPartialResults; } } else if (context.Request.Form["partialResults"] != null) { if (!Boolean.TryParse(context.Request.Form["partialResults"], out partialResults)) { partialResults = this._config.DefaultPartialResults; } } //Now we're going to parse the Query SparqlQueryParser parser = new SparqlQueryParser(this._config.Syntax); parser.DefaultBaseUri = context.Request.Url; parser.ExpressionFactories = this._config.ExpressionFactories; parser.QueryOptimiser = this._config.QueryOptimiser; SparqlQuery query = parser.ParseFromString(queryText); query.AlgebraOptimisers = this._config.AlgebraOptimisers; query.PropertyFunctionFactories = this._config.PropertyFunctionFactories; //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; } //Is this user allowed to make this kind of query? if (requireActionAuth) { HandlerHelper.IsAuthenticated(webContext, this._config.UserGroups, this.GetPermissionAction(query)); } //Clear query dataset if there is a protocol defined one userDefaultGraphs.RemoveAll(g => String.IsNullOrEmpty(g)); userNamedGraphs.RemoveAll(g => String.IsNullOrEmpty(g)); bool isProtocolDataset = false; if (userDefaultGraphs.Count > 0 || userNamedGraphs.Count > 0) { query.ClearDefaultGraphs(); query.ClearNamedGraphs(); isProtocolDataset = true; } //Set the Default Graph URIs (if any) if (isProtocolDataset) { foreach (String userDefaultGraph in userDefaultGraphs) { query.AddDefaultGraph(UriFactory.Create(userDefaultGraph)); } } else if (!this._config.DefaultGraphURI.Equals(String.Empty)) { //Only applies if the Query doesn't specify any Default Graph and there wasn't a protocol defined //dataset present if (!query.DefaultGraphs.Any()) { query.AddDefaultGraph(UriFactory.Create(this._config.DefaultGraphURI)); } } //Set the Named Graph URIs (if any) if (isProtocolDataset) { query.ClearNamedGraphs(); foreach (String userNamedGraph in userNamedGraphs) { query.AddNamedGraph(UriFactory.Create(userNamedGraph)); } } //Set Timeout setting if (timeout > 0) { query.Timeout = timeout; } else { query.Timeout = this._config.DefaultTimeout; } //Set Partial Results Setting query.PartialResultsOnTimeout = partialResults; //Set Describe Algorithm query.Describer = this._config.DescribeAlgorithm; //Now we can finally make the query and return the results Object result = this.ProcessQuery(query); this.ProcessResults(context, result); //Update the Cache as the request may have changed the endpoint this.UpdateConfig(context); } catch (RdfParseException parseEx) { HandleErrors(context, "Parsing Error", queryText, parseEx, (int)HttpStatusCode.BadRequest); } catch (RdfQueryTimeoutException timeoutEx) { HandleErrors(context, "Query Timeout Error", queryText, timeoutEx); } catch (RdfQueryException queryEx) { HandleErrors(context, "Update Error", queryText, queryEx); } catch (RdfWriterSelectionException writerSelEx) { HandleErrors(context, "Output Selection Error", queryText, writerSelEx, (int)HttpStatusCode.NotAcceptable); } catch (RdfException rdfEx) { HandleErrors(context, "RDF Error", queryText, 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", queryText, ex); } }