Error: Unbound record field Server.callback - Ocaml - webserver

I am following a tutorial that explains how to make a simple web server in OCaml with lwt and Cohttp.
I have a _tags file that contains the following:
true: package(lwt), package(cohttp), package(cohttp.lwt)
And a webserver.ml:
open Lwt
open Cohttp
open Cohttp_lwt_unix
let make_server () =
let callback conn_id req body =
let uri = Request.uri req in
match Uri.path uri with
| "/" -> Server.respond_string ~status:`OK ~body:"hello!\n" ()
| _ -> Server.respond_string ~status:`Not_found ~body:"Route not found" ()
in
let conn_closed conn_id () = () in
Server.create { Server.callback; Server.conn_closed }
let _ =
Lwt_unix.run (make_server ())
Then, ocamlbuild -use-ocamlfind webserver.native triggers the following error:
Error: Unbound record field callback
Command exited with code 2.
If I change to: Server.create { callback; conn_closed } it will also trigger:
Error: Unbound record field callback
Command exited with code 2.
I am not sure how to solve this, so thanks in advance for looking into this.

Probably, you are using a very outdated tutorial, that was written for an old cohttp interface. You can try to look at the up-to-date tutorials in the upstream repository.
In your case, at least the following changes should be made, to compile the program:
You should use function Server.make to create an instance of a server;
The callback and conn_closed values should be passed as function parameters, not as a record, e.g.,
Server.make ~callback ~conn_closed ()
You should use function Server.create and pass a value, that was returned from function Server.make to create a server instance.
So, probably, the following should work:
open Lwt
open Cohttp
open Cohttp_lwt_unix
let make_server () =
let callback conn_id req body =
let uri = Request.uri req in
match Uri.path uri with
| "/" -> Server.respond_string ~status:`OK ~body:"hello!\n" ()
| _ -> Server.respond_string ~status:`Not_found ~body:"Route not found" ()
in
Server.create (Server.make ~callback ())
let _ =
Lwt_unix.run (make_server ())

Related

Succesfully return a connection instance MongoDb - Rust

