How do I get browsermob-proxy to record ALL response bodies - browsermob-proxy

I'm having trouble with browsermob-proxy and its har exporting feature. Some response bodies are not logged (the entire "text" field is missing)
My setup (using browsermob-proxy 2.1.4)
curl -X POST http://localhost:8080/proxy?port=9091
curl -X PUT "http://localhost:8080/proxy/9091/har?captureHeaders=true&captureCookies=true&captureContent=true"
Some responses are fine:
"response": {
"status": 201,
"statusText": "Created",
"httpVersion": "HTTP/1.1",
"cookies": [],
"headers": [{
"name": "Cache-Control",
"value": "max-age=0, no-cache, no-store"
}, {
"name": "Content-Type",
"value": "application/json"
}, {
"name": "Date",
"value": "Thu, 15 Feb 2018 13:07:39 GMT"
}, {
"name": "Location",
"value": ...
}, {
"name": "Pragma",
"value": "no-cache"
}, {
"name": "Render-Time",
"value": "8"
}, {
"name": "Server",
"value": "openresty"
}, {
"name": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
}, {
"name": "transfer-encoding",
"value": "chunked"
}, {
"name": "Connection",
"value": "keep-alive"
}],
"content": {
"size": 8607,
"mimeType": "application/json",
"text": "{ <actual json body> }",
"comment": ""
}, ...
But some are not (maybe it is the special content/mime type? or maybe it is the gzip content encoding?)
"response": {
"status": 200,
"statusText": "OK",
"httpVersion": "HTTP/1.1",
"cookies": [],
"headers": [{
"name": "Cache-Control",
"value": "max-age=0, no-cache, no-store"
}, {
"name": "Content-Encoding",
"value": "gzip"
}, {
"name": "Content-Type",
"value": "application/some.custom.type-v1+json"
}, {
"name": "Date",
"value": "Thu, 15 Feb 2018 13:07:39 GMT"
}, {
"name": "Pragma",
"value": "no-cache"
}, {
"name": "Render-Time",
"value": "92"
}, {
"name": "Server",
"value": "openresty"
}, {
"name": "Strict-Transport-Security",
"value": "max-age=31536000; includeSubDomains"
}, {
"name": "Vary",
"value": "Accept-Encoding"
}, {
"name": "Content-Length",
"value": "1978"
}, {
"name": "Connection",
"value": "keep-alive"
}],
"content": {
"size": 7429,
"mimeType": "application/some.custom.type-v1+json",
"comment": ""
< there's nothing else here!! >
},
"redirectURL": "",
"headersSize": 444,
"bodySize": 1978,
"comment": ""
}, ...
Oh yea, both requests are over https, using MITM.

I think I found the answer to my own question
In BrowserMobHttpClient.java I found the following:
private boolean hasTextualContent(String contentType) {
return contentType != null && contentType.startsWith("text/") ||
contentType.startsWith("application/x-javascript") ||
contentType.startsWith("application/javascript") ||
contentType.startsWith("application/json") ||
contentType.startsWith("application/xml") ||
contentType.startsWith("application/xhtml+xml");
}
Looks like I'll have go make a custom build of browsermob-proxy to make it work.

Related

Bad Request "ErrorExecuteSearchStaleData" in Microsoft Graph API - Search for Mails

We are using Microsoft Graph Search API to search through our O365 emails. Since the search only allows 25 results per request for mails. (see https://learn.microsoft.com/en-us/graph/api/resources/search-api-overview?view=graph-rest-beta#page-search-results)
We figured to work around this by batching our search request like this:
POST https://graph.microsoft.com/beta/$batch
{
"requests": [
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-0",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 0,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-1",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 25,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-2",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 50,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-3",
"body":
{
"requests": [
{
"entityTypes": ["message"],
"query":
{
"query_string":
{
"query": "some search text"
}
},
"from": 75,
"size": 25
}
]
},
"headers":
{
"Content-Type": "application/json"
}
}
]
}
This works usually well, but in some cases the API returns something like this:
{
"id": "MyMails-2",
"status": 400,
"headers":
{
"Link": "<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,RemoveDeprecatedUnderscoreProperty&from=2021-12-01&to=2022-01-01>;rel=\"deprecation\";type=\"text/html\",<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,RemoveDeprecatedUnderscoreProperty&from=2021-12-01&to=2022-01-01>;rel=\"deprecation\";type=\"text/html\",<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,RemoveDeprecatedUnderscoreProperty&from=2021-12-01&to=2022-01-01>;rel=\"deprecation\";type=\"text/html\"",
"Deprecation": "Tue, 14 Dec 2021 23:59:59 GMT",
"Sunset": "Sat, 31 Dec 2022 23:59:59 GMT",
"Cache-Control": "no-cache",
"Content-Type": "application/json"
},
"body":
{
"error":
{
"code": "BadRequest",
"message": "\r\n An exception occurred\r\n Lss call failed with status code 400. \"Exchange service returned error ErrorExecuteSearchStaleData: Please reissue the query with rowOffset = 0. The specified rowoffset is '50', but the results are stale.\".",
"innerError":
{
"date": "2022-01-25T09:58:53",
"request-id": "75def95f-a857-427d-a8b4-ee2792329e87",
"client-request-id": "75def95f-a857-427d-a8b4-ee2792329e87"
}
}
}
}
I noticed the deprecation note in the header, however I can't find anything about it. Neither about the actual exception.
What am I missing?
[workaround]
Thanks for the hint #user2250152.
This heavily reduced the issue by splitting the request into two requests, while the second is able to request more than 25 mails at a time:
POST https://graph.microsoft.com/beta/$batch
{
"requests": [
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-0",
"body": {
"requests": [
{
"entityTypes": [
"message"
],
"query": {
"query_string": {
"query": "some search text"
}
},
"from": 0,
"size": 25
}
]
},
"headers": {
"Content-Type": "application/json"
}
},
{
"url": "/search/query",
"method": "POST",
"id": "MyMails-1",
"body": {
"requests": [
{
"entityTypes": [
"message"
],
"query": {
"query_string": {
"query": "some search text"
}
},
"from": 25,
"size": 200
}
]
},
"headers": {
"Content-Type": "application/json"
}
}
]
}
The error with stale results can happen time to time.
You can decrease the number of batch requests to reduce a chance that the error with stale results will occur.
For the first page "from": 0 the max size is 25. But for the next page "from": 25 you can increase the page size to 200.
I've tested the search query with "from": 25 and "size": 200 and it returns 200 results.
Resources:
Page search results

