Mercurial > p > roundup > code
comparison roundup/rest.py @ 7596:e5fa31aad344
fix: replace bad reverted code change; allow js rate headers
Last commit included an incorrect undo. I was going to move the Allow
header/output format parsing earlier in the dispatch method. But I
reverted it incorrectly and removed it instead. It has been added back
in the former location.
Header that allows javascript access to the rest rate limit header has
been moved. The rate limit headers can be accessed by client side
javascript regardless of the rate limit being exceeded.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Thu, 03 Aug 2023 18:28:19 -0400 |
| parents | 26ef5054e510 |
| children | 86ad72b3a258 |
comparison
equal
deleted
inserted
replaced
| 7595:26ef5054e510 | 7596:e5fa31aad344 |
|---|---|
| 2090 self.client.setHeader('Retry-After', '1') | 2090 self.client.setHeader('Retry-After', '1') |
| 2091 | 2091 |
| 2092 msg = _("Api rate limits exceeded. Please wait: %s seconds.") % retry_after | 2092 msg = _("Api rate limits exceeded. Please wait: %s seconds.") % retry_after |
| 2093 output = self.error_obj(429, msg, source="ApiRateLimiter") | 2093 output = self.error_obj(429, msg, source="ApiRateLimiter") |
| 2094 | 2094 |
| 2095 # expose these headers to rest clients. Otherwise they can't | |
| 2096 # respond to: | |
| 2097 # rate limiting (*RateLimit*, Retry-After) | |
| 2098 # obsolete API endpoint (Sunset) | |
| 2099 # options request to discover supported methods (Allow) | |
| 2100 self.client.setHeader( | |
| 2101 "Access-Control-Expose-Headers", | |
| 2102 ", ".join([ | |
| 2103 "X-RateLimit-Limit", | |
| 2104 "X-RateLimit-Remaining", | |
| 2105 "X-RateLimit-Reset", | |
| 2106 "X-RateLimit-Limit-Period", | |
| 2107 "Retry-After", | |
| 2108 "Sunset", | |
| 2109 "Allow", | |
| 2110 ]) | |
| 2111 ) | |
| 2112 | |
| 2095 return self.format_dispatch_output( | 2113 return self.format_dispatch_output( |
| 2096 self.__default_accept_type, | 2114 self.__default_accept_type, |
| 2097 output, | 2115 output, |
| 2098 True # pretty print for this error case as a | 2116 True # pretty print for this error case as a |
| 2099 # human may read it | 2117 # human may read it |
| 2124 "POST method not %s." % (override, method.upper())) | 2142 "POST method not %s." % (override, method.upper())) |
| 2125 logger.info( | 2143 logger.info( |
| 2126 'Ignoring X-HTTP-Method-Override using %s request on %s', | 2144 'Ignoring X-HTTP-Method-Override using %s request on %s', |
| 2127 method.upper(), uri) | 2145 method.upper(), uri) |
| 2128 | 2146 |
| 2147 # parse Accept header and get the content type | |
| 2148 # Acceptable types ordered with preferred one first | |
| 2149 # in list. | |
| 2150 try: | |
| 2151 accept_header = parse_accept_header(headers.get('Accept')) | |
| 2152 except UsageError as e: | |
| 2153 output = self.error_obj(406, _("Unable to parse Accept Header. %(error)s. " | |
| 2154 "Acceptable types: %(acceptable_types)s") % { | |
| 2155 'error': e.args[0], | |
| 2156 'acceptable_types': " ".join(sorted(self.__accepted_content_type.keys()))}) | |
| 2157 accept_header = [] | |
| 2158 | |
| 2159 if not accept_header: | |
| 2160 accept_type = self.__default_accept_type | |
| 2161 else: | |
| 2162 accept_type = None | |
| 2163 for part in accept_header: | |
| 2164 if accept_type: | |
| 2165 # we accepted the best match, stop searching for | |
| 2166 # lower quality matches. | |
| 2167 break | |
| 2168 if part[0] in self.__accepted_content_type: | |
| 2169 accept_type = self.__accepted_content_type[part[0]] | |
| 2170 # Version order: | |
| 2171 # 1) accept header version=X specifier | |
| 2172 # application/vnd.x.y; version=1 | |
| 2173 # 2) from type in accept-header type/subtype-vX | |
| 2174 # application/vnd.x.y-v1 | |
| 2175 # 3) from @apiver in query string to make browser | |
| 2176 # use easy | |
| 2177 # This code handles 1 and 2. Set api_version to none | |
| 2178 # to trigger @apiver parsing below | |
| 2179 # Places that need the api_version info should | |
| 2180 # use default if version = None | |
| 2181 try: | |
| 2182 self.api_version = int(part[1]['version']) | |
| 2183 except KeyError: | |
| 2184 self.api_version = None | |
| 2185 except (ValueError, TypeError): | |
| 2186 # TypeError if int(None) | |
| 2187 msg = ("Unrecognized api version: %s. " | |
| 2188 "See /rest without specifying api version " | |
| 2189 "for supported versions." % ( | |
| 2190 part[1]['version'])) | |
| 2191 output = self.error_obj(400, msg) | |
| 2192 | |
| 2193 # get the request format for response | |
| 2194 # priority : extension from uri (/rest/data/issue.json), | |
| 2195 # header (Accept: application/json, application/xml) | |
| 2196 # default (application/json) | |
| 2197 ext_type = os.path.splitext(urlparse(uri).path)[1][1:] | |
| 2198 | |
| 2199 # Check to see if the length of the extension is less than 6. | |
| 2200 # this allows use of .vcard for a future use in downloading | |
| 2201 # user info. It also allows passing through larger items like | |
| 2202 # JWT that has a final component > 6 items. This method also | |
| 2203 # allow detection of mistyped types like jon for json. | |
| 2204 if ext_type and (len(ext_type) < 6): | |
| 2205 # strip extension so uri make sense | |
| 2206 # .../issue.json -> .../issue | |
| 2207 uri = uri[:-(len(ext_type) + 1)] | |
| 2208 else: | |
| 2209 ext_type = None | |
| 2210 | |
| 2211 # headers.get('Accept') is never empty if called here. | |
| 2212 # accept_type will be set to json if there is no Accept header | |
| 2213 # accept_type wil be empty only if there is an Accept header | |
| 2214 # with invalid values. | |
| 2215 data_type = ext_type or accept_type or headers.get('Accept') or "invalid" | |
| 2129 if method.upper() == 'OPTIONS': | 2216 if method.upper() == 'OPTIONS': |
| 2130 # add access-control-allow-* access-control-max-age to support | 2217 # add access-control-allow-* access-control-max-age to support |
| 2131 # CORS preflight | 2218 # CORS preflight |
| 2132 self.client.setHeader( | 2219 self.client.setHeader( |
| 2133 "Access-Control-Allow-Headers", | 2220 "Access-Control-Allow-Headers", |