I'm learning rust, and I have been following the rust book. Usually, I try to implement something to strengthen my knowledge.
Recently I was looking into how to use the MongoDb create to create an instace and return the name of the database.
Here's a sample of what I have:
A folder containing a implementation to build a mongo connection string.
A .env file
A main function to get the info I need.
mongo_connection.rs
#[derive(Debug)]
pub struct ConnectionString {
pub username: String,
pub password: String,
pub cluster: String,
}
impl ConnectionString {
pub fn build_connection_string() -> String {
return format!("mongodb+srv://{}:{}#a{}.k1jklnx.mongodb.net/?retryWrites=true&w=majority",
Self.username, Self.password, Self.cluster)
}
}
main.rs
mod database;
use crate::database::mongo_connection;
use mongodb::{Client, options::ClientOptions};
use std::error::Error;
use dotenv::dotenv;
use std::env;
use tokio;
async fn create_database_connection() -> Client {
dotenv().ok(); //Loading environment variables from .env file
let connection_parameters = mongo_connection::ConnectionString{
username: env::var("USERNAME").expect("No username found on .env"),
password: env::var("PASSWORD").expect("No password found on .env"),
cluster: env::var("CLUSTER").expect("No cluster found on .env")
};
let mut url: String = mongo_connection::ConnectionString::build_connection_string();
println!("{}", url);
let options = ClientOptions::parse(&url).await?;
return Client::with_options(options).await;
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let client = create_database_connection().await?;
let db = client.database(&"runt");
println!("{:?}", db.name());
Ok(())
}
The problem I am facing is that I need to return a connection client. But I keep getting the Error:
let options = ClientOptions::parse(&url).await?;
^ cannot use the `?` operator in an async function that returns `Client`
If I change the result of the function to something like: Result<Client, dyn Eq> then I get the error:
async fn create_database_connection() -> Result<Client, dyn Eq> {
`Eq` cannot be made into an object
^^^^^^^^^^^^^^^ the trait cannot be made into an object because it uses `Self` as a type parameter
|
Any advice on how to solve the error or any workaround is much appreciated.
You could change the return type to either Result<Client, mongodb::error::Error> or Result<Client, Box<dyn Error>>. Either should work, but here's the code with the first version:
async fn create_database_connection() -> Result<Client, mongodb::error::Error> {
dotenv().ok(); //Loading environment variables from .env file
let connection_parameters = mongo_connection::ConnectionString{
username: env::var("USERNAME").expect("No username found on .env"),
password: env::var("PASSWORD").expect("No password found on .env"),
cluster: env::var("CLUSTER").expect("No cluster found on .env")
};
let mut url: String = mongo_connection::ConnectionString::build_connection_string();
println!("{}", url);
let options = ClientOptions::parse(&url).await?;
return Client::with_options(options).await;
}
In case you want to use the Box<dyn Error> version, the return value also needs a .into() to convert the Result<Client, mongodb::error::Error> of Client::with_options() into a Result<Client, Box<dyn Error>>.

I can't capture the DB reference

I'm trying to create an API using Actix-web, async-grahpql and sqlx with postgresql
In the QueryRoot of the async-graphql I am trying to capture the reference of the DB and make the query the database with sqlx, but it gives me an error
let items = Todo::list(&pool).await?;
| ^^^^^ expected struct `sqlx::Pool`, found enum `std::result::Result`
Here I want to capture the reference
use crate::todo::*;
use async_graphql::{Context, FieldResult};
use sqlx::postgres::PgPool;
pub struct QueryRoot;
#[async_graphql::Object]
impl QueryRoot {
async fn todos(&self, ctx: &Context<'_>) -> FieldResult<Vec<Todo>> {
let pool = ctx.data::<PgPool>();
let items = Todo::list(&pool).await?; //<-- This line generates an error
Ok(items)
}
}
Here I define the references
pub fn run(listener: TcpListener, db_pool: PgPool) -> Result<Server, std::io::Error> {
let data_db_pool = Data::new(db_pool);
//GraphQL
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.data(data_db_pool.clone()) //<- DB reference
.finish();
let server = HttpServer::new(move || {
App::new()
.app_data(db_pool.clone()) //<- DB reference
.data(schema.clone())
.route("/graphql", web::post().to(graphql))
.route("/graphql", web::get().to(graphql_playground))
})
.listen(listener)?
.run();
Ok(server)
}
What am I doing wrong? The complete code can be found here.
ctx.data::<T>() returns a Result wrapping a reference to T. You probably want.
let pool = ctx.data::<PgPool>()?;
// ^ return on Err, otherwise yield the &PgPool
let items = Todo::list(pool).await?;
// ^^^^ shouldn't need & here

the trait `std::convert::From<mongodb::error::Error>` is not implemented for `std::io::Error`

Trying to make server with actix-web & mongodb in rust. Getting error
the trait std::convert::From<mongodb::error::Error> is not implemented for std::io::Error
here is my code
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use mongodb::{options::ClientOptions, Client};
async fn greet(req: HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap_or("World");
format!("Hello {}!", &name)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
// Parse a connection string into an options struct.
let mut client_options = ClientOptions::parse("mongodb://localhost:27017")?;
// Manually set an option.
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
// List the names of the databases in that deployment.
for db_name in client.list_database_names(None)? {
println!("{}", db_name);
}
HttpServer::new(|| {
App::new()
.route("/", web::get().to(greet))
.route("/{name}", web::get().to(greet))
})
.bind("127.0.0.1:8000")?
.run()
.await
}
Did I missed anything?
It means that one of the functions you are calling with a ? at the end can return a mongodb::error::Error. But the signature of the main is a std::io::Result<()>, wich is an implied Result<(), std::io::Error>. The only error type it can accept is a io::Error, not a mongodb::Error.
It looks like all the functions you are escaping might return this mongodb::error::Error, so you can try to change the main signature to such a result: Result<(). mongodb::error::Error>.
But I would recommend you do proper error handling on those potential errors, as this is your main(). Change those ? to .expect("Some error message"); at least. The program will still crash, but it will crash in a way that is meaningful to you.

Vapor: how to not receive a particular upload?

In Vapor, how does one setup to check and decline an upload request prior to any part of such file being uploaded to the server?
My current attempt in Vapor 3 is with a route handler structured like:
func imagesUploadOneHandler(_ request: Request) throws -> EventLoopFuture<HTTPResponseStatus> {
let headers = request.http.headers
let headersUploadToken: [String] = headers["Upload-Token"]
if headersUploadToken.count != 1 || headersUploadToken[0] != aValidToken {
return HTTPResponseStatus.notAcceptable
}
// http body content type: 'application/octet-stream'
let dataFuture: EventLoopFuture<Data> = request.http.body.consumeData(max: 50_000_000, on: request)
let futureHTTPResponseStatus = dataFuture.map(to: HTTPResponseStatus.self, {
(data: Data) -> HTTPResponseStatus in
// ... other code
return HTTPResponseStatus.ok
})
return futureHTTPResponseStatus
}
Firstly, the above will not compile. The line return HTTPResponseStatus.notAcceptable has a compile time error "return HTTPResponseStatus.notAcceptable". How to convert HTTPResponseStatus to EventLoopFuture<HTTPResponseStatus> has been elusive.
Secondly, can some code prior to request.http.body.consumeData(...) in a route handler prevent an upload of the file content? Or, is some middleware needed instead to avoid uploading the data content (e.g. some large file) from the http.body?

recursive function with async request

I have a recursive function with an async request. I want to save in an array if the requests were successful, but i don't know how.
Concrete it is a function to upload files and if the function get a folder than the files inside this folder should also be uploaded.
I thought about implement this with a completionHandler, something about this:
func uploadFiles(pathToFile: NSURL) ->[Bool]{
var suc: [Bool] = [Bool]()
self.uploadFileRec(pathToFile, suc: &suc){
(result: [Bool]) in
println(result)
}
return suc
}
func uploadFilesRec(pathToFile: NSURL, inout suc: [Bool], completionHandler: (result: [Bool]) -> Void){
var isDir: ObjCBool = ObjCBool(true)
var manager: NSFileManager = NSFileManager.defaultManager()
manager.fileExistsAtPath(pathToFile.path!, isDirectory: &isDir)
if(isDir){
var error: NSError? = nil
let contents = manager.contentsOfDirectoryAtPath(pathToFile.path!, error: &error) as! [String]
for fileName in contents {
if(fileName != ".DS_Store"){
var pathString = pathToFile.path! + "/" + fileName
var updatePathtoFile = NSURL(fileURLWithPath: pathString)
self.uploadFilesRec(updatePathtoFile!, suc: &suc, completionHandler: completionHandler)
completionHandler(result: suc)
}
}
}
else{
asyncFileUpload(...){
...
suc.append(/*successful or not*/)
}
}
}
But the problem is that println get call not only one time but as many as uploadFileRec get called inside. So if i would call an another function instead of println, the function would be also called many times.
So i think the idea with completionHandler was wrong. How else can i realize this?
Ok i answer my own question.
The idea with complitionHandler was indeed false. Like I'm saying in the question, the complitionHandler is called as many times as the recursive function is called. If you want to collect the responses or like in my app collect if the upload of some files was successful you must use dispatch group. The main idea is to add all request in this group and wait until all are done.
In practice it means create group:
let group = dispatch_group_create()
Enter the group before you call the recursive function and then each time the function call itself:
dispatch_group_enter(self.group)
Leave the group when the request is done and as many time as you enter the group:
dispatch_group_leave(self.group)
And wait until all work is done:
dispatch_group_notify(group, dispatch_get_main_queue()) {
//Do work here
}
var isDir: ObjCBool = ObjCBool(true) so, you treat all files as dirs by default, and when manager.fileExistsAtPath fails you get deep recursion because isDirectory flag stays TRUE:
If path doesn’t exist, this value is undefined upon return
from Apple Doc...
var pathString = pathToFile.path! + "/" + fileName - not sure your're getting correct path in the end. So, you getting recursion. Check your pathString
manager.fileExistsAtPath result is ignored, so, you're doing blind upload...
Fix that things and keep going...