I've written the following Haskell code to download the CSV file (daliy prices) available on yahoo finance web site . In the last part of the code, there's a case statement. I would like to know when actually "rcode" contains the "Left" value. I've mentioned three cases, but all of them refer to "Right" values. I may be wrong. I'm referring to the HTTP response codes available on the following web site.
downloadCSVFile ::String-> IO (Bool,String)
downloadCSVFile company_code=do
let a="http://ichart.finance.yahoo.com/table.csv?s=" ++ company_code
let b=simpleHTTP $ getRequest a
src <- ( b >>= getResponseBody)
rcode <- fmap rspCode <$> b
case rcode of
Right (2,_,_) -> return (True,src)
Right (4,_,_) -> return (False,"Invalid URL..")
Right (5,_,_) -> return (False, "Server Error")
https://support.google.com/webmasters/answer/40132?hl=en
The Result a type that gets threaded around is an alias for Either ConnError a.
You'll get a Left value if the HTTP client library had some actual problem when connecting to the server. If it successfully connected to the server and received a HTTP response code from the server, that will always be a Right value.
See the Network.HTTP documentation for more details.
To handle the error cases, do something like this:
case rcode of
Left err -> return (False, "Connection error: " ++ show err)
Right (2,_,_) -> return (True,src)
Right (4,_,_) -> return (False,"Invalid URL..")
Right (5,_,_) -> return (False, "Server Error")
Right code -> return (False, "Unexpected code: " ++ show code)
I also added a "catch-all" case in case you get an unexpected response from the server.
Related
I am struggling with how to set different cache response headers based on whether the result is an Ok or an Error. My code is something like the following (but with other types in the result):
let resultToJson (result:Result<'a,string>) : HttpHandler =
match result with
| Ok o -> Successful.ok (json o)
| Error s -> ServerErrors.internalError (text s)
I can add the headers by doing something like the following:
let resultToJson (result:Result<'a,string>) : HttpHandler =
fun (next : HttpFunc) (ctx : HttpContext) ->
let response =
let headers = ctx.Response.Headers
match result with
| Ok o ->
headers.Add("Cache-Control", new StringValues("public, max-age=10, stale-while-revalidate=2"))
headers.Add("Vary", new StringValues("Origin"))
Successful.ok (json o)
| Error s ->
headers.Add("Cache-Control", new StringValues("no-cache"))
ServerErrors.internalError (text s)
response next ctx
But this does not feel right. I would like to use the standard HttpHandlers from the ResponseCaching module to set the right cache headers:
publicResponseCaching 10 (Some "Origin") // For Ok: Add 10 sec public cache, Vary by Origin
noResponseCaching // For Error: no caching
How do I achieve this?
The response cache handler is supposed to be piped into an normal pipeline. Your choice between Ok and Error is a choose function, so you can use a choose that takes a list of handlers that can be attempted. To reject a path, just return a task { return None }, to move forward, it's next ctx.
If you want to keep all the logic in one controller, like you have now, just keep your match and pipe your json/text response into one of the caching handlers.
let fn = json o >=> publicResponseCaching 30 None) in fn next ctx
if it's nested inside a hander, instead of in a pipeline, you have to apply the next & ctx
I found the solution to my problem.
Yes, I can chain the HttpHandlers like Gerard and Honza Brestan mentioned, using the fish operator (>=>). The reason I could not make that work in the first place was that I also had created a fish operator for the Result type in an opened module. Basically I had created proper fish soup
As soon as I refactored my code so that the module containing the Result fish operator was not open in this scope, everything worked fine as expected.
Another point to remember is that response caching needs to be called before the finalizing HttpHandler, otherwise it will not be called:
// Simplified code
let resultToJson =
function
| Ok o -> publicResponseCaching 10 (Some "Origin") >=> Successful.ok(json o)
| Error e -> noResponseCaching >=> ServerErrors.internalError(text e)
It's my first attempt to write anything in Erlang, so maybe the question is silly.
I'm writing a quite simple HTTP server using cowboy
db_name() -> "DB_test".
timestamp() ->
calendar:datetime_to_gregorian_seconds(calendar:universal_time()).
sha(Str) ->
<<X:256/big-unsigned-integer>> = crypto:hash(sha256, Str),
lists:flatten(io_lib:format("~64.16.0b", [X])).
handle_post(Req0, State) ->
Link = binary_to_list(cowboy_req:header(<<"link">>, Req0)),
dets:open_file(db_name(), []),
dets:insert(db_name(), {hashed_url(Link), Link, timestamp()}),
Req = cowboy_req:reply(200,
#{<<"content-type">> => <<"text/plain">>},
sha(Link),
Req0),
{ok, Req, State}.
The idea is that a POST HTTP request contains a 'link' header with some link. After recieving such request my server should store it's hash in dets table along with the link and its timestamp. The problem is that the "DB_test" file isn't created. Why?
Based on your example code, it's hard to say exactly why, since you're ignoring the return values from both dets:open_file/2 and dets:insert/2.
Both of them return different values for the success and failure cases; but do not throw exceptions.
See the official documentation for more details: http://erlang.org/doc/man/dets.html
The simplest solution to this is to crash the cowboy connection handling process in non-success cases. You can do that by doing something like the following:
{ok, Ref} = dets:open_file(db_name(), []),
ok = dets:insert(Ref, {hashed_url(Link), Link, timestamp()}),
This will crash with a badmatch exception in the failure cases, when the value returned cannot be pattern matched to the left-hand side of the statement, subsequently causing cowboy to return HTTP 500 to the client.
You'll then see details on what the actual error was in the stacktrace logged
A second solution would be to explicitly handle the failure cases, you can use the 'case' keyword for that.
An example would be something like:
case dets:open_file(db_name(), []) of
{ok, Ref} ->
do_success_things();
{error, Reason}=E ->
io:format("Unable to open database file: ~p~n", [E]),
do_failure_things();
end
For further reading, I'd highly recommend the Syntax in functions and Errors and exceptions chapters of Learn you some Erlang for great good: http://learnyousomeerlang.com/
I have recently started learning erlang, but have encountered an error that just leaves me puzzled.
The error is syntax error before: 'end' on the very last line. I have looked at examples an tried to find the error but I'm totally lost at the moment. Any Ideas?
ChannelToJoin = list:keysearch(ChannelName,1,State#server_st.channels),
case ChannelToJoin of
% Channel exists.
{value, Tuple} ->
if
%User is not a member of the channel
not list:member(UserID, Tuple) ->
%Add the user to the channel
Tuple#channel.users = list:append(Tuple#channel.users, [UserID]);
% If the user is already a member of the channel.
true -> true
end;
%Channel doesn't exist
false ->
%Create new channel and add the user to it.
NewState = State#server_st{channels = list:append(State#server_st.channels, NewChannel = #channel{name = ChannelName, users = [UserID]}
end
The second to last line, NewState = ..., is missing two closing parentheses: )}
Also note that you can't use lists:member inside if, as function calls are not allowed inside guard expressions (which is what if lets you use). Instead, use case:
case lists:member(UserID, Tuple#channel.users) of
false ->
%% Add the user to the channel
...;
true ->
%% Already a member
ok
end
I have the following F# program that retrieves a webpage from the internet:
open System.Net
[<EntryPoint>]
let main argv =
let mutable pageData : byte[] = [| |]
let fullURI = "http://www.badaddress.xyz"
let wc = new WebClient()
try
pageData <- wc.DownloadData(fullURI)
()
with
| :? System.Net.WebException as err -> printfn "Web error: \n%s" err.Message
| exn -> printfn "Unknown exception:\n%s" exn.Message
0 // return an integer exit code
This works fine if the URI is valid and the machine has an internet connection and the web server responds properly etc. In an ideal functional programming world the results of a function would not depend on external variables not passed as arguments (side effects).
What I would like to know is what is the appropriate F# design pattern to deal with operations which might require the function to deal with recoverable external errors. For example if the website is down one might want to wait 5 minutes and try again. Should parameters like how many times to retry and delays between retries be passed explicitly or is it OK to embed these variables in the function?
In F#, when you want to handle recoverable errors you almost universally want to use the option or the Choice<_,_> type. In practice the only difference between them is that Choice allows you to return some information about the error while option does not. In other words, option is best when it doesn't matter how or why something failed (only that it did fail); Choice<_,_> is used when having information about how or why something failed is important. For example, you might want to write the error information to a log; or perhaps you want to handle an error situation differently based on why something failed -- a great use case for this is providing accurate error messages to help users diagnose a problem.
With that in mind, here's how I'd refactor your code to handle failures in a clean, functional style:
open System
open System.Net
/// Retrieves the content at the given URI.
let retrievePage (client : WebClient) (uri : Uri) =
// Preconditions
checkNonNull "uri" uri
if not <| uri.IsAbsoluteUri then
invalidArg "uri" "The URI must be an absolute URI."
try
// If the data is retrieved successfully, return it.
client.DownloadData uri
|> Choice1Of2
with
| :? System.Net.WebException as webExn ->
// Return the URI and WebException so they can be used to diagnose the problem.
Choice2Of2 (uri, webExn)
| _ ->
// Reraise any other exceptions -- we don't want to handle them here.
reraise ()
/// Retrieves the content at the given URI.
/// If a WebException is raised when retrieving the content, the request
/// will be retried up to a specified number of times.
let rec retrievePageRetry (retryWaitTime : TimeSpan) remainingRetries (client : WebClient) (uri : Uri) =
// Preconditions
checkNonNull "uri" uri
if not <| uri.IsAbsoluteUri then
invalidArg "uri" "The URI must be an absolute URI."
elif remainingRetries = 0u then
invalidArg "remainingRetries" "The number of retries must be greater than zero (0)."
// Try to retrieve the page.
match retrievePage client uri with
| Choice1Of2 _ as result ->
// Successfully retrieved the page. Return the result.
result
| Choice2Of2 _ as error ->
// Decrement the number of retries.
let retries = remainingRetries - 1u
// If there are no retries left, return the error along with the URI
// for diagnostic purposes; otherwise, wait a bit and try again.
if retries = 0u then error
else
// NOTE : If this is modified to use 'async', you MUST
// change this to use 'Async.Sleep' here instead!
System.Threading.Thread.Sleep retryWaitTime
// Try retrieving the page again.
retrievePageRetry retryWaitTime retries client uri
[<EntryPoint>]
let main argv =
/// WebClient used for retrieving content.
use wc = new WebClient ()
/// The amount of time to wait before re-attempting to fetch a page.
let retryWaitTime = TimeSpan.FromSeconds 2.0
/// The maximum number of times we'll try to fetch each page.
let maxPageRetries = 3u
/// The URI to fetch.
let fullURI = Uri ("http://www.badaddress.xyz", UriKind.Absolute)
// Fetch the page data.
match retrievePageRetry retryWaitTime maxPageRetries wc fullURI with
| Choice1Of2 pageData ->
printfn "Retrieved %u bytes from: %O" (Array.length pageData) fullURI
0 // Success
| Choice2Of2 (uri, error) ->
printfn "Unable to retrieve the content from: %O" uri
printfn "HTTP Status: (%i) %O" (int error.Status) error.Status
printfn "Message: %s" error.Message
1 // Failure
Basically, I split your code out into two functions, plus the original main:
One function that attempts to retrieve the content from a specified URI.
One function containing the logic for retrying attempts; this 'wraps' the first function which performs the actual requests.
The original main function now only handles 'settings' (which you could easily pull from an app.config or web.config) and printing the final results. In other words, it's oblivious to the retrying logic -- you could modify the single line of code with the match statement and use the non-retrying request function instead if you wanted.
If you want to pull content from multiple URIs AND wait for a significant amount of time (e.g., 5 minutes) between retries, you should modify the retrying logic to use a priority queue or something instead of using Thread.Sleep or Async.Sleep.
Shameless plug: my ExtCore library contains some things to make your life significantly easier when building something like this, especially if you want to make it all asynchronous. Most importantly, it provides an asyncChoice workflow and collections functions designed to work with it.
As for your question about passing in parameters (like the retry timeout and number of retries) -- I don't think there's a hard-and-fast rule for deciding whether to pass them in or hard-code them within the function. In most cases, I prefer to pass them in, though if you have more than a few parameters to pass in, you're better off creating a record to hold them all and passing that instead. Another approach I've used is to make the parameters option values, where the defaults are pulled from a configuration file (though you'll want to pull them from the file once and assign them to some private field to avoid re-parsing the configuration file each time your function is called); this makes it easy to modify the default values you've used in your code, but also gives you the flexibility of overriding them when necessary.
The following code is the full source of a webmachine resource. The expected behaviour is that the streaming response should be a 200, and should be a string of specified length, entirely composed of the letter 'a'.
This string is indeed returned as the body of the response, but the status code is a 500. How can this be?
-module(dummy_binary_resource).
-export([init/1, to_html/2]).
-include_lib("webmachine/include/webmachine.hrl").
init(Config)->
{ok, Config}.
send_streamed_body(Remaining) ->
PacketSize=1024,
case Remaining of
Partial when Partial =< PacketSize ->
{string:chars($a,Partial),done};
Full ->
{string:chars($a,Full), fun() -> send_streamed_body(Remaining - PacketSize) end}
end.
to_html(ReqData,State)->
PathInfo = wrq:path_info(ReqData),
{ok,SizeString} = dict:find(size,PathInfo),
{Size,[]} = string:to_integer(SizeString),
{true,wrq:set_resp_body({stream,send_streamed_body(Size)},ReqData),State}.
The return value was incorrect.
The appropriate return value is {{halt, 200}, wrq:set_resp_body({stream, send_streamed_body(Size)}, ReqData), State}. because the request data record has been initialized with a default code of 500. It does seem slightly counterintuitive to be returning a "halt" value which isn't going to stop anything, but is in fact going to signify success.