Jump to content
Welcome to our new Citrix community!

HTTP Callout returned data


Joseph Tuttle

Recommended Posts

Having an issue with HTTP Callout returned data. I have an HTTP Callout reaching out to an API to return 2 values based on a query. Lets call them value1 and value2. They are returned as type TEXT in the format value1,value2. Correct values are returned to the page and are also rewritten as a new header string in the response. I have attempted this using both responses with no success. I do get policy strikes. I have pcapped the RESPONSE from the callout server. It is working correctly.

 

I am returning a type of TEXT to the callout. I have used the following to attempt to extract these values back to the responder.

HTTP.RES.BODY(100) and HTTP,RES.HEADER("MyNewHeaderName")

 

I set the responder to check for a returned value:

SYS.HTTP_CALLOUT(callout_name).CONTAINS("value1") and I have also tried a few other versions. It seems that the value is either not being returned to the responder to act on, or I am not acting on it correctly. Am I going about this totally wrong?

 

Config:

 

Callout Server (Responses are confirmed valid on both page and as a header value):

add lb vserver lb_vsrv_TESTING_BLAHCalloutService HTTP 199.169.259.239 80 -persistenceType NONE -cltTimeout 180 -appflowLog DISABLED

 

HTTP Callout:

add policy httpCallout callout_http_BLAHLookup -vServer lb_vsrv_TESTING_BLAHCalloutService -returnType TEXT -hostExpr "\"199.169.259.239:80\"" -urlStemExpr "\"/SOMEPATH/ANOTHERPATH/\"+HTTP.REQ.HEADER(\"GRABHEADERDATA\").BEFORE_STR(\",\")" -headers Request("Callout Request") -scheme http -resultExpr "HTTP.RES.BODY(100)" -cacheForSecs 600

 

Responder Policy:

add responder policy pol_res_BLAHCheck "SYS.HTTP_CALLOUT(callout_http_BLAHLookup).CONTAINS(\"value1\")" act_res_send_to_maintpage

 

When I send a known triggering value to the responder/callout, the following happens:

 - I have confirmed the Callout Server is both receiving the correct info from the Callout and replying with the correct info

 - Policy strikes increment on the Callout object

 - Responder policy strikes do NOT increment

 

It almost seems like the TEXT is not getting back to the RESPONDER to process. I assume that if I can get these values somehow back to the responder to be processed, they would be available to the audit log as well when logging RESPONDER policy hits to syslog.

 

Thoughts?

Link to comment
Share on other sites

One thing you can do when testing HTTPCallout is try hitting your website (callout) in a browser and then use any type of browser web developer tools (F12 menu) or other utility to look at the request/response headers of the page and test the individual elements.

 

Troubleshooting:

1) Focus on if callout takes data (input) and returns data (output) as you expect. Then test callout with the value1, value2, and other values...

2) Then see if originating lb request is properly supplying the GrabHeaderData:value1,value2 strings that is being used to parse together into the callout.

3) Finally, then we look at the responder policy. Though if the callout invocation is occurring properly in [1] and [2], then the responder policy is usually fine unless its criteria isn't being met (such as case sensitivity). 

4) Notes below, but a full network trace may help OR just a request/response header view when navigating the callout request via a web browser and the original user request to lb vserver.

 

But my basic interpretation of your Callout Request / Response page (the actual callout server), should be something like this: (If I misunderstood, update and we will correct.)

 

1a) First basic callout interpretation

Callout Request sent to:  http://199.169.259.239/SOMEPATH/ANOTHERPATH/

Actual Request structure:

Get /SOMEPATH/ANOTHERPATH/<VALUE of GRABHEADERDATA pre ",">

HTTP/1.1

HOST:  199.169.259.239

 

1b) The originating request must contain the GrabHeaderData header...

So, in this case, the data that you are sending to a LB VSERVER (for example)

Original Request 1 (from client to lb vserver): 

Some request http://<lbfqdn>/<something>  and this request contains the request header:  grabheaderdata:value1,value2

 

You are then extracting the value1 from this header in the original request to add to the Callout request for this transaction.

 

NOTE:  Therefore this header has to be present in the request originating from the client and not the result of a rewrite policy that applies to that request (as responder and the callout) will apply before rewrite is ever performed.