Call MS Graph API from MS Flow with Binary Body

Have an MS Flow trying to call the Graph API to update a user's profile photo. It takes a username and jpg photo file and then calls an HTTP connection secured using Azure AD (with https://graph.microsoft.com as the resource URI). Unfortunately, I get the error The file you chose isn't an image. Please choose a different file. which seems to indicate that I am not posting the file encoded in a way that MS Graph recognizes.
I've tried all variations I can think of with the body (using the #binary, #base64ToBinary, etc):
"body": "#{triggerBody()?['file']?['contentBytes']}"
to try to get it to treat "correctly", but with no luck.
This is the full flow definition:
{
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"$connections": {
"defaultValue": {},
"type": "Object"
},
"$authentication": {
"defaultValue": {},
"type": "SecureObject"
}
},
"triggers": {
"manual": {
"type": "Request",
"kind": "Button",
"inputs": {
"schema": {
"type": "object",
"properties": {
"file": {
"title": "Photo",
"type": "object",
"x-ms-dynamically-added": true,
"description": "Please select file or image",
"x-ms-content-hint": "FILE",
"properties": {
"name": {
"type": "string"
},
"contentBytes": {
"type": "string",
"format": "byte"
}
}
},
"text": {
"title": "Username",
"type": "string",
"x-ms-dynamically-added": true,
"description": "user#domain.com",
"x-ms-content-hint": "TEXT"
}
},
"required": [
"text"
]
}
}
}
},
"actions": {
"Invoke_an_HTTP_request": {
"runAfter": {},
"metadata": {
"flowSystemMetadata": {
"swaggerOperationId": "InvokeHttp"
}
},
"type": "ApiConnection",
"inputs": {
"host": {
"connection": {
"name": "#json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$connections']['webcontents_1']['connectionId']"
}
},
"method": "post",
"body": {
"method": "PUT",
"url": "https://graph.microsoft.com/v1.0/users/#{triggerBody()['text']}/photo/$value",
"headers": {
"Content-Type": "image/jpeg"
},
"body": "#{triggerBody()?['file']?['contentBytes']}"
},
"path": "/codeless/InvokeHttp",
"authentication": {
"type": "Raw",
"value": "#json(decodeBase64(triggerOutputs().headers['X-MS-APIM-Tokens']))['$ConnectionKey']"
}
}
}
}
}
and the full error:
{
"error": {
"code": 502,
"message": "{\r\n \"error\": {\r\n \"code\": 502,\r\n \"source\": \"unitedstates-002.azure-apim.net\",\r\n \"clientRequestId\": \"e105d9f2-af13-4a5b-8fbd-c84c49cadc8f\",\r\n \"message\": \"BadGateway\",\r\n \"innerError\": {\r\n \"error\": {\r\n \"code\": \"ErrorInternalServerError\",\r\n \"message\": \"An internal server error occurred. The operation failed., The file you chose isn't an image. Please choose a different file.\",\r\n \"innerError\": {\r\n \"request-id\": \"e3214f20-8592-4fb0-9d34-677322b231ae\",\r\n \"date\": \"2019-07-30T21:42:10\"\r\n }\r\n }\r\n }\r\n }\r\n}"
}
}

Reply sent email by Gmail API

Currently I'm trying to create send and reply using gmail api, in documentation here
Refernces and In-Reply-To must be set in compliance with the RFC 2822 standard, the problem is when I try to get References and In-Reply-To from specified id like below:
`{
"id": "16183e0822247c79",
"threadId": "16183e0822247c79",
"labelIds": [
"SENT"
],
"snippet": "terkait",
"historyId": "1640387",
"internalDate": "1518335984000",
"payload": {
"partId": "",
"mimeType": "multipart/mixed",
"filename": "",
"headers": [
{
"name": "Received",
"value": "from 1059028371380 named unknown by gmailapi.google.com with HTTPREST; Sat, 10 Feb 2018 23:59:44 -0800"
},
{
"name": "Date",
"value": "Sat, 10 Feb 2018 23:59:44 -0800"
},
{
"name": "From",
"value": "jaisanas2#gmail.com"
},
{
"name": "To",
"value": "jaisanas3#gmail.com"
},
{
"name": "Message-Id",
"value": "\u003cCA+8aSZeXMOETdH8NYtd18UWk5eiQnvT0oEnEWy_1HL6mJPuKjw#mail.gmail.com\u003e"
},
{
"name": "Subject",
"value": "terkait"
},
{
"name": "Mime-Version",
"value": "1.0"
},
{
"name": "Content-Type",
"value": "multipart/mixed; boundary=\"--==_mimepart_5a7ff7f050e3_3263ffa0ceb1cc020ea\"; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0",
"mimeType": "multipart/alternative",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "multipart/alternative; boundary=\"--==_mimepart_5a7ff7f05063_3263ffa0ceb1cc01916\"; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"body": {
"size": 0
},
"parts": [
{
"partId": "0.0",
"mimeType": "text/html",
"filename": "",
"headers": [
{
"name": "Content-Type",
"value": "text/html; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"body": {
"size": 14,
"data": "PHA-dGVya2FpdDwvcD4="
}
}
]
}
]
},
"sizeEstimate": 929
}`
When you see the result there are no headers In-Reply-To and Refernces, my questions is it is possible to reply email using API ?
Here is my code in ruby:
client = google_client user_id
token = Token.find_by_user_id(user_id)
access_token = token.access_token
gmail = Google::Apis::GmailV1::GmailService.new
gmail.authorization = client
message = Mail.new
message.date = Time.now
message.subject = "Re: #{subject}"
message.from = token.email
message.to = "#{to}"
# message.thread_id = "#{thread_id}"
message.message_id = "\u003cCA+8aSZeXMOETdH8NYtd18UWk5eiQnvT0oEnEWy_1HL6mJPuKjw#mail.gmail.com\u003e"
message.part content_type: 'multipart/alternative' do |part|
part.html_part = Mail::Part.new(body: "#{body}", content_type: 'text/html; charset=UTF-8')
end
msg = message.encoded
message_object = Google::Apis::GmailV1::Message.new(raw:message.to_s, thread_id: thread_id, content_type: 'message/rfc822')
gmail.send_user_message('me', message_object)
This code successfully send email in the same thread, but not reply email, here is what looks like inside my gmail sent emails:
As you can see, message with body lauv does not reply message terkait instead I just sent email lauv, my question is how to reply email?
Here you need to set In-Reply-To and References headers. If there is only one email in the thread use the message_id as In-Reply-To and References

Swagger - Set the "response" based on "consumes"

Is it possible to specify the "response" based on the "consumes" option on Swagger?
My API can return "json" or "text" and I'd like that the "example value", when the response's status is 200 changes if the user selects from Response Content Type the option application/json or text/plain.
This is a piece of my swagger.json file:
{
"swagger": "2.0",
"produces": [
"application/json",
"text/plain"
],
"consumes" : [
"application\/json"
],
"paths": {
"/writer/1/": {
"get": {
"summary": "Get all writers",
"description": "Writer description.",
"tags": [
"writer"
],
"responses": {
"200": {
"description" : "successful operation",
"schema" : {
"$ref" : "#\/definitions\/writerResponse"
}
},
}
}
}
"definitions": {
"writerResponse": {
"properties": {
"meta": {
"type" :"object",
"schema" : {
"$ref" : "#\/definitions\/metaDefinition"
}
},
"data": {
"type" :"array",
"items": {
"$ref" : "#\/definitions\/writer"
}
}
}
},
"writer": {
"properties": {
"id": {
"type": "integer",
"description": "writer id.",
"example": "1477"
},
"short": {
"type": "string",
"description": "short description.",
"example": "short example"
},
"modified": {
"type": "string",
"description": "modified description.",
"example": "2016-05-21 22:58:36"
}
}
},
}
This is an example of the JSON output when user selects application/json:
{
"meta": {
"total": "1234",
"last_page": "967",
"per_page": "4000",
"current_page": "1",
"next_page_url": "http://localhost/api/<ws>/1?page=2",
"prev_page_url": "http://localhost/api/<ws>/1?page=1",
"from": "1",
"to": "4000"
},
"data": [
{
"id": "1",
"short": "TEST1",
"modified": "2011-03-07 14:17:23"
},
{
"id": "5",
"short": "TEST2",
"modified": "2015-05-26 12:39:45"
}
]
}
And this is the output when the user selects text/plain:
id|short|modified
1|TEST1|2011-03-07 14:17:23
5|TEST2|2015-05-26 12:39:45

YouTube API chromeless player token expiration

Summary
I have a Youtube API chromeless player in my Flex/AS3 application. Sometimes I need the player to play movies from Youtube continuously (non-stop).
Issues
After playing for something like 12 or more hours the API token which was generated by the first player initialization request is getting expired.
http://www.youtube.com/apiplayer?version=3&modestbranding=1&autoplay=0
And the issue here is that the API call loadVideoById which is called again after the token is expired can't start playing video, because the token is not getting renewed automatically.
After playing our playlist (each item is getting played by calling to loadVideoById) for something like 4-5 or more hours the video sometimes starts to play and suddenly getting stuck. While no error event is dispatched.
Known Solutions
So for the first issue the solution is to renew the token each 12 hours by unloading the player and loading it again using the API call:
http://www.youtube.com/apiplayer?version=3&modestbranding=1&autoplay=0
For the second issue there are no ideas. Need your help.
The relevant source code
private function CreateYoutubePlayerSWF():void {
...
theYoutubeLoader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);
theYoutubeLoader.load(new URLRequest("http://www.youtube.com/apiplayer?version=3&modestbranding=1&autoplay=0"));
...
}
private function PlayPendingVideo():void {
...
theYoutubePlayer.loadVideoById(videoWaitingToPlayInfo.videoWaitingToPlay);
...
}
Requests & Responses
When the player stops playing this is what I get in the fiddler:
"request": {
"method": "GET",
"url": "http://www.youtube.com/get_video?cpn=Lj5HaLu7MzS5kG-T&fmt=35&splay=1&t=vjVQa1PpcFNwMVrYUDFOTTG-7co1uJFo3oyrB-qoP_k=&video_id=j-vJJSqw5Q4&eurl=http%3A%2F%2Fwatchitoo.com%2Fiframe.php%3Fid%3Dwwa-154%26scale%3Dfalse%26layout%3D14&asv=3&el=embedded&ps=chromeless&ptk=youtube_none&noflv=1",
...
"queryString": [
{
"name": "cpn",
"value": "Lj5HaLu7MzS5kG-T"
},
{
"name": "fmt",
"value": "35"
},
{
"name": "splay",
"value": "1"
},
{
"name": "t",
"value": "vjVQa1PpcFNwMVrYUDFOTTG-7co1uJFo3oyrB-qoP_k"
},
{
"name": "video_id",
"value": "j-vJJSqw5Q4"
},
{
"name": "eurl",
"value": "http%3A%2F%2Fwatchitoo.com%2Fiframe.php%3Fid%3Dwwa-154%26scale%3Dfalse%26layout%3D14"
},
{
"name": "asv",
"value": "3"
},
{
"name": "el",
"value": "embedded"
},
{
"name": "ps",
"value": "chromeless"
},
{
"name": "ptk",
"value": "youtube_none"
},
{
"name": "noflv",
"value": "1"
}
"response": {
"status": 410,
"statusText": "Gone",
"httpVersion": "HTTP/1.1",
"headers": [
{
"name": "Date",
"value": "Wed, 26 Jun 2013 16:32:37 GMT"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "Server",
"value": "Apache"
},
{
"name": "X-Frame-Options",
"value": "SAMEORIGIN"
},
{
"name": "Content-Type",
"value": "video/x-flv"
},
{
"name": "Cache-Control",
"value": "no-cache"
},
{
"name": "Content-Length",
"value": "0"
},
{
"name": "Expires",
"value": "Tue, 27 Apr 1971 19:44:06 EST"
}

Resources