Source.fromURL not throwing an exception for an invalid URL - scala

I am attempting to retrieve the sizes various websites whose URL will be passed to my script, but I'm not getting an exception when I pass an invalid URL, instead simply getting a very small page. I'm using Source.fromURL, and I get the following results:
thisIsClearlyABoggusURLThatCantPossiblyLeadAnyway 1052
www.bbc.co.uk 113871
The first one, as it says, shouldn't have anything in it, but it does. My script is as follows:
def main( args:Array[String] ){
val tasks = for(arg <- args) yield future {
try {
println(arg + " " + Source.fromURL( attachPrefix(arg) ).length)
} catch {
case e : java.net.UnknownHostException => println(arg + " *")
}
}
awaitAll(20000L, tasks: _*)
}
def attachPrefix( url:String ) = url.slice(0, 4) match {
case "http" => url
case "www." => "http://" + url
case _ => "http://www." + url
}
Each argument is being passed into the function attachPrefix to ensure it has the necessary prefix before being used. This problem has only come about since I started passing the url in as a parameter instead of mapping it onto the arg, which is what I was doing earlier with
args map attachPrefix
What's the difference between the two, and why is my current one giving such behaviour?

You can use the Source.fromURL(URI) signature. Creating a URI will effectively validate the URL as documented here. However, in this case, the URL http://www.thisIsClearlyABoggusURLThatCantPossiblyLead‌​Anyway is valid as far as the URI is concerned. On the other hand, the UrlValidator suggested by om-nom-nom considers it invalid, because the top level domain segment has more than 4 characters which is already out of date. I don't know of any entirely Scala validation libraries or why this would be a requirement, but you could try using a regular expression for validation. For example, this will catch your example, because the top level domain exceeds 6 letters:
val re = """^(https?://)?(([\w!~*'().&=+$%-]+: )?[\w!~*'().&=+$%-]+#)?(([0-9]{1,3}\.){3}[0-9]{1,3}|([\w!~*'()-]+\.)*([\w^-][\w-]{0,61})?[\w]\.[a-z]{2,6})(:[0-9]{1,4})?((/*)|(/+[\w!~*'().;?:#&=+$,%#-]+)+/*)$""".r
re.pattern.matcher("http://google.com").matches // true
re.pattern.matcher("http://www.thisIsClearlyABoggusURLThatCantPossiblyLeadAnyway").matches // false

Related

MockWS with dynamic url

I want to parse dynamic url into case url of MockWS like this
val ws = MockWS {
case ("POST", mockServer.getUrl + "/predict-nationality") =>
Action {
Ok(Json.obj(
"ARE" -> 0.0015790796301290394,
"ARG" -> 0.0015750796301290393,
"AUS" -> 0.027795471251010895
))
}
}
However, I got error saying that stable identifier required. Only String without any modification is accepted there. But I really need to use mockServer.getUrl + "/predict-nationality" as url in this case. I also can't define it as val because of the MockWS {} scope.
How can I deal with this? any other ways to use MockWS with dynamic url? or if this can be solved with play framework or anything, I'm happy to do so too. It just that I want to mock ws request and response (written in scala)

Passing Values to Multiple Files in Gatling