So, can you confirm the original LB request that you are invoking the callout for contains the GrabHeaderData header with the value1,value2 results...

 

 

1c) The callout then returns <value1> somewhere in the response body?

You can test this by sending a web request to the callout with the value1 manually inserted into the path and confirm the response body returns the string you are looking for.

This confirms if the right input is provided to the callout the correct output is returned.

 

So, first make sure the Callout can invoke with the data you expect and return the result you expect in its response body:

 

2) Then the responder policy, if the callout is not invoking or not matching your output, then the callout is false and the responder won't invoke.  

IF the callout is working, then you can focus on why the responder invocation isn't triggering or not.

Typically test callout with value1, value2, and then value3 (something out of bounds) and/or no value.

Then test with client requests that contain value1,<other>  and no value1 to see how the callout behaves.

 

IF the callout works, then the focus is on the responder policy:

1) remember the SYS.HTTP_CALLOUT(callout_http_BLAHLookup).CONTAINS("value1") expression is going to do a case-sensitive comparison on "value1". If you need this case-insensitive 

SYS.HTTP_CALLOUT(callout_http_BLAHLookup).set_text_mode(ignorecase).CONTAINS("value1")

 

 

 

Link to comment
Share on other sites

36 minutes ago, Rhonda Rowland1709152125 said:

 

Thanks Rhonda - I will answer inline below:

 

 

 

One thing you can do when testing HTTPCallout is try hitting your website (callout) in a browser and then use any type of browser web developer tools (F12 menu) or other utility to look at the request/response headers of the page and test the individual elements.

 

Troubleshooting:

1) Focus on if callout takes data (input) and returns data (output) as you expect. Then test callout with the value1, value2, and other values...

2) Then see if originating lb request is properly supplying the GrabHeaderData:value1,value2 strings that is being used to parse together into the callout.

3) Finally, then we look at the responder policy. Though if the callout invocation is occurring properly in [1] and [2], then the responder policy is usually fine unless its criteria isn't being met (such as case sensitivity). 

4) Notes below, but a full network trace may help OR just a request/response header view when navigating the callout request via a web browser and the original user request to lb vserver.

 

To the above - I can confirm via PCAP that the Callout is sending the GET formed correctly with the correct variables and formatted exactly as the API requires and can also confirm via PCAP that the API is returning a VALID response to the Callout. 

 

 

Quote

 

But my basic interpretation of your Callout Request / Response page (the actual callout server), should be something like this: (If I misunderstood, update and we will correct.)

 

1a) First basic callout interpretation

Callout Request sent to:  http://199.169.259.239/SOMEPATH/ANOTHERPATH/

Actual Request structure:

Get /SOMEPATH/ANOTHERPATH/<VALUE of GRABHEADERDATA pre ",">

HTTP/1.1

HOST:  199.169.259.239

 

The above is correct with in the GRABHEADERHDATA value being grabbed from a header value. So the request looks like:

Get /SOMEPATH/ANOTHERPATH/VALUEOFEXTRACTEDHEADER

This all works. Proper data is being sent to the backend and proper response is being returned per PCAP on the Callout LB VSVR and backend service

 

Quote

 

1b) The originating request must contain the GrabHeaderData header...

 

 

This is confirmed.

 

Quote

 

 

You are then extracting the value1 from this header in the original request to add to the Callout request for this transaction.

 

Not value1. Lets use the basic callout example. Lets say I am forwarding an IP as the GrabHeaderDataValue. The Callout looks like (Get /SOMEPATH/ANOTHERPATH/1.2.3.4). That data is being passed to the callout LB VIP. value1,value2 are responses sent back to the callout as a TEXT response both as a BODY value and as a HEADER injected via rewrite in the LB for the callout server. So we query the IP and lets say we return the following as value1,value2 --- Tom,John

 

I returned Tom and John 2 ways. As simple, unformatted BODY TEXT and as a new header Property value via rewrite on the LB VIP for the Callout server. I did this to see which way would be a more effective way to operate on the data once returned to the Callout and the Responder. I don't think it matters.

 

Quote

 

NOTE:  Therefore this header has to be present in the request originating from the client and not the result of a rewrite policy that applies to that request (as responder and the callout) will apply before rewrite is ever performed.

