So, here's what I'm doing with the API:
Auth (to get token and publicUrl for the particular region I need from the "object-store")
Use the publicUrl from the endpoint like so to get a list of files:
GET [publicUrl]/[container]
This returns an array where each item (object) looks like the following:
(
[hash] => 7213ee9a7d9dc119d2921a40e899ec5e
[last_modified] => 2015-12-29T02:46:08.400490
[bytes] => 1
[name] => Some type of file name.jpg
[content_type] => application/postscript
)
Now, how do I build the url to do a GET on the item (object)? I've tried the following:
[publicUrl]/[container]/[hash]
[publicUrl]/[container]/urlencoded([name])
among other things that don't make sense, but I tried anyway.
Any thoughts/help would be appreciated!
If you are using a Rackspace SDK, you can skip building the URLs yourself.
Here is the documentation for retrieving a Cloud Files object using a public URL. The object URL is the combination of the public URL of the container (found in the X-Cdn-Uri response header) with the object name appended.
For example, for a container named 'foo', send an authenticated HEAD request to the API:
HEAD {cloudFilesEndpoint}/foo
In the response, the container's public URL is in the 'X-Cdn-Uri' header:
HTTP/1.1 204 No Content
X-Cdn-Ssl-Uri: https://83c49b9a2f7ad18250b3-346eb45fd42c58ca13011d659bfc1ac1.ssl.cf0.rackcdn.com
X-Ttl: 259200
X-Cdn-Uri: http://081e40d3ee1cec5f77bf-346eb45fd42c58ca13011d659bfc1ac1.r49.cf0.rackcdn.com
X-Cdn-Enabled: True
X-Log-Retention: False
X-Cdn-Streaming-Uri: http://084cc2790632ccee0a12-346eb45fd42c58ca13011d659bfc1ac1.r49.stream.cf0.rackcdn.com
X-Trans-Id: tx82a6752e00424edb9c46fa2573132e2c
Content-Length: 0
Now, for an object named 'styles/site.css', append that name to the public URL, resulting in the following URL:
http://081e40d3ee1cec5f77bf-346eb45fd42c58ca13011d659bfc1ac1.r49.cf0.rackcdn.com/styles/site.css
Related
I am trying to use WordPress' media_sideload_image with a remotely hosted image in S3 in order to save it into WordPress' media gallery.
But for whatever reason, I always get a forbidden response no matter what I try and do regarding request options for the WordPress request. Visiting the URL directly in the browser works, wget works, postman works.
Does anyone have any ideas on how to make WordPress be able to successfully download this file?
Here's the code I'm using:
$attachment_ID = media_sideload_image('https://s3.amazonaws.com/mlsgrid/images/0788b2c2-d865-496b-bad3-69ebe9c1db79.png');
And here's the WordPres error response I get:
object(WP_Error)[2090]
public 'errors' =>
array (size=1)
'http_404' =>
array (size=1)
0 => string 'Forbidden' (length=9)
public 'error_data' =>
array (size=1)
'http_404' =>
array (size=2)
'code' => int 403
'body' => string '<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>39B59073BBC1205F</RequestId><HostId>6TwMl4cMbLXzr7jbx6ykQKaQuk0Rn5Oyc2Q3+02zmgtNoDqUvcg8VY32qGuS1ZMzgpZuLAefK3g=</HostId></Error>' (length=243)
protected 'additional_data' =>
array (size=0)
empty
Thanks!
After digging around WordPress' request functionality, it looks like it is setting a referer header on each request to be the same as the URL being fetched and I guess Amazon S3 rejects requests with a referer header set? (not sure if that is specific to the bucket I'm fetching images from or true across every single bucket).
Here's how I got it working by removing the referer header from the request, basically just filter for all S3 URLs and remove the referer request header.
// Remove referer from request headers if the URL is an S3 bucket.
add_action( 'http_api_curl', function ($ch, $parsed_args, $url) {
$s3_url = 'https://s3.amazonaws.com';
if (substr($url, 0, strlen($s3_url)) === $s3_url) {
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Referer:']);
}
}, 10, 3);
I am using Play Framework / Reactjs in a single page application.
The authentication is built using ActionBuilders, such that any async request made from the browser is protected by the "AuthorizedAction".
Here is the implementation of the ActionBuiler's invokeBlock:
override def invokeBlock[A](request: Request[A], block: (AuthorizedRequest[A]) ⇒ Future[Result]): Future[Result] = {
val maybeToken = request.headers.get(AuthTokenHeader).orElse(request.getQueryString(AuthTokenUrlKey))
maybeToken match {
case Some(token) => cache.get[Long](token) match {
case Some(u_id) =>
val user = User.findByID(u_id).get
val req = AuthorizedRequest(request, user)
block(req)
case None => Future.successful(Unauthorized) // REDIRECT HERE
}
case None => Future.successful(Unauthorized) // REDIRECT HERE
}
}
This is working great, with one exception: I would like instead of simply returning Unauthorized to also redirect to the login page. Because as of now, if the token times out, and the user causes an async request to be fired, it will simply error out in the console, and the page will look broken.
method 1:
replace the two Future.successful lines above with:
Future.successful(Redirect("/login"))
this actually does call the controller for the /login route, however the URL does not change. Instead, the response of the async request that was fired contains the data =
`<!DOCTYPE html ...
Here's an example of async request whose response.data contains the screenshot text below:
axios('/ping')
.then((response) => {
let userID = response.data; // contains junk
store.dispatch(SomeAction.xyz(userID))
})
.catch((error) => {
browserHistory.push('/login');
})
Looks like I'm missing something crucial here. Any ideas how to proceed from here on?
method 2:
on the front-end, use React router's browserHistory.push('/login')
in all the async requests' .catch()
Though I would like to avoid this as much as possible, if method 1 can successfully work.
Thanks
Turns out this has less to do with Play Framework and more with HTTP codes/responses and how they are interpreted by the browser.
It seems we should not return Redirects to async requests, as they would cause the issue of method 1 in the question.
Instead, people have been suggesting one of the following:
return Ok/200 with some specific header indicating to the front-end that a redirect must take place
return some custom error code, e.g. Status(xyz) that the front-end code would understand and interpret as a request to redirect to some route.
Choosing the appropriate HTTP code is another topic, and from the two options above, we have three choices:
200 or custom 2xx code, interpreted as a success and handled as such in the promise
3xx custom code, may seem appropriate as we are asking for a redirect
401 or custom 4xx code. Technically, the incoming async request was Unauthorized (401), and so should be an error level response.
The crucial part is that handling the response code seems like it will have to be at the front-end, along the lines of method 2 in the question.
I'm leaning towards the default 401
I have a custom service that must return data in CSV format.
I can't use a standard Express route, because I need Feathers' hooks on this endpoint.
I couldn't find an example of a Feathers service returning non-HTML, non-JSON data, and have found no way to specify a response content type.
Using res.set('Content-Type', 'text/csv') before returning from the service method didn't work; the final Content-Type header was reset to application/json, even though the method's return value was a regular string.
How can I properly set arbitrary response content types in Feathers' custom service methods?
You can customize the response format like this:
const feathers = require('feathers');
const rest = require('feathers-rest');
const app = feathers();
function restFormatter(req, res) {
res.format({
'text/plain': function() {
res.end(`The Message is: "${res.data.text}"`);
}
});
}
app.configure(rest(restFormatter));
The complete documentation can be found here.
Using your own service specific middleware to send the response should also work.
When I set TTLs explicitly in a controller
$response->setMaxAge(60);
$response->setSharedMaxAge(60);
everything works perfectly OK and I get the right response headers:
X-Symfony-Cache GET /hello/show/: miss, store
... and then ...
X-Symfony-Cache GET /hello/show/: stale, invalid, store
... and then ...
X-Symfony-Cache GET /hello/show/: fresh
But when I don't set TTLs explicitly in a controller and want to use default TTL settings:
class AppCache extends HttpCache {
protected function getOptions() {
return array(
'debug' => true,
'default_ttl' => 60,
'private_headers' => array('Authorization', 'Cookie'),
'allow_reload' => false,
'allow_revalidate' => false,
'stale_while_revalidate' => 2,
'stale_if_error' => 60,
);
}
}
I always get a "miss".
X-Symfony-Cache GET /hello/show/: miss
It looks like Symfony doesn't take the default_ttl setting into account. Why?
I found the reason. It's due to private/public reponses. The documentation states:
Public vs private Responses¶
Both gateway and proxy caches are considered "shared" caches as the
cached content is shared by more than one user. If a user-specific
response were ever mistakenly stored by a shared cache, it might be
returned later to any number of different users. Imagine if your
account information were cached and then returned to every subsequent
user who asked for their account page!
To handle this situation, every response may be set to be public or
private:
public: Indicates that the response may be cached by both private and shared caches;
private: Indicates that all or part of the response message is intended for a single user and must not be cached by a shared cache.
Symfony conservatively defaults each response to be private. To take
advantage of shared caches (like the Symfony2 reverse proxy), the
response will need to be explicitly set as public
So to make Symfony use the default_ttl setting you have to explicitly set your Response to be public like so:
$response->setPublic();
I am hitting RestfulServer via an ajax call (url: BaseHref + "api/v1/Post/" + postId + '/PostTracks' to retrieve DataObject relations:
public function PostTracks(){
$controller = Controller::curr();
$request = $controller->getRequest();
$passkey = $request->getHeader('passkey');
$tracks = $this->owner->Tracks();
$set = array();
foreach($tracks as $track)
{
$set[] = array(
'm4aURL' => $track->m4a()->URL,
'oggURL' => $track->ogg()->URL,
'Title' => $track->Title
);
}
$this->outputJSON(200, $set);
}
At the top of the method I am trying to grab the value of a custom header that I sent in my ajax call via the beforeSend method. I have verified that the header is sent in the request to RestfulServer controller, but am having trouble getting the value.I am not getting anything for the value of $passkey.
How can I get header info from a RestfulServer controller. I don't understand why getRequest isn't working since RestfulServer extends from Controller.
You can use print_r($request->getHeaders()) to see all the headers attached to the request. In any case, I suspect the issue is with the casing of "passkey". By default SilverStripe will parse header names in CamelCaseFormat - so I suspect the header will be called Passkey or PassKey.
One nice way to debug issues with request is using Debug::dump($request->getHeaders()) or Debug::log($request->getHeaders()).
The latter will write a log file to the site that you can then track if you have terminal access to the server by "tail -f debug.log", or downloading them again and again.
That way you can see what logs out when you cant drirectly access the url.