I am designing a software test using Gatling for my company's web based application. After many iterations, I have come to a design that I believe will make it easy for testers at my company to easily change parameters as needed, but it is quite complex. The main file which contains the scenario, setup, and simulation calls is all contained in one file that then connects to a different file via methods for each of the other functions we are testing.
My problem is when logging into our API with a request, using one of the function files, it sends a response that includes a session token. It is important that I am able to pass the value of that token back to the main file, so that I can use it as a parameter in other function methods. I am having a problem passing the actual value of this token to the main file.
Here is the sample code for the method:
object BasicLogin {
def login ( hostparam:String, controlparam:String, usernameparam:String, passwordparam:String) : (ChainBuilder, String) = {
val user = usernameparam
val password = passwordparam
val host = hostparam
val controlid = controlparam
val logintype = "user"
val jsonrequest = """{"logintype":"""" + logintype + """",
"controlid":"""" + controlid + """",
"user":"""" + user + """",
"password":"""" + password + """",
"host":"""" + host + """"
}"""
val loginChain = exec(http("Login")
.post("sample.url.com")
.body(StringBody(jsonrequest))
.asJSON
.check(jsonPath("$..result").is("true"))
.check(jsonPath("$..session")
.find
.saveAs("currentSession")))
val accessToken: String = exec(session => {
val sessionid = session("currentSession").as[String]
println("token: " + sessionid)
session })
.toString
return (loginChain, accessToken)
}
}
Here is my code calling the method for login:
val host = "IPaddress"
val controlid = "IDcontroller"
val username = "JDoe"
val password = "TerriblePassword"
val result = BasicLogin.login(host, controlid, username, password)
val basiclogin:ChainBuilder = result._1
val currentSession:String = result._2
println("Session: " + currentSession)
println("Login: " + basiclogin)
And here is what I get in response:
Session: ChainBuilder(List(io.gatling.core.action.builder.SessionHookBuilder#6f70f32f))
Login: ChainBuilder(List(io.gatling.http.action.sync.HttpRequestActionBuilder#3dd69f5a))
I have searched in many different places, and have not found much in terms of similar issues. I know Gatling is run in multi-cast, and not sure if this is even possible. I have utilized the save as method, and tried calling the value using Gatling DSL, but no luck. I originally was trying to use just values without methods, but that failed miserably. I have also tried the set method and mapping, but neither of these worked either.
So now I am trying to get a value from the response, and return it back to the main file using the method. It runs, but it returns the chainbuilder.
Any advice?
Rob

Require header across routes without giving up 404

I’d like to require a header in my akka-http routes and can do so via
val route = headerValueByName("foo") { foo =>
pathPrefix("path") {
get {
...
} ~ ...
}
}
However, now any requests that don't match a path will get rejected with 400 (missing header) and not 404.
Is there a neat way to get around this without repeatedly moving headerValueByName after the path matchers?
That is, is there a way to only apply an outer directive ( headerValueByName) and its rejections if the inner path and method matchers are successful?
You don't specify what you want to do in case the header is not specified, so I'll asume you want to return 400 (Bad request).
A possible solution is to use the optionalHeaderValueByName directive and then complete the request with the specified error, for example:
val route = optionalHeaderValueByName("foo") { optionalHeader =>
optionalHeader map { header =>
// rest of your routes
} getOrElse complete(StatusCodes.BadRequest)
}

Having trouble with getting basic auth from play framework

I have request.headers.get("Authorization") but I still don't receive anything other than none
def test = Action { request =>
Ok("Here " + request.headers.get("Authorization") + " there")
}
I would like to be able to parse the basic auth, but since I currently don't even receive it its kind of a mute point.
Try doing request.headers.getHeaders() (which produces a map) and loop through the map, printing all Key,Value pairs to see if it is truly missing.

How to retain accented characters when doing a 301 redirect

We have a situation where we're giving the super-users of our marketing website the ability to directly search for a product with its slug. The link they hit redirects to the actual page for that product. This works fine for english locales. However in case of non-english locales where the slugs have been localised, the redirect causes all accented characters to be replaced by '?'.
For example, http://domain.xx.be/fr/category/catégorie/product_name redirects to http://domain.xx.be/fr/category/cat?gorie/product_name which gives a 404.
Is there a way to retain the accented characters in the redirect url when using the play mvc Results api. P.S. We get the absolute redirect url as part of a json response from a different API.
EDIT: adding some code for clarity
def getProductPage(slug: String, locale: String) = AsyncAction {
flow {
val response = gateway.getPathBySlug(slug, locale).!
val url = (response.json \ "url").as[String]
MovedPermanently(url)
} recover {
case ex => throw ex
}
}
You need to play a little with encodings, in Java it works for me:
public static Result bad() {
return temporaryRedirect("http://fr.wikipedia.org/wiki/Catégorie");
}
public static Result good() {
return temporaryRedirect(encodeUrl("http://fr.wikipedia.org/wiki/Catégorie"));
}
public static String encodeUrl(String url) {
try {
url = URLEncoder.encode(url.trim(), "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return url
.replace("%20", "_")
.replace("%21", "!")
.replace("%22", "'") // single quotas
.replace("%24", "$")
.replace("%27", "'")
.replace("%28", "(")
.replace("%29", ")")
.replace("%2C", ",")
.replace("%2F", "/")
.replace("%3A", ":")
.replace("%3F", "_") // question sign
.replace("%E2%80%9E", "'") // lower quotas
.replace("%E2%80%9D", "'") // upper quotas
.replace("+", "_")
.replace("%25", "percent");
}
It encodes the special accented chars to URL entities but brings common URL characters back to live after all
java.net.URI#toASCIIString to the rescue.
From the documentation
Returns the content of this URI as a string.
If this URI was created by invoking one of the constructors in this
class then a string equivalent to the original input string, or to the
string computed from the originally-given components, as appropriate,
is returned. Otherwise this URI was created by normalization,
resolution, or relativization, and so a string is constructed from
this URI's components according to the rules specified in RFC 2396,
section 5.2, step 7.
So your code becomes
def getProductPage(slug: String, locale: String) = AsyncAction {
flow {
val response = gateway.getPathBySlug(slug, locale).!
val url = (response.json \ "url").as[String]
val encodedDestination = new URI(url).toASCIIString
MovedPermanently(encodedDestination)
} recover {
case ex => throw ex
}
}