Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EasyRdf incorrectly uses Accept: headers #301

Open
TallTed opened this issue Sep 14, 2018 · 1 comment · May be fixed by #405
Open

EasyRdf incorrectly uses Accept: headers #301

TallTed opened this issue Sep 14, 2018 · 1 comment · May be fixed by #405
Labels

Comments

@TallTed
Copy link
Contributor

TallTed commented Sep 14, 2018

A mutual user reported that EasyRdf was delivering an EasyRdfSparqlResult, instead of their desired EasyRdf_Graph, when running CONSTRUCT SPARQL queries against Virtuoso endpoints. (Also see this post.)

The user perceived this to be a problem with Virtuoso, but the reason turns out to be that EasyRdf is issuing these requests with --

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0

EasyRdf turns each of these specified content-types into an EasyRdfSparqlResult.

EasyRdf clearly wants an RDF serialization back, which it can turn into an EasyRdf_Graph, but it's asking for text/html or application/xhtml+xml with equal (top) preference (implied q=1.0), and if those aren't available, for application/xml;q=0.9, and only if none of those are available, for whatever the SPARQL server can give it (*/*;q=0).

EasyRdf should be asking for RDF serializations it can turn into an EasyRdf_Graph, such as text/turtle, application/rdf+xml, application/trig, application/n-triples, application/n-quads, application/ld+json, sparql-results+xml...

@TallTed TallTed changed the title EasyRDF incorrectly uses Accept: headers EasyRdf incorrectly uses Accept: headers Sep 14, 2018
@njh njh added the bug label Jun 12, 2020
simontaurus added a commit to simontaurus/easyrdf that referenced this issue May 4, 2024
@simontaurus
Copy link

simontaurus commented May 4, 2024

The problem is that default Accept header mentioned by @TallTed are set for CONSTRUCT queries here

} elseif ($query_verb === 'CONSTRUCT' or $query_verb === 'DESCRIBE') {
// only "graph"
$accept = Format::getHttpAcceptHeader();
} else {

by calling
public static function getHttpAcceptHeader(array $extraTypes = array())

Workaround is to create a subclass of \EasyRdf\Sparql\Client to override the executeQuery methode:

class CustomSparqlClient extends \EasyRdf\Sparql\Client {

   /**
     * Build http-client object, execute request and return a response
     *
     * @param string $processed_query
     * @param string $type            Should be either "query" or "update"
     *
     * @return Http\Response|\Zend\Http\Response
     * @throws Exception
     */
    protected function executeQuery($processed_query, $type)
    {
        $client = \EasyRdf\Http::getDefaultHttpClient();
        $client->resetParameters();

        // Tell the server which response formats we can parse
        $sparql_results_types = array(
            'application/sparql-results+json' => 1.0,
            'application/sparql-results+xml' => 0.8
        );
	$sparql_graph_types = array(
            'application/ld+json' => 1.0,
	    'application/rdf+xml' => 0.9,
            'text/turtle' => 0.8,
	    'application/n-quads' => 0.7,
	    'application/n-triples' => 0.7,
        );

        if ($type == 'update') {
            // accept anything, as "response body of a […] update request is implementation defined"
            // @see http://www.w3.org/TR/sparql11-protocol/#update-success
            $accept = \EasyRdf\Format::getHttpAcceptHeader($sparql_results_types);
            $this->setHeaders($client, 'Accept', $accept);

            $client->setMethod('POST');
            $client->setUri($this->updateUri);
            $client->setRawData($processed_query);
            $this->setHeaders($client, 'Content-Type', 'application/sparql-update');
        } elseif ($type == 'query') {
            $re = '(?:(?:\s*BASE\s*<.*?>\s*)|(?:\s*PREFIX\s+.+:\s*<.*?>\s*))*'.
                '(CONSTRUCT|SELECT|ASK|DESCRIBE)[\W]';

            $result = null;
            $matched = mb_eregi($re, $processed_query, $result);

            if (false === $matched or count($result) !== 2) {
                // non-standard query. is this something non-standard?
                $query_verb = null;
            } else {
                $query_verb = strtoupper($result[1]);
            }

            if ($query_verb === 'SELECT' or $query_verb === 'ASK') {
                // only "results"
                $accept = \EasyRdf\Format::formatAcceptHeader($sparql_results_types);
            } elseif ($query_verb === 'CONSTRUCT' or $query_verb === 'DESCRIBE') {
                // only "graph"
                $accept = \EasyRdf\Format::formatAcceptHeader($sparql_graph_types);
            } else {
                // both
                $accept = \EasyRdf\Format::getHttpAcceptHeader($sparql_results_types);
            }

            $this->setHeaders($client, 'Accept', $accept);

            $encodedQuery = 'query=' . urlencode($processed_query);

            // Use GET if the query is less than 2kB
            // 2046 = 2kB minus 1 for '?' and 1 for NULL-terminated string on server
            if (strlen($encodedQuery) + strlen($this->queryUri) <= 2046) {
                $delimiter = $this->queryUri_has_params ? '&' : '?';

                $client->setMethod('GET');
                $client->setUri($this->queryUri . $delimiter . $encodedQuery);
            } else {
                // Fall back to POST instead (which is un-cacheable)
                $client->setMethod('POST');
                $client->setUri($this->queryUri);
                $client->setRawData($encodedQuery);
                $this->setHeaders($client, 'Content-Type', 'application/x-www-form-urlencoded');
            }
        } else {
            throw new Exception('unexpected request-type: '.$type);
        }

        if ($client instanceof \Zend\Http\Client) {
            return $client->send();
        } else {
            return $client->request();
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants