Dead letters using Akka HTTP client and Akka Streams - scala

I'm trying to use Akka HTTP and Akka Streams to run a scraper. I'm starting with a bunch of index pages, parsing links out of them, then fetching each link and parsing that page, to return a bunch of individual links. So, like this:
fetch-top-level-page -> list-of-links-to-child-pages -> fetch-child-page -> list-of-links-in-child-page
My problem is that I'm not even able to fetch a single page. Each top-level URL I try to fetch results in a dead letter, and nothing ever makes it further down the pipeline.
In this sample code, all I'm trying to do is send HttpRequest into the pool to be transformed into an HttpResponse, and prove that it worked by printing stuff to the screen.
implicit val system = ActorSystem("scraper")
implicit val ec = system.dispatcher
implicit val settings = system.settings
implicit val materializer = ActorMaterializer()
val requests = List(HttpRequest(...), HttpRequest(...))
val poolClientFlow = Http().superPool[Promise[HttpResponse]](settings = ConnectionPoolSettings(system).withMaxConnections(10))
Source(requests)
.map (req => { println("-", req); req}) // this part runs fine
.via(poolClientFlow)
.map(resp => {println("|", resp); resp}) // this never runs
.toMat(Sink.foreach { p => println(p) })(Keep.both)
.run()
Here's what I get:
(-,(HttpRequest(...),Future(<not completed>)))
(-,(HttpRequest(...),Future(<not completed>)))
[INFO] [03/07/2018 15:10:03.681] [scraper-akka.actor.default-dispatcher-5] [akka://scraper/user/pool-master] Message [akka.http.impl.engine.client.PoolMasterActor$SendRequest] without sender to Actor[akka://scraper/user/pool-master#1333123700] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[INFO] [03/07/2018 15:10:03.683] [scraper-akka.actor.default-dispatcher-5] [akka://scraper/user/pool-master] Message [akka.http.impl.engine.client.PoolMasterActor$SendRequest] without sender to Actor[akka://scraper/user/pool-master#1333123700] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
I'm new to Akka and obviously making some basic error, since this seems to be the exact use case that Akka, Akka Streams, and Akka HTTP were built for.
Any ideas?

Unable to reproduce with Akka HTTP 10.1.0-RC2 and Akka Streams 2.5.11. The following works:
val requests = List((HttpRequest(uri = "http://akka.io"), Promise[HttpResponse]()),
(HttpRequest(uri = "http://www.yahoo.com"), Promise[HttpResponse]()))
val poolClientFlow =
Http().superPool[Promise[HttpResponse]](settings = ConnectionPoolSettings(system).withMaxConnections(10)
Source(requests)
.map { req => println("-", req); req }
.via(poolClientFlow)
.map { resp => println("|", resp); resp }
.toMat(Sink.foreach(println))(Keep.both)
.run()
// The following is printed:
// (-,(HttpRequest(HttpMethod(GET),http://akka.io,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1)),Future()))
// (-,(HttpRequest(HttpMethod(GET),http://www.yahoo.com,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1)),Future()))
// (|,(Success(HttpResponse(301 Moved Permanently,List(Date: Thu, 08 Mar 2018 14:33:46 GMT, Connection: keep-alive, Cache-Control: max-age=3600, Expires: Thu, 08 Mar 2018 15:33:46 GMT, Location: https://akka.io/, Server: cloudflare, CF-RAY: 3f8604d386979cf6-AMS),HttpEntity.Chunked(application/octet-stream),HttpProtocol(HTTP/1.1))),Future()))
// (Success(HttpResponse(301 Moved Permanently,List(Date: Thu, 08 Mar 2018 14:33:46 GMT, Connection: keep-alive, Cache-Control: max-age=3600, Expires: Thu, 08 Mar 2018 15:33:46 GMT, Location: https://akka.io/, Server: cloudflare, CF-RAY: 3f8604d386979cf6-AMS),HttpEntity.Chunked(application/octet-stream),HttpProtocol(HTTP/1.1))),Future())
// (|,(Success(HttpResponse(301 Moved Permanently,List(Date: Thu, 08 Mar 2018 14:33:46 GMT, Connection: keep-alive, Via: http/1.1 media-router-fp6.prod.media.ir2.yahoo.com (ApacheTrafficServer [c s f ]), Server: ATS, Cache-Control: no-store, no-cache, Content-Language: en, X-Frame-Options: SAMEORIGIN, Location: https://www.yahoo.com/),HttpEntity.Strict(text/html,ByteString(114, 101, 100, 105, 114, 101, 99, 116)),HttpProtocol(HTTP/1.1))),Future()))
// (Success(HttpResponse(301 Moved Permanently,List(Date: Thu, 08 Mar 2018 14:33:46 GMT, Connection: keep-alive, Via: http/1.1 media-router-fp6.prod.media.ir2.yahoo.com (ApacheTrafficServer [c s f ]), Server: ATS, Cache-Control: no-store, no-cache, Content-Language: en, X-Frame-Options: SAMEORIGIN, Location: https://www.yahoo.com/),HttpEntity.Strict(text/html,ByteString(114, 101, 100, 105, 114, 101, 99, 116)),HttpProtocol(HTTP/1.1))),Future())
// [WARN] [03/08/2018 14:46:31.003] [scraper-akka.actor.default-dispatcher-4] [scraper/Pool(shared->http://akka.io:80)] [0 (WaitingForResponseEntitySubscription)] Response entity was not subscribed after 1 second. Make sure to read the response entity body or call `discardBytes()` on it. GET / Empty -> 301 Moved Permanently Chunked
Probably a better approach is something like this (note the call to discardEntityBytes()):
Source(requests)
.map { req => println("-", req); req }
.via(poolClientFlow)
.map { resp => println("|", resp); resp }
.toMat(Sink.foreach({
case ((util.Success(resp), p)) =>
resp.discardEntityBytes()
p.success(resp)
case ((util.Failure(e), p)) => p.failure(e)
}))(Keep.both)
.run()
// The following is printed:
// (-,(HttpRequest(HttpMethod(GET),http://akka.io,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1)),Future()))
// (-,(HttpRequest(HttpMethod(GET),http://www.yahoo.com,List(),HttpEntity.Strict(none/none,ByteString()),HttpProtocol(HTTP/1.1)),Future()))
// (|,(Success(HttpResponse(301 Moved Permanently,List(Date: Thu, 08 Mar 2018 14:38:32 GMT, Connection: keep-alive, Via: http/1.1 media-router-fp21.prod.media.ir2.yahoo.com (ApacheTrafficServer [c s f ]), Server: ATS, Cache-Control: no-store, no-cache, Content-Language: en, X-Frame-Options: SAMEORIGIN, Location: https://www.yahoo.com/),HttpEntity.Strict(text/html,ByteString(114, 101, 100, 105, 114, 101, 99, 116)),HttpProtocol(HTTP/1.1))),Future()))
// (|,(Success(HttpResponse(301 Moved Permanently,List(Date: Thu, 08 Mar 2018 14:38:32 GMT, Connection: keep-alive, Cache-Control: max-age=3600, Expires: Thu, 08 Mar 2018 15:38:32 GMT, Location: https://akka.io/, Server: cloudflare, CF-RAY: 3f860bca84a32ba6-AMS),HttpEntity.Chunked(application/octet-stream),HttpProtocol(HTTP/1.1))),Future()))

Related

Flutter http get() 403 status code problem

I'm practicing on making some get requests using flutter's http plugin, and when I make a request to [https://www.pexels.com], I get 403 Status Code.
But when I open the target Url with chrome browser It works fine.
Is it something that can be solved using some headers?? If so, How??
Here is my Code:
void getData() async {
Response response = await get(Uri.encodeFull('https://www.pexels.com'));
if (response.statusCode == 200) {
print('connected');
} else {
print('connection error');
}
print(response.statusCode);
print(response.headers);
}
Status Code: I/flutter (28151): 403
response.headers:I/flutter (28151): {connection: close, cache-control: private, max-age=0, no-store, no-cache, must-revalidate, post-check=0, pre-check=0, set-cookie: __cfduid=d22dd51645467ea70238107d1e15f9a2d1607634921; expires=Sat, 09-Jan-21 21:15:21 GMT; path=/; domain=.pexels.com; HttpOnly; SameSite=Lax,__cf_bm=d4c93b1eb4dd2f86fe359617a86f82ba9c90e0d0-1607634921-1800-Ae/p5j0v/53c10n4CBxIlK2mTPDdau3ZpYqbReQLNVrOEyX6pjZBZ6RU0TWU8mxnL4MfxpFfNiLeHkgAlCjwzJY=; path=/; expires=Thu, 10-Dec-20 21:45:21 GMT; domain=.pexels.com; HttpOnly; Secure; SameSite=None, transfer-encoding: chunked, date: Thu, 10 Dec 2020 21:15:21 GMT, cf-request-id: 06f01b415e0000af9c2d97d000000001, content-encoding: gzip, vary: Accept-Encoding, content-type: text/html; charset=UTF-8, expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct", server: cloudflare, cf-ray: 5ff9fb156d4baf9c-BGW, cf-chl-bypass: 1, x-frame-options: SAMEORIGIN, expires: Thu, 01 Jan 1970 00:00:01 GMT}
Request headers from Google Chrome Browser:

bluemix app push hangs after upload

command bx app push hangs after message "Done uploading" is shown.
I activated CF_TRACE env var, and the following request is made several times before failing with:
Error processing app files: Error uploading application.
Server error, status code: 502, error code: 0, message:
REQUEST: [2018-08-01T11:47:21-03:00]
GET /v2/jobs/5dc92acb-8573-422b-8a60-2e6b558dc26e HTTP/1.1
Host: api.ng.bluemix.net
Accept: application/json
Authorization: [PRIVATE DATA HIDDEN]
Connection: close
Content-Type: application/json
User-Agent: go-cli 6.32.0+0191c33d9.2017-09-26 / linux
RESPONSE: [2018-08-01T11:47:21-03:00]
HTTP/1.1 200 OK
Connection: close
Content-Length: 270
Cache-Control: max-age=0, no-cache, no-store
Content-Type: application/json;charset=utf-8
Date: Wed, 01 Aug 2018 14:47:21 GMT
Expires: Wed, 01 Aug 2018 14:47:21 GMT
Pragma: no-cache
Server: nginx
X-Backside-Transport: OK OK
X-Content-Type-Options: nosniff
X-Global-Transaction-Id: 2574851421
{
"metadata": {
"guid": "5dc92acb-8573-422b-8a60-2e6b558dc26e",
"created_at": "2018-08-01T14:36:25Z",
"url": "/v2/jobs/5dc92acb-8573-422b-8a60-2e6b558dc26e"
},
"entity": {
"guid": "5dc92acb-8573-422b-8a60-2e6b558dc26e",
"status": "queued"
}
}
I noticed the status queued on the entity object. What that means ? What can I do?
EDIT:
There was a problem with the region my app was on. It was reported at: https://console.bluemix.net/status

Paypal Adaptive

I am using paypal adaptive. Its working if i use sandbox account. But paypal is not working if i use live account.
Paypal throw error as below.
Array
(Server: Apache
X-EBAY-SOA-REQUEST-ID: 15674515-0c90-a115-45a6-9f3dfffd5017!AdaptivePayments!10.17.84.90![]
X-PAYPAL-SERVICE-VERSION: 1.0.0
X-PAYPAL-SERVICE-NAME: {http://svcs.paypal.com/types/ap}AdaptivePayments
X-PAYPAL-API-RC: 580029
X-EBAY-SOA-RESPONSE-DATA-FORMAT: NV
X-PAYPAL-OPERATION-NAME: Preapproval
CACHE-CONTROL: no-cache
X-PAYPAL-ERROR-RESPONSE: TRUE
X-EBAY-SOA-MESSAGE-PROTOCOL: NONE
Vary: Accept-Encoding
HTTP_X_PP_AZ_LOCATOR: dcg12.slc
Paypal-Debug-Id: 29d4c08e5ad57
Set-Cookie: X-PP-SILOVER] => name=LIVE6.APIT.1&silo_version=880&app=adaptivepaymentspartaweb_api3t&TIME=3709250391&HTTP_X_PP_AZ_LOCATOR=dcg12.slc;
Expires=Wed, 10 Aug 2016 12:28:21 GMT; domain=.paypal.com; path=/;
Secure; HttpOnly
Set-Cookie: X-PP-SILOVER=; Expires=Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: max-age=0, no-cache, no-store, must-revalidate
Pragma: no-cache
Connection: close
Content-Type: text/plain;charset=UTF-8
In this error, its showing like missing some subscription parameter. But I could not find what parameter is missing. Since its working correctly in sandbox.
responseEnvelope.timestamp=2016-08-10T04:58:21.486-07:00
[responseEnvelope.ack] => Failure
[responseEnvelope.correlationId] => 29d4c08e5ad57
[responseEnvelope.build] => 24003818
[error(0).errorId] => 580029
[error(0).domain] => PLATFORM
[error(0).subdomain] => Application
[error(0).severity] => Error
[error(0).category] => Application
[error(0).message] => One of the required parameters for subscription is missing
[error(0).parameter(0)] => Subscription
)
Kindly check and give me the solution.

NSURLRequestCachePolicy.UserProtocolCachPolicy requirements

I am using the following code for caching, the response received form the server has the following headers. Is there any header that needs to be set from the request side, for the caching to work for 10 seconds of age.
Connection Received Resopnse Headers= [Date: Sat, 12 Sep 2015 22:51:16
GMT, Transfer-Encoding: Identity, Server: Apache-Coyote/1.1,
Content-Type: application/json;charset=UTF-8, Expires: Sat, 12 Sep
2015 22:51:26 GMT, Cache-Control: max-age=10, must-revalidate]
The mighty code which is not caching.
import UIKit
class HTTPJSONDonwload: NSObject , NSURLConnectionDataDelegate , NSURLConnectionDelegate {
static let httpjsonDownloader:HTTPJSONDonwload = HTTPJSONDonwload()
func startDownload(){
let serverRequest = getServerURL()
NSURLConnection(request: serverRequest, delegate: self, startImmediately: true)
}
func getServerURL() -> NSMutableURLRequest{
let request:NSMutableURLRequest = NSMutableURLRequest(URL:NSURL(string:"http://citiesfav-jcitiesj.rhcloud.com/Cache/getAllCities")! )
request.cachePolicy = NSURLRequestCachePolicy.UseProtocolCachePolicy
request.HTTPMethod = "POST"
return request
}
func connection(connection: NSURLConnection, didReceiveData data: NSData) {
print("Connection Data= \(NSString(data: data, encoding: NSUTF8StringEncoding))")
}
func connection(connection: NSURLConnection, didReceiveResponse response: NSURLResponse) {
print("Connection Received Resopnse Headers= \((response as! NSHTTPURLResponse).allHeaderFields)")
}
func connection(connection: NSURLConnection, willCacheResponse cachedResponse: NSCachedURLResponse) -> NSCachedURLResponse? {
print("Connection will cache Response")
return cachedResponse
}
}
After removing must-revalidate from the header it was still fetching the request.
Connection Received Resopnse Headers= [Cache-Control: max-age=10,
Transfer-Encoding: Identity, Date: Sun, 13 Sep 2015 18:35:43 GMT,
Content-Type: application/json;charset=UTF-8, Server:
Apache-Coyote/1.1, Expires: Sun, 13 Sep 2015 18:35:53 GMT]
Later findings show the POST request does get cached, but does not work like GET, where max-age is considered.
func startDownload(){
let serverRequest = getServerURL()
let cache = NSURLCache.sharedURLCache()
let response = cache.cachedResponseForRequest(serverRequest)
if response != nil {
serverRequest.cachePolicy = NSURLRequestCachePolicy.ReturnCacheDataDontLoad
}
NSURLConnection(request: serverRequest, delegate: self, startImmediately: true)
}
tl;dr
You need to use GET instead of POST.
Lengthy Explanation
The issue is that you're request is a POST.
func getServerURL() -> NSMutableURLRequest{
...
request.HTTPMethod = "POST"
...
}
In general, POST requests are used to create (or sometimes also to update) a resource on the server. Reusing the cached response for a creation or update request doesn't make much sense because you have to send the request to the server anyway (otherwise nothing is going to be created or updated). It seems that iOS automatically circumvents the cache on POST requests.
In your particular case, however, you don't really need the POST because you're merely reading data from the server. That means you should use a GET request instead.
func getServerURL() -> NSMutableURLRequest{
...
request.HTTPMethod = "GET"
...
}
I verified that the iOS system actually reuses the cache with the following snippet.
let d = HTTPJSONDonwload()
// Initial request. Can not reuse cache.
d.startDownload()
// Subsequent request after 5 seconds. Should be able to reuse the cache.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(5 * NSEC_PER_SEC)), dispatch_get_main_queue()) {
d.startDownload()
}
// Subsequent request after 11 seconds. Cannot reuse the cache because
// the expiration timeout is 10 seconds.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(11 * NSEC_PER_SEC)), dispatch_get_main_queue()) {
d.startDownload()
}
When I run this in the simulator and monitor the network calls with Charles Proxy, I indeed only see two events:
The first call is the initial request
and the second call is the third request which was issued after a delay of 11 seconds.
Note that the second request, which was issued after 5 seconds, does not appear which means that the response was retrieved from the cache. The delegate methods of NSURLConnection will, however, still be called just as if the response came from the network. With the logging output in your code you'll, therefore, see all three requests on the console.
Connection Received Resopnse Headers= [Server: Apache-Coyote/1.1, Content-Type: application/json;charset=UTF-8, Keep-Alive: timeout=15, max=100, Proxy-Connection: Keep-alive, Date: Mon, 14 Sep 2015 06:28:05 GMT, Content-Encoding: gzip, Content-Length: 36, Cache-Control: max-age=10, Vary: Accept-Encoding]
Connection Data= Optional({"1":"New York"})
Connection will cache Response
Connection Received Resopnse Headers= [Server: Apache-Coyote/1.1, Content-Type: application/json;charset=UTF-8, Keep-Alive: timeout=15, max=100, Proxy-Connection: Keep-alive, Date: Mon, 14 Sep 2015 06:28:05 GMT, Content-Encoding: gzip, Content-Length: 36, Cache-Control: max-age=10, Vary: Accept-Encoding]
Connection Data= Optional({"1":"New York"})
Connection Received Resopnse Headers= [Server: Apache-Coyote/1.1, Content-Type: application/json;charset=UTF-8, Keep-Alive: timeout=15, max=99, Proxy-Connection: Keep-alive, Date: Mon, 14 Sep 2015 06:28:16 GMT, Content-Encoding: gzip, Content-Length: 36, Cache-Control: max-age=10, Vary: Accept-Encoding]
Connection Data= Optional({"1":"New York"})
Connection will cache Response
Note that there is no Connection will cache Response after the second request because the response was retrieved from the cache and there is no point in caching it again.

WebAuthenticationBroker returns a UserCancel WebAuthenticationResult on a successful Facebook authentication

Using the WebAuthenticationBroker for Single Sign On in a Windows Phone Store app (WinRT / WP8.1), i use the following code to engage the Facebook login process :
string redirectUri2 = Windows.Security.Authentication.Web.WebAuthenticationBroker.GetCurrentApplicationCallbackUri().AbsoluteUri;
FacebookClient fb = new FacebookClient();
Uri loginUri = fb.GetLoginUrl(new {
client_id = FacebookAppID,
redirect_uri = redirectUri2,
response_type = "token",
scope = FacebookScope,
display = "popup"
});
WebAuthenticationBroker.AuthenticateAndContinue(loginUri, new Uri(redirectUri));
In the ContinueWebAuthentication(WebAuthenticationBrokerContinuationEventArgs args) callback, the WebAuthenticationResult.ResponseStatus I get is UserCancel, even on a successful sign in.
Trace of the last response from Facebook :
HTTP/1.1 200 OK
X-Content-Type-Options: nosniff
Cache-Control: private, no-cache, no-store, must-revalidate
Expires: Sat, 01 Jan 2000 00:00:00 GMT
X-XSS-Protection: 0
X-Frame-Options: DENY
Facebook-API-Version: v2.0
Pragma: no-cache
Content-Type: text/html; charset=utf-8
P3P: CP="Facebook does not have a P3P policy. Learn why here: http://fb.me/p3p"
Set-Cookie: wd=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; Max-Age=-1413367561; path=/; domain=.facebook.com; httponly
X-FB-Debug: {edited out}
Date: Wed, 15 Oct 2014 10:06:02 GMT
Connection: keep-alive
Content-Length: 411
<script type="text/javascript">window.location.href="ms-app:\/\/s-1-15-2-908865707-3825634006-813379085-3082126904-2549935584-1522363559-xxxxxxxxxx\/#access_token={edited out}&expires_in=5113018";</script>
What is going on with the WebAuthenticationBroker ?
Additional information on the issue : the date/time/timezone on the phone are correct.