So, can you confirm the original LB request that you are invoking the callout for contains the GrabHeaderData header with the value1,value2 results...

 

Yes. The Callout is being sent with proper form and data and is returning proper data 2 ways. Via the BODY and the Callout server is behind a LB which has a rewrite that adds it as a new header as well. Per above. 

 

Quote

 

1c) The callout then returns <value1> somewhere in the response body?

You can test this by sending a web request to the callout with the value1 manually inserted into the path and confirm the response body returns the string you are looking for.

This confirms if the right input is provided to the callout the correct output is returned.

 

The above is confirmed.

 

Quote

 

So, first make sure the Callout can invoke with the data you expect and return the result you expect in its response body:

 

based on PCAPS of the Callout server at both the LB VIP for it and the backend server, all requests for data are well formed and all responses are correct. We are sending data and the callout server is returning data.

 

Quote

 

2) Then the responder policy, if the callout is not invoking or not matching your output, then the callout is false and the responder won't invoke.  

IF the callout is working, then you can focus on why the responder invocation isn't triggering or not.

Typically test callout with value1, value2, and then value3 (something out of bounds) and/or no value.

Then test with client requests that contain value1,<other>  and no value1 to see how the callout behaves.

 

IF the callout works, then the focus is on the responder policy:

1) remember the SYS.HTTP_CALLOUT(callout_http_BLAHLookup).CONTAINS("value1") expression is going to do a case-sensitive comparison on "value1". If you need this case-insensitive 

SYS.HTTP_CALLOUT(callout_http_BLAHLookup).set_text_mode(ignorecase).CONTAINS("value1")

 

If I set the responder to just TRUE, the correct responder action takes places. If I invoke the callout and inspect the returned TEXT for a value that I know is present in the returned callout TEXT. The responder does not trigger.

 

I know that the Callout Policy is sending the correct data, correctly formatted. I know the Callout Service is responding with proper data and properly formatted.

 

with Callout returning as TEXT...Is that TEXT parsable after being returned to the responder policy? Is there a limit to the operators or formatting of expressions in the responder policy once the callout is invoked and the data is returned?

 

I cannot tell where the transaction is failing, but I know the callout server and it's LB VIP are sending back valid data to the callout. I just don't know whats happening after that. Can the Callout not digest the returned data? Can the responder not accept the data if the callout hands it back to the responder for eval?

 

I have done simple BOOL callouts before, but here I am getting TEXT back and need to operate on it. Doesn't seem to be a lot of docs out there for operating on this TEXT return function.

 

Thanks as always.

 

 

Quote

 

 

 

Link to comment
Share on other sites

24 minutes ago, Joseph Tuttle said:

If I set the responder to just TRUE, the correct responder action takes places. If I invoke the callout and inspect the returned TEXT for a value that I know is present in the returned callout TEXT. The responder does not trigger.

 

I know that the Callout Policy is sending the correct data, correctly formatted. I know the Callout Service is responding with proper data and properly formatted.

 

with Callout returning as TEXT...Is that TEXT parsable after being returned to the responder policy? Is there a limit to the operators or formatting of expressions in the responder policy once the callout is invoked and the data is returned?

 

 

1) TEXT based callouts are very easy to work with. Most common issue is retrieving too little data in the callout response.

Once the callout return value is set, then a responder policy evaluation can look at that text with text operations.

 

Most likely your Callout Response is extracting too little data...

a) for the callout response output:  

Data Type Text if you will return a section of Data to the ADC to look at.

Expression of data to respond extract this data.

By looking at the manual callout invocation (going to a web browser and making the request and to see the response), you will then see how big the response output is.

Depending on if the callout returns data in a header or the response body, will affect your expression.

 

b) For the callout response, likely 100 is too little. For initial testing do  HTTP.RES.BODY(10000)

And turn caching off: 0 seconds during initial testing. REASON: if the callout change per user request, then caching will defeat the purpose.

 

 

3 hours ago, Joseph Tuttle said:

"HTTP.RES.BODY(100)" -cacheForSecs 600

 

2) When the responder evaluates the callout, you then do

