Passed
Push — master ( 599585...a7cf01 )
by Béla
03:50
created

ServiceHost::getResponseETag()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace POData\OperationContext;
4
5
use POData\Common\Messages;
6
use POData\Common\HttpStatus;
7
use POData\Common\ODataConstants;
8
use POData\Common\Url;
9
use POData\Common\UrlFormatException;
10
use POData\Common\ODataException;
11
use POData\Common\Version;
12
use POData\Common\MimeTypes;
13
use POData\OperationContext\Web\WebOperationContext;
14
15
/**
16
 * Class ServiceHost
17
 *
18
 * It uses an IOperationContext implementation to get/set all context related
19
 * headers/stream info It also validates the each header value
20
 *
21
 * @package POData\OperationContext
22
 */
23
Class ServiceHost
24
{
25
    /**
26
     * Holds reference to the underlying operation context.
27
     *
28
     * @var IOperationContext
29
     */
30
    private $_operationContext;
31
32
    /**
33
     * The absolute request Uri as Url instance.
34
     * Note: This will not contain query string
35
     *
36
     * @var Url
37
     */
38
    private $_fullAbsoluteRequestUri;
39
40
    /**
41
     * The absolute request Uri as string
42
     * Note: This will not contain query string
43
     *
44
     * @var string
45
     */
46
    private $_fullAbsoluteRequestUriAsString = null;
47
48
    /**
49
     * The absolute request Uri as Url instance.
50
     * Note: This will not contain query string
51
     *
52
     * @var Url
53
     */
54
    private $_absoluteRequestUri;
55
56
    /**
57
     * The absolute request Uri as string
58
     * Note: This will not contain query string
59
     *
60
     * @var string
61
     */
62
    private $_absoluteRequestUriAsString = null;
63
64
    /**
65
     * The absolute service uri as Url instance.
66
     * Note: This value will be taken from configuration file
67
     *
68
     * @var Url
69
     */
70
    private $_absoluteServiceUri;
71
72
    /**
73
     * The absolute service uri string.
74
     * Note: This value will be taken from configuration file
75
     *
76
     * @var string
77
     */
78
    private $_absoluteServiceUriAsString = null;
79
80
    /**
81
     * Gets reference to the operation context.
82
     *
83
     * @return IOperationContext
84
     */
85 84
    public function getOperationContext()
86
    {
87 84
        return $this->_operationContext;
88
    }
89
90
    /**
91
     * @param IOperationContext $context the OperationContext implementation to use.
92
     * If null the IlluminateOperationContex will be used.  Defaults to null.
93
     *
94
     * @throws ODataException
95
     */
96 84
    public function __construct(IOperationContext $context = null)
97
    {
98 84
        if (is_null($context)) {
99 84
            $this->_operationContext = new WebOperationContext();
100
        } else {
101
            $this->_operationContext = $context;
102
        }
103
104
        // getAbsoluteRequestUri can throw UrlFormatException
105
        // let Dispatcher handle it
106 84
        $this->_absoluteRequestUri = $this->getAbsoluteRequestUri();
107 84
        $this->_absoluteServiceUri = null;
108
109
        //Dev Note: Andrew Clinton 5/19/16
110
        //_absoluteServiceUri is never being set from what I can tell
111
        //so for now we'll set it as such
112 84
        $this->setServiceUri($this->_getServiceUri());
113
    }
114
115
    /**
116
     * Gets the absolute request Uri as Url instance
117
     * Note: This method will be called first time from constructor.
118
     *
119
     * @throws ODataException if AbsoluteRequestUri is not a valid URI
120
     *
121
     * @return Url
122
     */
123 84
    public function getAbsoluteRequestUri()
124
    {
125 84
        if (is_null($this->_absoluteRequestUri)) {
126 84
            $this->_absoluteRequestUriAsString = $this->_operationContext->incomingRequest()->getRawUrl();
127 84
			$originalRequest = $this->_operationContext->incomingRequest();
128
            // Validate the uri first
129
            try {
130 84
                $this->_fullAbsoluteRequestUriAsString = $this->_absoluteRequestUriAsString;
131 84
                $this->_fullAbsoluteRequestUri = new Url($this->_fullAbsoluteRequestUriAsString);
132 84
				$this->_fullAbsoluteRequestUri->setQueryOptions($originalRequest->getQueryParameters());
133
            } catch (UrlFormatException $exception) {
134
                throw ODataException::createBadRequestError($exception->getMessage());
135
            }
136
137 84
            $queryStartIndex = strpos($this->_absoluteRequestUriAsString, '?');
138 84
            if ($queryStartIndex !== false) {
139
                $this->_absoluteRequestUriAsString = substr(
140
                    $this->_absoluteRequestUriAsString,
141
                    0,
142
                    $queryStartIndex
143
                );
144
            }
145
146
            // We need the absolute uri only not associated components
147
            // (query, fragments etc..)
148 84
            $this->_absoluteRequestUri = new Url($this->_absoluteRequestUriAsString);
149 84
            $this->_absoluteRequestUriAsString = rtrim($this->_absoluteRequestUriAsString, '/');
150
        }
151
152 84
        return $this->_absoluteRequestUri;
153
    }
154
155
    /**
156
     * Gets the absolute request Uri as string
157
     * Note: This will not contain query string
158
     *
159
     * @return string
160
     */
161
    public function getAbsoluteRequestUriAsString()
162
    {
163
        return $this->_absoluteRequestUriAsString;
164
    }
165
166
167
    /**
168
     * Sets the service url from which the OData URL is parsed
169
     *
170
     * @param string $serviceUri The service url, absolute or relative.
171
     *
172
     * @return void
173
     *
174
     * @throws ODataException If the base uri in the configuration is malformed.
175
     */
176 84
    public function setServiceUri($serviceUri)
177
    {
178 84
        if (is_null($this->_absoluteServiceUri)) {
179 84
            $isAbsoluteServiceUri = (strpos($serviceUri, 'http://') === 0)
180 84
                || (strpos($serviceUri, 'https://') === 0);
181
            try {
182 84
                $this->_absoluteServiceUri = new Url($serviceUri, $isAbsoluteServiceUri);
183
            } catch (UrlFormatException $exception) {
184
                throw ODataException::createInternalServerError(Messages::hostMalFormedBaseUriInConfig());
185
            }
186
187 84
            $segments = $this->_absoluteServiceUri->getSegments();
188 84
            $lastSegment = $segments[count($segments) - 1];
189 84
            $endsWithSvc
190 84
                = (substr_compare($lastSegment, '.svc', -strlen('.svc'), strlen('.svc')) === 0);
191 84
            if (!$endsWithSvc
192 84
                || !is_null($this->_absoluteServiceUri->getQuery())
193 84
                || !is_null($this->_absoluteServiceUri->getFragment())
194
            ) {
195
                throw ODataException::createInternalServerError(Messages::hostMalFormedBaseUriInConfig(true));
196
            }
197
198 84
            if (!$isAbsoluteServiceUri) {
199
                $requestUriSegments = $this->_absoluteRequestUri->getSegments();
200
                $i = count($requestUriSegments) - 1;
201
                // Find index of segment in the request uri that end with .svc
202
                // There will be always a .svc segment in the request uri otherwise
203
                // uri redirection will not happen.
204
                for (; $i >= 0; $i--) {
205
                    $endsWithSvc = (substr_compare($requestUriSegments[$i], '.svc', -strlen('.svc'), strlen('.svc')) === 0);
206
                    if ($endsWithSvc) {
207
                        break;
208
                    }
209
                }
210
211
                $j = count($segments) - 1;
212
                $k = $i;
213
                if ($j > $i) {
214
                    throw ODataException::createBadRequestError(
215
                        Messages::hostRequestUriIsNotBasedOnRelativeUriInConfig(
216
                            $this->_absoluteRequestUriAsString,
217
                            $serviceUri
218
                        )
219
                    );
220
                }
221
222
                while ($j >= 0 && ($requestUriSegments[$i] === $segments[$j])) {
223
                    $i--; $j--;
224
                }
225
226
                if ($j != -1) {
227
                    throw ODataException::createBadRequestError(
228
                        Messages::hostRequestUriIsNotBasedOnRelativeUriInConfig(
229
                            $this->_absoluteRequestUriAsString,
230
                            $serviceUri
231
                        )
232
                    );
233
                }
234
235
                $serviceUri = $this->_absoluteRequestUri->getScheme()
236
                    . '://'
237
                    . $this->_absoluteRequestUri->getHost()
238
                    . ':'
239
                    . $this->_absoluteRequestUri->getPort();
240
241
                for ($l = 0; $l <= $k; $l++) {
242
                    $serviceUri .= '/' . $requestUriSegments[$l];
243
                }
244
245
                $this->_absoluteServiceUri = new Url($serviceUri);
246
            }
247
248 84
            $this->_absoluteServiceUriAsString = $serviceUri;
249
        }
250
    }
251
252
253
    /**
254
     * Gets the absolute Uri to the service as Url instance.
255
     * Note: This will be the value taken from configuration file.
256
     *
257
     * @return Url
258
     */
259 84
    public function getAbsoluteServiceUri()
260
    {
261 84
        return $this->_absoluteServiceUri;
262
    }
263
264
    /**
265
     * Gets the absolute Uri to the service as string
266
     * Note: This will be the value taken from configuration file.
267
     *
268
     * @return string
269
     */
270
    public function getAbsoluteServiceUriAsString()
271
    {
272
        return $this->_absoluteServiceUriAsString;
273
    }
274
275
    /**
276
     * Dev Note: Andrew Clinton
277
     * 5/19/16
278
     *
279
     * Currently it doesn't seem that the service URI is ever being built
280
     * so I am doing that here.
281
     *
282
     * return void
283
     */
284 84
    private function _getServiceUri()
285
    {
286 84
        if (($pos = strpos($this->_absoluteRequestUriAsString, ".svc")) !== FALSE) {
287 84
            $serviceUri = substr($this->_absoluteRequestUriAsString, 0, $pos + strlen(".svc")) . '/';
288 84
            return $serviceUri;
289
        }
290
291
        return $this->_absoluteRequestUriAsString;
292
    }
293
294
    /**
295
     * Gets the value for the DataServiceVersion header of the request.
296
     *
297
     * @return string|null
298
     */
299 84
    public function getRequestVersion()
300
    {
301 84
        return $this->_operationContext
302 84
            ->incomingRequest()
303 84
            ->getRequestHeader(ODataConstants::HTTPREQUEST_HEADER_DATA_SERVICE_VERSION);
304
    }
305
306
    /**
307
     * Gets the value of MaxDataServiceVersion header of the request
308
     *
309
     * @return string|null
310
     */
311 84
    public function getRequestMaxVersion()
312
    {
313 84
        return $this->_operationContext
314 84
            ->incomingRequest()
315 84
            ->getRequestHeader(ODataConstants::HTTPREQUEST_HEADER_MAX_DATA_SERVICE_VERSION);
316
    }
317
318
319
    /**
320
     * Get comma separated list of client-supported MIME Accept types
321
     *
322
     * @return string
323
     */
324
    public function getRequestAccept()
325
    {
326
        return $this->_operationContext
327
            ->incomingRequest()
328
            ->getRequestHeader(ODataConstants::HTTPREQUEST_HEADER_ACCEPT);
329
    }
330
331
332
    /**
333
     * Get the character set encoding that the client requested
334
     *
335
     * @return string
336
     */
337
    public function getRequestAcceptCharSet()
338
    {
339
        return $this->_operationContext
340
            ->incomingRequest()
341
            ->getRequestHeader(ODataConstants::HTTPREQUEST_HEADER_ACCEPT_CHARSET);
342
    }
343
344
345
346
    /**
347
     * Get the value of If-Match header of the request
348
     *
349
     * @return null|string
350
     */
351
    public function getRequestIfMatch()
352
    {
353
        return $this->_operationContext
354
            ->incomingRequest()
355
            ->getRequestHeader(ODataConstants::HTTPREQUEST_HEADER_IF_MATCH);
356
    }
357
358
    /**
359
     * Gets the value of If-None-Match header of the request
360
     *
361
     * @return null|string
362
     */
363
    public function getRequestIfNoneMatch()
364
    {
365
        return $this->_operationContext
366
            ->incomingRequest()
367
            ->getRequestHeader(ODataConstants::HTTPREQUEST_HEADER_IF_NONE);
368
    }
369
370
    /**
371
     * Gets the value of Content-Type header of the request
372
     *
373
     * @return string
374
     */
375
    public function getRequestContentType()
376
    {
377
        return $this->_operationContext
378
            ->incomingRequest()
379
            ->getRequestHeader(ODataConstants::HTTP_CONTENTTYPE);
380
    }
381
382
    /**
383
     * Set the Cache-Control header on the response
384
     *
385
     * @param string $value The cache-control value.
386
     *
387
     * @return void
388
     *
389
     @ throws InvalidOperation
390
     */
391
    public function setResponseCacheControl($value)
392
    {
393
        $this->_operationContext->outgoingResponse()->setCacheControl($value);
394
    }
395
396
    /**
397
     * Gets the HTTP MIME type of the output stream
398
     *
399
     * @return string
400
     */
401
    public function getResponseContentType()
402
    {
403
        return $this->_operationContext
404
            ->outgoingResponse()
405
            ->getContentType();
406
    }
407
408
    /**
409
     * Sets the HTTP MIME type of the output stream
410
     *
411
     * @param string $value The HTTP MIME type
412
     *
413
     * @return void
414
     */
415
    public function setResponseContentType($value)
416
    {
417
        $this->_operationContext
418
            ->outgoingResponse()
419
            ->setContentType($value);
420
    }
421
422
    /**
423
     * Sets the content length of the output stream
424
     *
425
     * @param string $value The content length
426
     *
427
     * @return void
428
     *
429
     * @throw Exception if $value is not numeric throws notAcceptableError
430
     */
431
    public function setResponseContentLength($value)
432
    {
433
        if (preg_match('/[0-9]+/', $value)) {
434
            $this->_operationContext->outgoingResponse()->setContentLength($value);
435
        } else {
436
            throw ODataException::notAcceptableError(
437
                "ContentLength:$value is invalid"
438
            );
439
        }
440
    }
441
442
    /**
443
     * Gets the value of the ETag header on the response
444
     *
445
     * @return string|null
446
     */
447
    public function getResponseETag()
448
    {
449
        return $this->_operationContext->outgoingResponse()->getETag();
450
    }
451
452
    /**
453
     * Sets the value of the ETag header on the response
454
     *
455
     * @param string $value The ETag value
456
     *
457
     * @return void
458
     */
459
    public function setResponseETag($value)
460
    {
461
        $this->_operationContext->outgoingResponse()->setETag($value);
462
    }
463
464
    /**
465
     * Sets the value Location header on the response
466
     *
467
     * @param string $value The location.
468
     *
469
     * @return void
470
     */
471
    public function setResponseLocation($value)
472
    {
473
        $this->_operationContext->outgoingResponse()->setLocation($value);
474
    }
475
476
    /**
477
     * Sets the value status code header on the response
478
     *
479
     * @param string $value The status code
480
     *
481
     * @return void
482
     */
483
    public function setResponseStatusCode($value)
484
    {
485
        $floor = floor($value/100);
486
        if ($floor >= 1 && $floor <= 5) {
487
            $statusDescription = HttpStatus::getStatusDescription($value);
0 ignored issues
show
Bug introduced by
$value of type string is incompatible with the type integer expected by parameter $statusCode of POData\Common\HttpStatus::getStatusDescription(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

487
            $statusDescription = HttpStatus::getStatusDescription(/** @scrutinizer ignore-type */ $value);
Loading history...
488
            if (!is_null($statusDescription)) {
489
                $statusDescription = ' ' . $statusDescription;
490
            }
491
492
            $this->_operationContext
493
                ->outgoingResponse()->setStatusCode($value . $statusDescription);
494
        } else {
495
            throw ODataException::createInternalServerError(
496
                'Invalid Status Code' . $value
497
            );
498
        }
499
    }
500
501
    /**
502
     * Sets the value status description header on the response
503
     *
504
     * @param string $value The status description
505
     *
506
     * @return void
507
     */
508
    public function setResponseStatusDescription($value)
509
    {
510
        $this->_operationContext
511
            ->outgoingResponse()->setStatusDescription($value);
512
    }
513
514
    /**
515
     * Sets the value stream to be send a response
516
     *
517
     * @param string &$value The stream
518
     *
519
     * @return void
520
     */
521
    public function setResponseStream(&$value)
522
    {
523
        $this->_operationContext->outgoingResponse()->setStream($value);
524
    }
525
526
    /**
527
     * Sets the DataServiceVersion response header
528
     *
529
     * @param string $value The version
530
     *
531
     * @return void
532
     */
533
    public function setResponseVersion($value)
534
    {
535
        $this->_operationContext->outgoingResponse()->setServiceVersion($value);
536
    }
537
538
    /**
539
     * Get the response headers
540
     *
541
     * @return array<headername, headerValue>
542
     */
543
    public function &getResponseHeaders()
544
    {
545
        return $this->_operationContext->outgoingResponse()->getHeaders();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_operation...esponse()->getHeaders() returns the type array<string,string> which is incompatible with the documented return type POData\OperationContext\headerValue[].
Loading history...
546
    }
547
548
    /**
549
     * Add a header to response header collection
550
     *
551
     * @param string $headerName  The name of the header
552
     * @param string $headerValue The value of the header
553
     *
554
     * @return void
555
     */
556
    public function addResponseHeader($headerName, $headerValue)
557
    {
558
        $this->_operationContext
559
            ->outgoingResponse()->addHeader($headerName, $headerValue);
560
    }
561
562
    /**
563
     * Translates the short $format forms into the full mime type forms
564
     * @param Version $responseVersion the version scheme to interpret the short form with
565
     * @param string $format the short $format form
566
     * @return string the full mime type corresponding to the short format form for the given version
567
     */
568 49
    public static function translateFormatToMime(Version $responseVersion, $format) {
569
        //TODO: should the version switches be off of the requestVersion, not the response version? see #91
570
571
        switch ($format) {
572
573
            case ODataConstants::FORMAT_XML:
574 18
                $format = MimeTypes::MIME_APPLICATION_XML;
575 18
                break;
576
577
            case ODataConstants::FORMAT_ATOM:
578 6
                $format = MimeTypes::MIME_APPLICATION_ATOM;
579 6
                break;
580
581
            case ODataConstants::FORMAT_VERBOSE_JSON:
582 1
                if ($responseVersion == Version::v3()) {
583
                    //only translatable in 3.0 systems
584 1
                    $format = MimeTypes::MIME_APPLICATION_JSON_VERBOSE;
585
                }
586 1
                break;
587
588
            case ODataConstants::FORMAT_JSON:
589 18
                if ($responseVersion == Version::v3()) {
590 6
                    $format = MimeTypes::MIME_APPLICATION_JSON_MINIMAL_META;
591
                } else {
592 12
                    $format = MimeTypes::MIME_APPLICATION_JSON;
593
                }
594 18
                break;
595
596
        }
597
598 49
        return $format . ';q=1.0';
599
    }
600
601
602 84
    public function getFullAbsoluteRequestUri()
603
    {
604 84
        return $this->_fullAbsoluteRequestUri;
605
    }
606
607
}
608