Opened 10 years ago
Last modified 9 years ago
#5463 new Enhancement
add CORS in XML RPC response
Reported by: | bertrand.gressier | Owned by: | |
---|---|---|---|
Priority: | Normal | Milestone: | None Set |
Component: | Transmission | Version: | 2.82 |
Severity: | Normal | Keywords: | |
Cc: |
Description
Hi.
Thanks for your tool. it's very powerful
I need a feature. I develop a client side application in javascript angular and I can't directly call transmission server because I have CORS exception in the brower.
My solution, I use a proxy pass and inject missed headers. Access-Control-Allow-Origin "*" Access-Control-Expose-Headers "X-Transmission-Session-Id" Access-Control-Allow-Methods "GET,POST,PUT,OPTIONS,DELETE" Header always set Access-Control-Allow-Headers "Authorization, Content-Type, X-Transmission-Session-Id"
And it works.
If you can add this header directly in the product, switch on with a flag in the config file. It would be fantastic.
tks for all
Change History (11)
comment:1 Changed 9 years ago by ibizaman
comment:2 Changed 9 years ago by ibizaman
I'm currently implementing it.
Just to be sure it matches the author's will, here is what I'm planning to do:
When making a POST CORS request, the browser first sends a similar OPTION request (some headers are omitted) :
OPTIONS /transmission/rpc HTTP/1.1 Host: 192.168.42.30:9091 Access-Control-Request-Method: POST Origin: http://192.168.42.21:8000 Access-Control-Request-Headers: content-type
The server should then answer with at least these headers:
HTTP/1.1 200 OK Access-Control-Allow-Origin: 192.168.42.21 Access-Control-Request-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-Transmission-Session-Id, Content-Type
Then the browser will send the real POST request and the server will answer by repeating the Access-Control-Allow-Origin header.
So first thing to implement is the handling of the OPTIONS request.
Second, on what basis does the server choose to send or not the headers, i.e. allows the request to be executed or not? I would make this decision based on the rpc-whitelist and rpc-whitelist-enabled parameters. If the rpc-whitelist is enabled and one of the rpc-whitelist mask matches the address in the origin header, then the server sends back the origin header in the Access-Control-Allow-Origin header. By sending back the origin header and not the mask, the server avoids to say too much about the configuration.
By the way, the server documentation says the rpc-whitelist must be a list of addresses. If the handling of CORS requests is based on rpc-whitelist, the configuration should allow domain names also. When browsing the rpc-server.c file, I don't see anything requesting this. Why is this imposed then?
Please leave any comments. :)
comment:3 Changed 9 years ago by ibizaman
Update on where I am:
I added this function to libtransmission/rpc-server.c:
@@ -543,8 +548,42 @@ evbuffer_free (buf); tr_free (data); } + +static void +handle_cors_headers (struct evhttp_request * req, struct tr_rpc_server * server, int headers) +{ + if (server->isWhitelistEnabled) + { + tr_list * l; + + for (l=server->whitelist; l!=NULL; l=l->next) + { + char* mask = l->data; + if (tr_wildmat (req->remote_host, mask)) + break; + } + + if (l != NULL) + { + const char * origin = evhttp_find_header (req->input_headers, "Origin"); + evhttp_add_header (req->output_headers, "Access-Control-Allow-Origin", origin); +#ifdef REQUIRE_SESSION_ID + evhttp_add_header (req->output_headers, "Access-Control-Expose-Headers", "X-Transmission-Session-Id"); +#endif + evhttp_add_header (req->output_headers, "Access-Control-Allow-Methods", "GET,POST"); + } + } +}
It is used in handle_rpc_from_json:
static void handle_rpc_from_json (struct evhttp_request * req, struct tr_rpc_server * server, const char * json, @@ -552,6 +591,9 @@ { struct rpc_response_data * data; + handle_cors_headers (req, server); +
And in handle_request when issuing a 403 (i.e. get new api key):
@@ -682,16 +732,19 @@ "<p><code>%s: %s</code></p>", TR_RPC_SESSION_ID_HEADER, sessionId); evhttp_add_header (req->output_headers, TR_RPC_SESSION_ID_HEADER, sessionId); + handle_cors_headers (req, server);
With this, I think I covered nearly every cases:
- Handling of REQUIRE_SESSION_ID,
- Handling of 403 request
I'm still battling with the handling of the OPTIONS request. I really don't know what's going on since when receiving that request, the code doesn't even go in the handle_request function of rpc_server.c and the client gets a 501 request. The only file in which this error is related is third-party/libevent/http.c but it's a third-party library so I should not touch it, right?
This is the function I'm talking about, I added the OPTIONS handling (see commented line), did a make install in the third-party/libevent folder and a make clean && make in the root transmission folder but it does not work.
static struct evhttp* evhttp_new_object(void) { struct evhttp *http = NULL; if ((http = mm_calloc(1, sizeof(struct evhttp))) == NULL) { event_warn("%s: calloc", __func__); return (NULL); } http->timeout = -1; evhttp_set_max_headers_size(http, EV_SIZE_MAX); evhttp_set_max_body_size(http, EV_SIZE_MAX); evhttp_set_allowed_methods(http, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_HEAD | EVHTTP_REQ_PUT | EVHTTP_REQ_DELETE | EVHTTP_REQ_OPTIONS); // added this TAILQ_INIT(&http->sockets); TAILQ_INIT(&http->callbacks); TAILQ_INIT(&http->connections); TAILQ_INIT(&http->virtualhosts); TAILQ_INIT(&http->aliases); return (http); }
I'm a little bit lost here and don't know if I'm on the right path here.
comment:4 Changed 9 years ago by mike.dld
Couldn't you try adding EVHTTP_REQ_OPTIONS later, in startServer () (libtransmission/rpc-server.c)? Something like
// ... server->httpd = evhttp_new (server->session->event_base); // this line is already there evhttp_set_allowed_methods (server->httpd, ... | EVHTTP_REQ_OPTIONS); // this is what you add // ...
comment:5 Changed 9 years ago by bertrand.gressier
Hello,
Tks to work on this feature.
Have you find a solution to your problem ?
I'm very happy to see this feature in the next release. This solution open the door to new client-side application
If I can help, warn me
comment:6 Changed 9 years ago by bertrand.gressier
- Milestone changed from None Set to Sometime
comment:7 Changed 9 years ago by livings124
- Milestone changed from Sometime to None Set
comment:8 Changed 9 years ago by ibizaman
Hi!
I did manage to make it kind of work. Kind of since it was not thoroughly tested. I must also say I didn't work on this lately.
About testing, I have a major concern: how to automate it since it implies using a web browser? Obviously the behavior of a client using CORS could be mocked but I don't think I'll be able to do it reliably.
Here is the current state of modifications: http://pastebin.com/iAkAcrGy
comment:9 Changed 9 years ago by BathZ
Hi, I'm very interested in this ticket.
I'm currently running a Lighttpd instance to proxy the request to transmission RPC. the config is a little hack-ish : https://gist.github.com/bathizte/80f40f27f02452f98fd6. Nginx until now and without the "more headers" module does not allow to add headers on every response such as a 409.
PhantomJS can be used to test the RPC responses to a web browser.
comment:10 Changed 9 years ago by jordan
ibizaman, are you still working on this?
comment:11 Changed 9 years ago by ibizaman
Jordan, I'm sorry but not really. Although the code I've posted above have some quirks, it's a good start. Do you plan taking over?
I second this request. This would be very nice.
In the meantime, can the author of the issue explain how he installed and configured the proxy?
Thanks!