sys.http_callout("<calloutname">) and now text based operations are permitted, though the inline editor won't show them, the expression builder will...

So the responder should be able to do sys.http_callout("<calloutname>").set_text_mode(ignorecase).contains("<value of interest>")

 

Reminders:

>> If the callout is down, expression is automatically FALSE and no responder hit

>> If the callout cannot evaluate with inputs, callout is false or undefined, and therefore responder is false

 

If your lb request (original traffic) needs a rewrite to apply before you invoke the callout...this won't work as responder and callout run before rewrite.

 

Look for responder policy hits, undefined hits, and we may have to look at the callout output to make sure the responder expression is correct. (or the callout request is doing what's expected for live traffic)

 

 

  • Like 2
Link to comment
Share on other sites

54 minutes ago, Rhonda Rowland1709152125 said:

 

1) TEXT based callouts are very easy to work with. Most common issue is retrieving too little data in the callout response.

Once the callout return value is set, then a responder policy evaluation can look at that text with text operations.

 

Most likely your Callout Response is extracting too little data...

a) for the callout response output:  

Data Type Text if you will return a section of Data to the ADC to look at.

Expression of data to respond extract this data.

By looking at the manual callout invocation (going to a web browser and making the request and to see the response), you will then see how big the response output is.

Depending on if the callout returns data in a header or the response body, will affect your expression.

 

b) For the callout response, likely 100 is too little. For initial testing do  HTTP.RES.BODY(10000)

And turn caching off: 0 seconds during initial testing. REASON: if the callout change per user request, then caching will defeat the purpose.

 

 

 

2) When the responder evaluates the callout, you then do

sys.http_callout("<calloutname">) and now text based operations are permitted, though the inline editor won't show them, the expression builder will...

So the responder should be able to do sys.http_callout("<calloutname>").set_text_mode(ignorecase).contains("<value of interest>")

 

Reminders:

>> If the callout is down, expression is automatically FALSE and no responder hit

>> If the callout cannot evaluate with inputs, callout is false or undefined, and therefore responder is false

 

If your lb request (original traffic) needs a rewrite to apply before you invoke the callout...this won't work as responder and callout run before rewrite.

 

Look for responder policy hits, undefined hits, and we may have to look at the callout output to make sure the responder expression is correct. (or the callout request is doing what's expected for live traffic)

 

 

 

 

Got me again. Returns were all caps. You rock always!!!

Link to comment
Share on other sites

16 hours ago, Rhonda Rowland1709152125 said:

Perfect.  Always nice when it is an easy fix.  And you did all the hard parts :)

 

You always rock!

 

Two additional things - Can you think of any way with this setup to get the Callout responses back to SYSLOG or inserted into Weblogs on the backend? Extract the text value from the Callout as part of the actual responder operation and log somehow via compound expression? If, in the same/compound expression, I need that data again, can I access the returned TEXT data without another callout? Or would it make more sense to callout as one policy to grab the data and pass that returned value to another policy as a string or header value for action and logging? Thoughts? I am trying to avoid multiple calls for the same data as this will be a high speed operation and latency is a concern.

 

Thanks as always!

Link to comment
Share on other sites

1) Adding to syslog is straightforward,

On the responder policy that invokes the callout, upon callout true / responder policy hit, configure a log action which will include audit details in syslog for this responder policy hit.

If you need every request to the callout logged, whether it triggers a responder result or not, then you would want the callout server on an lb vserver and use a responder/noop policy to do a logaction on the callout request itself.  

 

2)  Getting value to web service...

You can do weblogging from the callout server and log it, or do NSWL to log web logging on behalf of web transactions, but this would result in callout web logs essentially separate from backend traffic management web logs (lb destinations).

 

To use your callout result to your backend server is going to be different.

1) You can also invoke the callout in a rewrite policy and use rewrite to pass results in custom header to backend TM (lb vserver) service destinations.

     BUT this might be affected by where your responder redirects to.

 

2) To limit repeating your callout multiple times on the same transaction, caching might be used but you need to think of details of request.

