Depending on the request uri I want to forward the requests with varnish to different servers using data from a mysql database. The database contains 2 fields: name and base_url. If the request goes to /forwards/%name% I want to forward the request to the server/backend defined by base_url. Me first try was to set an existing backend with VRT_SetHdr, but this does not work:
VRT_SetHdr(sp, HDR_REQ, "\010backend:", my_backend, vrt_magic_string_end);
Maybe it's possible to set a header like X-backend and then do the selection with ordinary VCL?
I also want to define the backends on runtime using the same data from mysql. Is this possible anyway?
As you say, you can switch backends in VCL if a header is set, provided that every backends are pre-declared in VCL:
vcl_recv {
# ...
if ( req.http.X-backend ) {
set req.backend = req.http.X-backend;
}
# ...
}
EDIT:
As #Bhaskar has pointed in his comment, an additional if is needed for each backend due to varnish structure assignment restrictions. Something like:
vcl_recv {
# ...
if ( req.http.X-backend ) {
if ( req.http.X-backend == "predefined" ) {
set req.backend = "predefined";
}
}
# ...
}
Related
Salesforce supports different sandboxes.
For example "partial" or "development" sandbox.
Is there a way to detect which kind of sandbox my script is connected to?
I use Python and simple_salesforce.
My Python's not good enough. I can give hints but you'll have to experiment a bit yourself.
https://github.com/simple-salesforce/simple-salesforce "Additional features" says there's internal class that can expose to you session_id and instance.
You can use these to craft a HTTP GET call to
Authorization: Bearer {session_id}
{instance}/services/data/v51.0/limits
The "limits" resource will tell you (among others) what's the data and file storage available in this org. It'll return a JSON similar to
{
...
"DataStorageMB" : {
"Max" : 200,
"Remaining" : 196
},
...
}
Use DataStorageMB.Max and table at bottom of https://help.salesforce.com/articleView?id=sf.data_sandbox_environments.htm&type=5 to figure out where you are. 200 => Developer, 1024 => Developer Pro...
Edit - if you'd be using Apex (maybe exposed as REST service, "simple salesforce" has nice built-in to access them)
Integer storageLimit = OrgLimits.getMap().get('DataStorageMB').getLimit();
System.debug(storageLimit);
String sandboxType;
switch on storageLimit{
when 200 {
sandboxType = 'Developer';
}
when 1024 {
sandboxType = 'Developer Pro';
}
when 5120 {
sandboxType = 'Partial Copy';
}
when else {
sandboxType = 'Full Copy';
}
}
System.debug(sandboxType);
Steps to find the sandbox type:
Setup --> Deployment Settings --> continue -- > you will find the type of sandbox.
I'm setting up a web page using cookies to determine if the user already logged in, using a cookie containing his id. Problem is : The cookie is either not written or the cookie collection is not updated.
I've tried reading the documentation, but it does not define the usage of CookieCollection.
Here's the function where i write my cookie :
function displayData(){
$id = $this->getRequest()->getSession()->read('id');
$cookies = CookieCollection::createFromServerRequest($this->getRequest());
if(!$cookies->has('id')){
$cookie = (new Cookie('id'))
->withValue($id)
->withExpiry(new DateTime('+999 year'))
->withPath('/')
->withDomain('break-first.eu')
->withSecure(true)
->withHttpOnly(true);
$cookies = $cookies->add($cookie);
}
// Other stuff
}
And where I try reading it :
function index(){
$cookies = $this->getRequest()->getCookieCollection();
dd($cookies);
}
I expect having a cookie named "id", but I don't have it. Only CAKEPHP and pll_language are showing up.
First things first, CakePHP provides authentication functionality with cookie authentication, you may want to have a look at that instead of driving a custom solution.
Cookbook > Plugins > Authentication
That being said, what you're doing there will create a cookie collection object, which however is just that, a lone object somewhere in space, it won't affect the state of your application, in order for that to happen you have to actually modify the response object.
However what you're trying to do there doesn't require cookie collections in the first place, you can simply read and write cookies directly via the methods provided by the request and response objects, like:
// will be `null` in case the cookie doesn't exist
$cookie = $this->getRequest()->getCookie('id');
// responses are immutable, they need to be reassinged
this->setResponse(
$this->getResponse()->withCookie(
(new Cookie('id'))
->withValue($id)
->withExpiry(new DateTime('+999 year'))
->withPath('/')
->withDomain('break-first.eu')
->withSecure(true)
->withHttpOnly(true)
)
);
And if you where to use a cookie collection for whatever reason, then you'd use withCookieCollection() to pass it into the response:
$this->setResponse($this->getResponse()->withCookieCollection($cookies));
If you run into strict typing errors, you could for example create a custom reponse class with an overridden Response::convertCookieToArray() method and cast the string to an integer there (make sure that PHP_INT_MAX covers your target date timestamp, 32-Bit incompatibility is why the fix that landed in CakePHP 4.x, probably won't come to 3.x), something like:
src/Http/Response.php
namespace App\Http;
use Cake\Http\Cookie\CookieInterface;
use Cake\Http\Response as CakeResponse;
class Response extends CakeResponse
{
protected function convertCookieToArray(CookieInterface $cookie)
{
$data = parent::convertCookieToArray($cookie);
$data['expire'] = (int)$data['expire'];
return $data;
}
}
You can pass that into the app in your webroot/index.php file, as the second argument of the $server->run() call:
// ...
$server->emit($server->run(null, new \App\Http\Response()));
See also
Cookbook > Request & Response Objects > Request > Cookies
Cookbook > Request & Response Objects > Response > Setting Cookies
Example:
to("xslt:mapping.xsl?saxon=true&transformerCacheSize=5")
When I use Saxon, I will set this property all over the place. Having a String constant for them or creating my own xslt endpoint does not seem to be the proper way.
Is there something I can set those properties for all xslt endpoints?
You can configure it by subscribing to the CamelContext's startupListener and setting the XsltEndpoints' parameters (be careful, because the following example does set every XsltEndpoint endpoint's saxon and transformerCacheSize properties). Example (ctx is an instance of CamelContext):
ctx.addStartupListener((ctx, alreadyStarted) -> {
ctx.getEndpoints().forEach(e -> {
if(e instanceof XsltEndpoint) {
((XsltEndpoint) e).setTransformerCacheSize(5);
((XsltEndpoint) e).setSaxon(true);
}
});
});
In case of Spring Boot saxon=true can be configured using the application.properties file (XsltComponentConfiguration). AFAIK the transformerCacheSize cannot be configured from the properties file, because it is a parameter of the XsltEndpoint.
# application.properties
camel.component.xslt.saxon = true
If the configuration is always the same, you can declare a direct endpoint to handle all requests.
from("direct:my-xslt")
.to("xslt:mapping.xsl?saxon=true&transformerCacheSize=5")
And then from your other routes:
.to("direct:my-xslt")
Direct endpoints run in the same thread, so after all its just a way of isolating parts of the route that do a specific job.
As a bonus if you need to do any kind of transformation/log before/after your xslt that apply to all routes, you can simply do it in your direct route.
I'm trying to create a Gatling scenario which requires switching the protocol to a different host during the test. The user journey is
https://example.com/page1
https://example.com/page2
https://accounts.example.com/signin
https://example.com/page3
so as part of a single scenario, I need to ether switch the protocol defined in the scenario set up, or switch the baseUrl defined on the protocol but I can't figure out how to do that.
A basic scenario might look like
package protocolexample
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class Example extends Simulation {
val exampleHttp = http.baseURL("https://example.com/")
val exampleAccountsHttp = http.baseURL("https://accounts.example.com/")
val scn = scenario("Signin")
.exec(
http("Page 1").get("/page1")
)
.exec(
http("Page 2").get("/page2")
)
.exec(
// This needs to be done against accounts.example.com
http("Signin").get("/signin")
)
.exec(
// Back to example.com
http("Page 3").get("/page3")
)
setUp(
scn.inject(
atOnceUsers(3)
).protocols(exampleHttp)
)
}
I just need to figure out how to ether switch the host or protocol for the 3rd step. I know I can create multiple scenarios but this needs to be a single user flow across multiple hosts.
I've tried directly using the other protocol
exec(
// This needs to be done against accounts.example.com
exampleAccountsHttp("Signin").get("/signin")
)
which results in
protocolexample/example.scala:19: type mismatch;
found : String("Signin")
required: io.gatling.core.session.Session
exampleAccountsHttp("Signin").get("/signin")
and also changing the base URL on the request
exec(
// This needs to be done against accounts.example.com
http("Signin").baseUrl("https://accounts.example.com/").get("/signin")
)
which results in
protocolexample/example.scala:19: value baseUrl is not a member of io.gatling.http.request.builder.Http
You can use an absolute URI (inclusive protocol) as a parameter for Http.get, Http.post and so on.
class Example extends Simulation {
val exampleHttp = http.baseURL("https://example.com/")
val scn = scenario("Signin")
.exec(http("Page 1").get("/page1"))
.exec(http("Page 2").get("/page2"))
.exec(http("Signin").get("https://accounts.example.com/signin"))
.exec(http("Page 3").get("/page3"))
setUp(scn.inject(atOnceUsers(3))
.protocols(exampleHttp))
}
see: https://gatling.io/docs/current/cheat-sheet/#http-protocol-urls-baseUrl
baseURL: Sets the base URL of all relative URLs of the scenario on which the configuration is applied.
I am currently working in gatling.
SOLUTION:
=> WHAT WE USE IF WE HAVE ONE HTTPBASE:
var httpConf1= http.baseUrl("https://s1.com")
=>FOR MULTIPLE HTTPBASE:
var httpConf1=http.**baseUrls**("https://s1.com","https://s2.com","https://s3.com")
You can use baseUrls function which takes a list of httpBase urls.
ANOTHER METHOD:
Reading from csv file all httpbase and storing it in list buffer in scala language and then converting it into list while passing it in http.baseUrls
var listOfTenants:ListBuffer[String] = new ListBuffer[String] //buffer
var httpConf1=http.**baseUrls**(listOfTenants.toList) //converting buffer to list.
Hope it helps!.
Thank you.😊
Assume I have set up an arbitrarily complex Flow[HttpRequest, HttpResponse, Unit].
I can already use said flow to handle incoming requests with
Http().bindAndHandle(flow, "0.0.0.0", 8080)
Now I would like to add logging, leveraging some existing directive, like logRequestResult("my-service"){...}
Is there a way to combine this directive with my flow? I guess I am looking for another directive, something along the lines of
def completeWithFlow(flow: Flow): Route
Is this possible at all?
N.B.: logRequestResult is an example, my question applies to any Directive one might find useful.
Turns out one (and maybe the only) way is to wire and materialize a new flow, and feed the extracted request to it. Example below
val myFlow: Flow[HttpRequest, HttpResponse, NotUsed] = ???
val route =
get {
logRequestResult("my-service") {
extract(_.request) { req ⇒
val futureResponse = Source.single(req).via(myFlow).runWith(Sink.head)
complete(futureResponse)
}
}
}
Http().bindAndHandle(route, "127.0.0.1", 9000)
http://doc.akka.io/docs/akka/2.4.2/scala/http/routing-dsl/overview.html
Are you looking for route2HandlerFlow or Route.handlerFlow ?
I believe Route.handlerFlow will work based on implicits.
eg /
val serverBinding = Http().bindAndHandle(interface = "0.0.0.0", port = 8080,
handler = route2HandlerFlow(mainFlow()))