How can I serve a file based on a parameter in Dream? - ocaml

I am trying to write a simple Dream application that will handle routing by simply serving an HTML file associated with the route parameter.
I tried doing it with this code:
let serve_file dir request =
Dream.from_filesystem dir ((Dream.param request "file") ^ ".html")
let () =
Dream.run
## Dream.logger
## Dream.router [
Dream.get "/" (Dream.from_filesystem "html" "index.html");
Dream.get "/:file" (serve_file "html")
]
I am not sure why this isn't working, I get a type error that stumps me even more as well:
This expression has type Dream.request -> Dream.handler, but an expression was expected of
type: Dream.handler = Dream.request -> Dream.response Lwt.t
How can this be the case when the first get is accepting (Dream.from_filesystem)?

You need to send the request to the from_filesystem handler when defining the serve_file handler:
let serve_file dir request =
Dream.from_filesystem
dir
(Dream.param request "file" ^ ".html")
request

Related

Remove uploaded files with filepond

I really enjoy the filepond library and would like to implement it in my flask app. Since I was not able to find any useful examples online, I started to write my own, small, proof of concept web application. I would like to upload multiple images to the server and save the filenames in the database. Furthermore, I would like to edit an entry and add additional files or remove the existing ones.
So far I figured out how to upload and revert files before the form is submitted. I am also able to load existing files inside the edit form. Just when I click the 'x' button on a loaded image inside the edit form the image is removed from the filepond window and a 'removefile' event is fired, but the file still remains on the server. Is it possible to trigger the revert request on a loaded file or is there a better solution altogether?
x-button does not remove the file from the server
Here are the relevant snippets from my js file:
FilePond.registerPlugin(
FilePondPluginFileValidateSize,
FilePondPluginImagePreview,
FilePondPluginFileRename,
FilePondPluginFileValidateType
);
inputElement = document.querySelector(".filepond");
token = document
.querySelector('input[name="csrf_token"]')
.getAttribute("value");
FilePond.setOptions({
server: {
headers: { "X-CSRF-TOKEN": token },
process: "./process",
revert: "./revert",
load: {
url: "../",
}
},
});
const filepond = FilePond.create(inputElement, {
// Here I pass the files to my edit form in the following format:
//
// files: [
// {
// source: 'static/images/some_name.png',
// options: {
// type: 'local'
// }
// }]
});
The relevant code from .py file:
#app.route("/process", methods=["POST"])
#app.route("/edit/process", methods=["POST"])
def process():
upload_dir = "static/images"
file_names = []
for key in request.files:
file = request.files[key]
picture_fn = file.filename
file_names.append(picture_fn)
picture_path = os.path.join(upload_dir, picture_fn)
try:
file.save(picture_path)
except:
print("save fail: " + picture_path)
return json.dumps({"filename": [f for f in file_names]})
#app.route("/revert", methods=["DELETE"])
#app.route("/edit/revert", methods=["DELETE"])
def revert():
upload_dir = "static/images"
parsed = json.loads(request.data)
picture_fn = parsed["filename"][0]
picture_path = os.path.join(upload_dir, picture_fn)
try:
os.remove(picture_path)
except:
print("delete fail: " + picture_path)
return json.dumps({"filename": picture_fn})
Here is the repository to my full flask-filepond app:
https://github.com/AugeJevgenij/flask_filepond
Please excuse me if the question is unclear, does not make sense or the code is written poorly.
I just started programming a few months ago.
Acording to filepond documentation you can remove a file stored locally on the server like this:
FilePond.setOptions({
server: {
remove: (source, load, error) {
// 'source' is the path of the file and should be sent to a server endpoint via http
// call the load method before ending the function
load()
}
}
})
then on your server where you receive the source (path), use it to delete the file. Keep in mind that this is a risky approach to get your website hacked!

Why Finch using EndPoint to represent Router, Request Parameter and Request Body

