]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/libs/objectcache/RESTBagOStuff.php
MediaWiki 1.30.2
[autoinstalls/mediawiki.git] / includes / libs / objectcache / RESTBagOStuff.php
1 <?php
2
3 /**
4  * Interface to key-value storage behind an HTTP server.
5  *
6  * Uses URL of the form "baseURL/{KEY}" to store, fetch, and delete values.
7  *
8  * E.g., when base URL is `/v1/sessions/`, then the store would do:
9  *
10  * `PUT /v1/sessions/12345758`
11  *
12  * and fetch would do:
13  *
14  * `GET /v1/sessions/12345758`
15  *
16  * delete would do:
17  *
18  * `DELETE /v1/sessions/12345758`
19  *
20  * Configure with:
21  *
22  * @code
23  * $wgObjectCaches['sessions'] = array(
24  *      'class' => 'RESTBagOStuff',
25  *      'url' => 'http://localhost:7231/wikimedia.org/v1/sessions/'
26  * );
27  * @endcode
28  */
29 class RESTBagOStuff extends BagOStuff {
30
31         /**
32          * @var MultiHttpClient
33          */
34         private $client;
35
36         /**
37          * REST URL to use for storage.
38          * @var string
39          */
40         private $url;
41
42         public function __construct( $params ) {
43                 if ( empty( $params['url'] ) ) {
44                         throw new InvalidArgumentException( 'URL parameter is required' );
45                 }
46                 parent::__construct( $params );
47                 if ( empty( $params['client'] ) ) {
48                         $this->client = new MultiHttpClient( [] );
49                 } else {
50                         $this->client = $params['client'];
51                 }
52                 // Make sure URL ends with /
53                 $this->url = rtrim( $params['url'], '/' ) . '/';
54                 // Default config, R+W > N; no locks on reads though; writes go straight to state-machine
55                 $this->attrMap[self::ATTR_SYNCWRITES] = self::QOS_SYNCWRITES_QC;
56         }
57
58         /**
59          * @param string $key
60          * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
61          * @return mixed Returns false on failure and if the item does not exist
62          */
63         protected function doGet( $key, $flags = 0 ) {
64                 $req = [
65                         'method' => 'GET',
66                         'url' => $this->url . rawurlencode( $key ),
67                 ];
68
69                 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
70                 if ( $rcode === 200 ) {
71                         if ( is_string( $rbody ) ) {
72                                 return unserialize( $rbody );
73                         }
74                         return false;
75                 }
76                 if ( $rcode === 0 || ( $rcode >= 400 && $rcode != 404 ) ) {
77                         return $this->handleError( "Failed to fetch $key", $rcode, $rerr );
78                 }
79                 return false;
80         }
81
82         /**
83          * Handle storage error
84          * @param string $msg Error message
85          * @param int $rcode Error code from client
86          * @param string $rerr Error message from client
87          * @return false
88          */
89         protected function handleError( $msg, $rcode, $rerr ) {
90                 $this->logger->error( "$msg : ({code}) {error}", [
91                         'code' => $rcode,
92                         'error' => $rerr
93                 ] );
94                 $this->setLastError( $rcode === 0 ? self::ERR_UNREACHABLE : self::ERR_UNEXPECTED );
95                 return false;
96         }
97
98         /**
99          * Set an item
100          *
101          * @param string $key
102          * @param mixed $value
103          * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
104          * @param int $flags Bitfield of BagOStuff::WRITE_* constants
105          * @return bool Success
106          */
107         public function set( $key, $value, $exptime = 0, $flags = 0 ) {
108                 $req = [
109                         'method' => 'PUT',
110                         'url' => $this->url . rawurlencode( $key ),
111                         'body' => serialize( $value )
112                 ];
113                 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
114                 if ( $rcode === 200 || $rcode === 201 ) {
115                         return true;
116                 }
117                 return $this->handleError( "Failed to store $key", $rcode, $rerr );
118         }
119
120         /**
121          * Delete an item.
122          *
123          * @param string $key
124          * @return bool True if the item was deleted or not found, false on failure
125          */
126         public function delete( $key ) {
127                 $req = [
128                         'method' => 'DELETE',
129                         'url' => $this->url . rawurlencode( $key ),
130                 ];
131                 list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $this->client->run( $req );
132                 if ( $rcode === 200 || $rcode === 204 || $rcode === 205 ) {
133                         return true;
134                 }
135                 return $this->handleError( "Failed to delete $key", $rcode, $rerr );
136         }
137 }