If you are only logging the value inserted when the responder policy HITS occur, then caching is not needed (I don't think)

>> On redirect request, use a rewrite to insert the necessary header with value.  Since this value is now part of the redirected path, just do a rewrite to extract the path (shouldn't require a second callout invocation).  But only  traffic affected by responder will be "tagged" with header

 

If you want the value1, value2 lookup regardless of whether the responder did a redirect or not and you want it passed back to the backend traffic via a header insertion (and there's no way to retrieve this value from the original request), then you would need to do this callout or a new one for the rewrite policy on the lb vserver traffic as it goes to its final destination.

 

Does the callout change per user/source ip making the request OR per transaction?

If the value1 or value2 doesn't change for a fixed amount of time (regardless of user or transaction using it), then the cache callout result for 5 minutes or 10 mintues MIGHT work.

But if the value changes per user request, then you need a new callout per invocation.  Even with an lb vserver for the callout with caching policies, if the rules aren't right you are giving cached results to the wrong user. So you might have to invoke the callout twice.

 

Responder can't see rewrite; but since after a responder policy hit occurs the user makes a new transaction, a rewrite can apply after responder. If the value you need is in the new redirect URL, then use rewrite based on the current URL and a callout won't be needed.

 

Bottom line:

If you can explain the data the user sends, the callout result/responder redirect, and what you want to pass to the backend service. And if this only applies to redirected results (as opposed to non responder/redirected traffic), there might be a better way to suggest it is done.

 

Link to comment
Share on other sites

  • 2 weeks later...

@Rhonda Rowland

 

All apologies for the delayed response. As always, I appreciate your consult. Let's distill this down a little further - 

 

 - Assuming my callout is returning proper data and that data is actionable by the responder policy and in that simple form, the callout data is not available for logging

 

In researching this, I came across THIS little jewel:

 

https://docs.citrix.com/en-us/citrix-adc/current-release/appexpert/variables/configuring-using-variables.html

https://developer-docs.citrix.com/projects/netscaler-command-reference/en/12.0/ns/ns-assignment/ns-assignment/

 

Which on its face seems perfect for this situation. Lets load the HTTP Callout returned data into a variable, so it is then available for other policy objects without have to perform an additional callout. This article is about as clear as mud LOL. Calling objects that do not exist in the example and such.

 

Anyhoo - 

 

Under the "Assignment Action" is the real crux of the matter.

 

 - Step one enumerates the format of the HTTP Callout (which is working and returning data in the format of value1,value2,value3)

 - Step two creates a variable and that variables parameters (more on the map vs. singleton concept below)

 -Step three creates the aforementioned HTTP Callout and defines it's parameters

 -Step four sets an assignment. This is mapping an identifier (in the case of a map) to a value returned from the HTTP Callout. Creating a table of data. Per below if you will:

image.thumb.png.e4ec8d70da9d297d3062412e195216da.png

 -Step five sets a responder action if something in the returned callout data returns TRUE. Let's say, per the example we do a 403.

 -Step six is a responder policy that calls a variable that does not exist and performs an action that does not exist and this is where the confusion starts.

 -Step seven is another responder that calls the MAP value which then checks the associated VALUE for "BLOCK"

 - Step eight are the bindings

 

Lets ignore and assume that steps one, three, five and eight are functioning perfectly and our intent is to do the following:

 

Step one:

 - Select a proper variable type. I do not need to identify anything on the left side.  My only concern is the data on the right. Should I select a map variable or a singleton? (ulong/text/map). It sounds like an ulong in which the data/index on the left doesn't really matter

 

Step four:

 - Is this really needed if not mapping data? 

 

Step Six

 - Again confusing as it is referencing both a variable and an action that do not exist. This is totally confusing. Is this what is calling the assignment to do the HTTP Callout?

 

Step Seven

 - This actually appears to be parsing the returned callout data table looking for a value and then taking some action

 

My thinking here is that if I can load the callout returned data into a variable, my policy structure would look like:

 

30 Ignore known good traffic

50 Identify targeted traffic NOOP/100

70 NOOP/END

100 perform callout and store in a variable/NEXT

200  examine variable contents with responder. IF true send to 400

300 NOOP/END

400 send variable contents to rate limiting engine as a basis for rate limiting and SYSLOG if TRUE/600

500 NOOP/END

500 rewrite REQ header to inject variable data as a header to send to backend/weblogs/END

 

What are your thoughts?

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...