In finch, we can define router, request parameters, request body like this.
case class Test(name: String, age: Int)
val router: Endpoint[Test] = post("hello") { Ok(Test("name", 30)) }
val requestBody: Endpoint[Test] = body.as[Test]
val requestParameters: Endpoint[Test] = Endpoint.derive[Test].fromParams
The benefit is that we can compose EndPoint together. For example, I can define:
The request path is hello and Parameter should have name and age. (router :: requestParameters)
However, I can still run an invalid endpoint which doesnt include any request path successfully (There is actually no compilation error)
Await.ready(Http.serve(":3000", requestParameters.toService))
The result is returning 404 not found page. Even though I expect that the error should report earlier like compilation error. I wonder that is this a design drawback or it is actually finch trying to fix ?
Many thanks in advance
First of all, thanks a lot for asking this!
Let me give you some insight on how Finch's endpoints work. If you speak category theory, an Endpoint is an Applicative embedding StateT represented as something close to Input => Option[(Input, A)].
Simply speaking, an endpoint takes an Input that wraps an HTTP request and also captures the current path (eg: /foo/bar/baz). When endpoint is applied on to a given request and either matches it (returning Some) or falls over (returning None). When matched, it changes the state of the Input, usually removing the first path segment from it (eg: removing foo from /foo/bar/baz) so the next endpoint is the chain can work with a new Input (and new path).
Once endpoint is matched, Finch checks if there is something else left in the Input that wasn't matched. If something is left, the match considered unsuccessful and your service returns 404.
scala> val e = "foo" :: "bar"
e: io.finch.Endpoint[shapeless.HNil] = foo/bar
scala> e(Input(Request("/foo/bar/baz"))).get._1.path
res1: Seq[String] = List(baz)
When it comes to endpoints matching/extracting query-string params, no path segments are being touched there and the state is passed to the next endpoint unchanged. So when an endpoint param("foo") is applied, the path is not affected. That simply means, the only way to serve a query-string endpoint (note: an endpoint that only extract query-string params) is to send it a request with empty path /.
scala> val s = param("foo").toService
s: com.twitter.finagle.Service[com.twitter.finagle.http.Request,com.twitter.finagle.http.Response] = <function1>
scala> s(Request("/", "foo" -> "bar")).get
res4: com.twitter.finagle.http.Response = Response("HTTP/1.1 Status(200)")
scala> s(Request("/bar", "foo" -> "bar")).get
res5: com.twitter.finagle.http.Response = Response("HTTP/1.1 Status(404)")

Need Perl SOAP::Transport::HTTP::CGI sanity check

