Coverage report: /development/source/library/org/datagraph/spocq-shard/src/spocq-server/configuration.lisp
| Kind | Covered | All | % |
| expression | 44 | 210 | 21.0 |
| branch | 0 | 22 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; -*- Mode: lisp; Syntax: ansi-common-lisp; Base: 10; Package: org.datagraph.spocq.server.implementation; -*-
3
(in-package :org.datagraph.spocq.server.implementation)
5
(:documentation "http configuration"
6
"The HTTP configuration is managed similar to that for a query processor which is executed
7
as a command. In addition to initialization settings, each request establishes a dynamic
8
context for global configuration variables and extends it with the http request configuration.
9
This involves applying parse-http-configuration to the collected header and query arguments,
10
as returned by request-configuration-list. The parse step extracts those arguments which
11
are http settings in *http-argument-to-setting-map*, canonicalizing the argument name and
14
(defparameter *http-argument-to-setting-map* (make-hash-table :test 'equalp)
15
"A hash table to translate between http run-time setting keywords.
16
It includes both header and query parameter names, but just those which are to be captured
18
Any parameters to be retrieved locally, as required, are left in the request or response object.
20
- remote-ip-address : local
21
- acceptCharset : utf-8 should be established as an explicit default, but is not
22
- acceptEncoding : local
23
- ifRange : not supported
24
- Range : first-last or limit x offset y
25
- auth_token : local to the authentication functions
26
- IfModifiedSince/IfUnmodifiedSince: local to theNotModified response logic
27
- $.* -> bindings : processed locally in execute-graph-store-query
29
! needs to go into an around function in graph-store-response :
30
! request->link_jsonld_context() -> settings['context']
31
xsl-stylesheet needs to be post-processed
32
- agent-id came from the dydra-query front-end an is not present from http directly
33
- timeout = set global parameter
34
- settings : came from the dydra-uery front-end
38
(loop for (setting-name . argument-names)
39
in '((:accept-date-time "acceptDateTime")
41
(:agent-location "agentLocation")
43
(:base-iri "base" "baseIRI")
44
(:blank-node-prefix "skolemizePrefix")
45
(:blank-node-skolemize "skolemize")
46
(:default-context-term "defaultContextTerm")
47
(:default-graphs "defaultGraphs" "from" "defaultGraphURI")
48
(:describe-form "describeForm")
49
(:describe-object-depth "describeObjectDepth")
50
(:describe-subject-depth "describeSubjectDepth")
51
(:dynamic-bindings "dynamicBindings")
52
(:federation-mode "federationMode")
54
(:import-limit "importLimit")
55
(:link "link") ; recognize link headers
56
(:memory-limit "memoryLimit")
57
(:named-contexts-term "namedContextsTerm")
58
(:named-graphs "namedGraphs" "fromNamed" "namedGraphURI")
59
(:namespace-bindings "prefixes" "namespaceBindings")
60
(:operation-limit "operationLimit")
61
(:provenance-repository-id "provenanceRepositoryID" "provenanceRepository")
62
(:query-signature "signature" "querySignature")
64
(:repository-id "repositoryID" "repository")
65
(:request-content-type "contentType" "queryContentType" "requestContentType")
66
(:request-values "values" "valuesData")
67
(:response-charset "acceptCharset")
68
(:response-content-type "accept" "responseContentType")
69
(:response-encoding "acceptEncoding")
70
(:response-limit "limit" "responseLimit" "queryLimit")
71
(:response-offset "offset" "responseOffset" "queryOffset")
72
(:revision-end-time "revisionEndTime")
73
(:revision-id "revisionUUID" "revisionID" "revision")
74
(:revision-start-time "revisionStartTime")
75
(:library-path "libraryPath" "librarypath" "library-path" )
76
(:start-time "startTime")
77
(:task-id "Request-ID" "queryID" "id" "taskID")
78
(:time-interval "timeInterval")
79
(:time-limit "timeLimit")
80
(:trace-routing-key "traceRoutingKey")
81
(:undefined-variable-behavior "undefinedVariableBehavior")
82
(:user-id "userID" "userTag" "clientRequestID")
83
(:xslt-stylesheet "xslt" "xsltstylesheet"))
84
do (loop for argument-name in argument-names
85
do (setf (gethash argument-name *http-argument-to-setting-map*) setting-name)))
87
(defun http-argument-setting (argument-name)
88
(gethash argument-name *http-argument-to-setting-map*))
91
20170520: reconcile with php
92
the php chain is nginx->php(dydra-query)->shell(dydra-query)->sharedmem->store->spocq
93
of which the nginx,dydra-query. ans spoc interfaces each has its representation for the options
94
this implements the direct acquisition from the http headers
95
- /opt/rails/public/cgi-bin/sparql.php
96
- /development/source/library/com/unfuddle/dydra/dydra-ndk/src/query/query.cc
100
(defun parse-http-configuration (configuration-association-list &optional (argument-list ()))
101
"Given an http a-list with the query and/or form arguments, assemble a property
102
list in which the keys are the canonical settings' names and then canonicalize this to
103
produce a request configuration p-list with converted assembled compound argument values.
105
The process involves a case-insensitive comparison with a key value which is net all
106
non-alphanumeroc characters."
108
(loop for (argument-name . value) in configuration-association-list
109
for canonical-argument-name = (remove-if-not #'alpha-char-p (string argument-name))
110
for setting-name = (http-argument-setting canonical-argument-name)
112
do (case setting-name
113
((:values :values-data :request-values)
114
(push value (getf argument-list :request-values)))
116
(setf argument-list (list* setting-name value argument-list)))))
117
(dydra:canonicalize-argument-list argument-list))
119
(defmethod dydra:canonicalize-configuration-argument ((key (eql :link)) (value string))
120
"Deconstruct the rfc5988 link to extract the target, ref and type
121
determine the effective argument name, transform the value, return the new list.
122
(see https://tools.ietf.org/rfc/rfc5988.txt)
123
This parses all headers, but returns just the first one.
124
multiple headers are not permitted"
125
(let* ((parsed-values (parse-http-link-header value))
126
(parsed-value (first parsed-values)))
127
(when (rest parsed-values)
128
(error 'multiple-context-link-headers))
129
(destructuring-bind (target &rest arguments &key rel &allow-other-keys) parsed-value
130
(when (and target rel)
131
(apply #'canonicalize-link-argument target (cons-symbol :keyword rel) arguments)))))
133
(defun parse-http-link-header (header)
135
(let ((link-phrases (split-string header ","))
136
(target-scanner (load-time-value
137
(cl-ppcre:create-scanner `(:sequence (:greedy-repetition 0 nil :whitespace-char-class)
139
(:register (:sequence (:greedy-repetition 0 nil (:inverted-char-class #\>))))
141
(argument-scanner (load-time-value
142
(cl-ppcre:create-scanner `(:sequence
143
(:greedy-repetition 0 nil :whitespace-char-class)
145
(:greedy-repetition 0 nil :whitespace-char-class)
146
(:register (:greedy-repetition 1 nil (:inverted-char-class #\= :whitespace-char-class)))
147
(:greedy-repetition 0 nil :whitespace-char-class)
149
(:greedy-repetition 0 nil :whitespace-char-class)
151
(:register (:greedy-repetition 1 nil (:inverted-char-class #\")))
153
(loop for phrase in link-phrases
155
(multiple-value-bind (matched target)
156
(cl-ppcre:scan-to-strings target-scanner header)
157
(when matched (let* ((start (length matched))
158
(arguments (loop for (matched binding) = (multiple-value-list (cl-ppcre:scan-to-strings argument-scanner header :start start))
160
append (list (cons-symbol :keyword (aref binding 0)) (aref binding 1))
161
do (incf start (length matched)))))
162
(cons (aref target 0)
165
;;; (parse-http-link-header "<http://json-ld.org/contexts/person>; rel=\"describedby\"; type=\"application/ld+json\"")
169
(defgeneric canonicalize-link-argument (target relation &rest args)
171
(:method ((target t) (relation t) &rest args)
172
(log-warn "unknown link relation: ~s ~s . ~s" target relation args)
174
(:method ((target string) (relation (eql :describedby)) &key type &allow-other-keys)
175
(when (equal type "application/ld+json")
176
(list :context target)))
177
(:method ((target string) (relation (eql :|HTTP://WWW.W3.ORG/NS/JSON-LD#CONTEXT|)) &key type &allow-other-keys)
178
(when (equal type "application/ld+json")
179
(list :context target))))
183
(defmethod dydra:canonicalize-configuration-argument ((key (eql :range)) (value string))
184
"Deconstruct the rfc7233 range either as
187
limit <limit> offset <offset>
188
and normalize to expressit as a slice.
189
(see https://tools.ietf.org/html/rfc7233)"
190
(when (spocq.e:string-starts value "solutions=")
191
(setf value (subseq value 10))
192
(destructuring-bind (first &optional last) (split-string value "-")
194
(let ((first (parse-integer first))
195
(last (parse-integer last)))
196
`(:offset ,first :limit ,(1+ (- last first))))
197
(dydra:parse-term value :production 'sparql-1-0-4::|LimitOffsetClauses|)))))
201
(defgeneric call-with-http-configuration-bound (op &rest args)
202
(:method (op &rest dydra:*request-headers*
204
((:operation dydra:*operation*) dydra:*operation*)
205
((:context json-ld:*context*) json-ld:*context*)
207
(declare (dynamic-extent dydra:*request-headers*))
208
;; (print (list :headers dydra:*request-headers*))
209
(apply #'dydra:call-with-configuration-bound op dydra:*request-headers*)))
212
(defmacro with-http-configuration (parsed-argument-list &body body)
213
(let ((op (gensym "with-http-configuration")))
214
`(flet ((,op () ,@body))
215
(declare (dynamic-extent #',op))
216
;; (print (list :parsed ,parsed-argument-list))
217
(apply #'call-with-http-configuration-bound #',op ,parsed-argument-list))))
221
(defmethod (setf spocq.i:configuration-parameter) ((value list) (parameter (eql :response-functions)))
222
(assert (every #'(lambda (name)
223
(cond ((and (symbolp name) (fboundp name) (typep (symbol-function name) 'http:resource-function)))
225
(warn "Invalid response function name: ~s." name))))
228
"All response functions must be resource functions.")
229
(setq *response-functions* value))
231
(defmethod (setf spocq.i:configuration-parameter) ((value string) (parameter (eql :accept-date-time)))
232
(setq spocq.i::*revision-id* value))
234
(defmethod (setf spocq.i:configuration-parameter) ((value string) (parameter (eql :enable-content-encoding)))
235
(setq *graph-store-get.enable-content-encoding* (if value t nil)))
237
(defmethod (setf spocq.i:configuration-parameter) ((value string) (parameter (eql :history-directory)))
238
(setf (spocq.i:configuration-parameter :history-directory) (pathname value)))
240
(defmethod (setf spocq.i:configuration-parameter) ((value null) (parameter (eql :history-directory)))
241
(setq *history-directory* nil))
243
(defmethod (setf spocq.i:configuration-parameter) ((value pathname) (parameter (eql :history-directory)))
244
(assert (probe-file value) () "Invalid history-directory: ~s." value)
245
(setq *history-directory* value))
247
(defmethod (setf spocq.i:configuration-parameter) ((value integer) (parameter (eql :host-port)))
248
(setq *host-port* value))
250
(defmethod (setf spocq.i:configuration-parameter) ((value integer) (parameter (eql :http-accept-count-limit)))
251
(setq *http-accept-count-limit* value))
253
(defmethod (setf spocq.i:configuration-parameter) ((value symbol) (parameter (eql :http-log-level)))
254
(assert (member value http:*log-levels*) ()
255
"Invalid log-level: ~s." value)
256
(setq http:*log-level* value))
258
(defmethod (setf spocq.i:configuration-parameter) ((value t) (parameter (eql :memento-response-headers)))
259
(setq *memento-response-headers* (not (null value))))
261
(defmethod (setf spocq.i:configuration-parameter) ((value null) (parameter (eql :request-count-limit)))
262
(setq *request-count-limit* value))
264
(defmethod (setf spocq.i:configuration-parameter) ((value integer) (parameter (eql :request-count-limit)))
265
(setq *request-count-limit* value))
267
(defmethod (setf spocq.i:configuration-parameter) ((value t) (parameter (eql :supersede-post-quad-graphs)))
268
(setq *supersede-post-quad-graphs* (not (null value))))
270
(defmethod (setf spocq.i:configuration-parameter) ((value t) (parameter (eql :transport-security-mode)))
271
(assert (member value '(nil :strict)) ()
272
"Invalid transport security mode: ~s" value)
273
(setq *transport-security-mode* value))
275
(defmethod (setf spocq.i:configuration-parameter) ((value symbol) (parameter (eql :taskmaster-class)))
276
(assert (subtypep value 'spocq-taskmaster) ()
277
"Invalid taskmaster class: ~s" value)
278
(setq *class.taskmaster* value))
282
;;; atandard request argument access
284
(defun request-client-request-id (request)
285
(or (http:request-header request :client-request-id)
286
(http:request-query-argument request "USER-ID")
287
(http:request-query-argument request "user_id")))