OK, I think there's no easy (make that lazy) way to do what I want but given the Perl SOAP::Transport::HTTP::CGI code fragment below what I am looking to do is intercept all SOAP operation passing through the service and log the result of an operation or fault...
SOAP::Transport::HTTP::CGI
-> dispatch_to(
#first arg needs to be the directory holding the PackageName.pm modules with no trailing "/". The args aftre the first are name of SPECIFIC packages to be loaded as needed by SOAP requests
#Failure to call out specific moudules below will allow the external SOAP modules to be loaded, but the overall module #INC path for other Perl modules will be blocked for security reasons
SOAP_MODULE_INCULDE, #name of the directory holding the PackageName.pm modules with no trailing "/"
"TechnicalMetaDataExtraction", #prod - wrapper for EXIFTool
"Ingest", #module (package) name
"ImageManipulation", #module (package) name
"FacebookBroadcast", #unfinished
"CompressDecompress", #unfinished
"ImageOCR", #prod - tesseract
"HandleDotNet", #prod
"Pipeline", #prod (needs work)
"TwitterBroadcast", #prototype
"Messaging", #prototype but text format email works
"Property", #development
"FileManager", #prototype
"PassThrough" #prod - module to do location conversion (URL -> Fedora Obj+DS, Fedora Obj+DS -> file, URL -> InlineBase64, etc.) but format conversion
) #done with the dispacth_to section
-> on_action(sub {
#on_action method lets you specify SOAPAction understanding. It acceptsreference to subroutine that takes three parameters: SOAPAction, method_uri and method_name.
#'SOAPAction' is taken from HTTP header and method_uri and method_name are extracted from request's body. Default behavior is match 'SOAPAction' if present and ignore it otherwise.
#die SOAP::Data->type('string')->name('debug')->value("Intercepted call, SOAP request='".shift(#_)."'");
if($Debug) {
##_ notes:
#[0] - "http://www.example.org/PassThrough/NewOperation"
#[1] - http://www.example.org/PassThrough/
#[2] - NewOperation
#[3] - "undefined"
my %DataHash=(
message => #_[0]
);
#SendMessageToAMQTopic(localtime()." - ".#_[0]);
SendDebugMessage(\%DataHash, "info");
} #there's only one element passed at this level
}) #end on_action
#-> on_debug() #not valid for SOAP::Transport::HTTP::CGI
#-> request() #valid, but does not fire - request method gives you access to HTTP::Request object which you can provide for Server component to handle request.
#-> response() #does not fire - response method gives you access to HTTP::Response object which you can access to get results from Server component after request was handled.
#-> options({compress_threshold => 10000}) #causes problems for the JavaScript soap client - removed for the moment
-> handle() #fires but ignores content in sub - handle method will handle your request. You should provide parameters with request() method, call handle() and get it back with response().
;
Initially I thought I could get the information I needed from the "on_action" method, but that only contains the destination of the SOAP call (before it is sent?) and I'm looking for data in the operation result that will be sent back to the SOAP client. The documentation of "SOAP::Transport::HTTP::CGI" is a bit thin and there are few examples online.
Anyone know if this is possible give the what the code above is set up? If not, then the only other option is to alter each method of my SOAP service code modules to include the "SendDebugMessage" function.
I would suggest subclassing SOAP::Transport::HTTP::CGI and hooking into the handle() method. An untested and probably non-working example would be:
package MySoapCGI;
use Data::Dumper;
use SOAP::Transport::HTTP;
use base 'SOAP::Transport::HTTP::CGI';
sub handle {
my $self = shift;
$self->SUPER::handle(#_);
warn Dumper($self->request);
warn Dumper($self->response);
}
Replace the dumpers with whatever logging you want. You may need to do some XML parsing, because these will be the raw HTTP::Request and HTTP::Response.

Can't launch ocsigen server due to failure : ("That function cannot be called here because it needs information about the request or the site.")

I want to create a service who generates its HTML according to the parameter given and a map. Given the parameter, the service search in the map for the html, and a function to launch on client side.
type sample =
(string (* little text *)*
Html5_types.html Eliom_content.Html5.elt (* html page *) *
(unit -> unit)(* Demonstration function *))
Given that the function is to be launched on client side, I insert it in the map as a client value :
{client{
let demo_function = ignore (Ojquery.add_html
(Ojquery.jQ "li") "<p id='test1'>new paragraph</p>") }}
let get_samples () =
let samples_map = Samples.empty in
let samples_map = Samples.add "add_html"
("text",
(Eliom_tools.F.html
(** html stuff **)
),
{unit->unit{demo_function}}) samples_map in
samples_map
And then I register the service like this :
let sample_service =
Eliom_service.service
~path:["examples"]
~get_params:Eliom_parameter.(string "entry")
()
let () =
Examples_app.register
~service:sample_service
(fun (entry) () ->
try
(let entry = Samples.find entry samples_map in
let html = ((function (name, html, func) -> html) entry) in
let func = ((function (name, html, func) -> func) entry) in
ignore {unit{%func ()}};
Lwt.return (html))
with Not_found -> Lwt.return (not_found)
)
The rest of the code is pretty much only the result of a classic eliom-distillery, with the inclusion of the ojquery package for the client function used.
The compilation phase goes smoothly, but when I try to launch the server, I get the following error message :
ocsigenserver: main: Fatal - Error in configuration file: Error while parsing configuration file: Eliom: while loading local/lib/examples/examples.cma: Failure("That function cannot be called here because it needs information about the request or the site.")
My first guess was that it is due to the fact that I store client values outside of a service, but is there any way to store this kind of values on the server?
I tried to wrap them in regular functions :
let demo_serv_func () = {unit{demo_client_func ()}}
But the problem remained...
I found the issue. The problem was not because I stored client functions, but because I used Eliom_tools.F.html outside of a service.
It happens that Eliom_tools needs the context of the service to function, and since I was storing it outside of the service, it could not work.
I solved the issue by using Eliom_tools inside the service, and storing the body of the HTML page in the map.

Handling Cookies in Ocamlnet

I'm trying to write a bot pulling some data which is only available to authenticated users. I settled for ocaml (v. 3.12.1) and ocamlnet (v. 3.6.5). The first part of the script sends a POST request to the website and by the html I receive back, I can tell that the authentication worked (p1 and p2's values in this code sample are obviously not the ones I'm using).
open Http_client
open Nethttp
let pipeline = new pipeline
let () =
let post_call = new post
"http://www.kraland.org/main.php?p=1&a=100"
[("p1", "username");
("p2", "password");
("Submit", "Ok!")]
in
pipeline#add post_call;
pipeline#run();
Then I extract the cookies where the php session id, the account name, a hash of the password, etc. are stored, put them in the header of the next request and run it. And this is where I run into troubles: I systematically get the boring page every anonymous visitor gets.
let cookies = Header.get_set_cookie post_call#response_header in
let get_call = new get "http://www.kraland.org/main.php?p=1" in
let header = get_call#request_header `Base in
Header.set_set_cookie header cookies;
pipeline#add get_call;
pipeline#run();
When I print the content of the cookies, I do get something weird: I would expect the domain of the cookies to be kraland.org but it does not seem to be the case. This is the printing command I use together with the output:
List.iter (fun c -> Printf.printf "%.0f [%s%s:%b] %s := %s\n"
(match c.cookie_expires with None -> -1. | Some f -> f)
(match c.cookie_domain with None -> "" | Some s -> s)
(match c.cookie_path with None -> "" | Some s -> s)
c.cookie_secure c.cookie_name c.cookie_value)
cookies;
-1 [/:false] PHPSESSID := 410b97b0536b3e949df17edd44965926
1372719625 [:false] login := username
1372719625 [:false] id := myid
1372719625 [:false] password := fbCK/0M+blFRLx3oDp+24bHlwpDUy7x885sF+Q865ms=
1372719625 [:false] pc_id := 872176495311
Edit: I had a go at the problem using Haskell's Http-conduit-browser and it works like a charm using something very much like the doc's example.