Skip to content

Commit

Permalink
Merge pull request #1 from WURFL/0.1.0
Browse files Browse the repository at this point in the history
0.1.0
  • Loading branch information
andreacastello authored Oct 18, 2021
2 parents dda1a03 + 123836b commit 7cce69d
Show file tree
Hide file tree
Showing 11 changed files with 2,029 additions and 3 deletions.
22 changes: 21 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,24 @@
name = "wmclient"
version = "0.1.0"
authors = ["Andrea Castello <[email protected]>"]
edition = "2018"
edition = "2018"
license = "Apache-2.0"
homepage = "https://www.scientiamobile.com"
repository = "https://github.com/WURFL/wurfl-microservice-client-rust"
readme = "README.md"
description = "WURFL Microservice (by ScientiaMobile, Inc.) is a mobile device detection service that can quickly and accurately detect over 500 capabilities of visiting devices. It can differentiate between portable mobile devices, desktop devices, SmartTVs and any other types of devices that have a web browser."


[dependencies]
#ureq = { version = "2.2.0", features = ["json", "charset"] }
reqwest = { version = "0.10", features = ["blocking", "json"] }
openssl = { version = "^0.10", features = ["vendored"] }
lru = "0.7.0"
serde = { version = "1.0.130", features = ["derive"]}
serde_json = "1.0"
thiserror = "1.0"
md5 = "0.7.0"
# the following dependencies are needed to make the hyper example work.
# you can comment them you don't want to keep or use it.
hyper = { version = "0.14.13", features = ["full"] }
tokio = { version = "1.12.0", features = ["full"] }
152 changes: 150 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# WURFL Microservice client for Rust

==============

WURFL Microservice (by ScientiaMobile, Inc.) is a mobile device detection service that can quickly and accurately detect over 500 capabilities of visiting devices. It can differentiate between portable mobile devices, desktop devices, SmartTVs and any other types of devices that have a web browser.
Expand All @@ -12,4 +11,153 @@ This is the Rust Client API for accessing the WURFL Microservice. The API is rel

- WURFL Microservice for Azure: https://www.scientiamobile.com/products/wurfl-microservice-for-azure/

- WURFL Microservice for Google Cloud Platform: https://www.scientiamobile.com/products/wurfl-microservice-for-gcp/
- WURFL Microservice for Google Cloud Platform: https://www.scientiamobile.com/products/wurfl-microservice-for-gcp/

Compiling the WURFL microservice client requires Rust 1.48 or above.

The Example project contains an example of client api usage for a console application :

```rust
use std::collections::HashMap;
use wmclient::WmClient;

fn main() {
// Let's create the WURFL microservice client by setting the connection data of out WURFL Microservice server
let client_res = WmClient::new("http", "localhost", "8080", "");
// Client is mutable because because some its internal can be modified depending on its interaction with the user or
// the server.
let mut client: WmClient;
if client_res.is_ok(){
client = client_res.unwrap();
println!("-----------------------------------------------------------------------------------");
println!("WURFL Microservice client created successfully. Rust client API version: {}", client.get_api_version());
println!("-----------------------------------------------------------------------------------");
} else {
println!("Unable to create WURFL Microservice client: {}", client_res.err().unwrap().to_string());
return;
}
// Let's add the caching layer to the client
client.set_cache_size(10000);
// Let's gather some server info.
let info_res = client.get_info();
if info_res.is_err(){
println!("Unable to get server info. Exiting.");
return;
}
let info = info_res.unwrap();
println!("WURFL Microservice information:");
println!("Server version: {}", info.wm_version);
println!("WURFL API version: {}", info.wurfl_api_version);
println!("WURFL file info: {}", info.wurfl_info);

// set the capabilities we want to receive from WM server
// Static capabilities
let static_caps = vec! {"model_name brand_name"};
client.set_requested_static_capabilities(Some(static_caps));
// Virtual capabilities
let virtual_caps = vec! {"is_smartphone form_factor"};
client.set_requested_virtual_capabilities(Some(virtual_caps));

// use this headers to perform a device detection.
let mut headers = HashMap::new();
headers.insert("Content-Type", "application/json");
headers.insert("Accept", "text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1");
headers.insert("Accept-Encoding", "gzip, deflate");
headers.insert("Accept-Language", "en");
headers.insert("Device-Stock-Ua", "Mozilla/5.0 (Linux; Android 8.1.0; SM-J610G Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36");
headers.insert("Forwarded", "for=\"110.54.224.195:36350\"");
headers.insert("Referer", "https://www.cram.com/flashcards/labor-and-delivery-questions-889210");
headers.insert("User-Agent", "Opera/9.80 (Android; Opera Mini/51.0.2254/184.121; U; en) Presto/2.12.423 Version/12.16");
headers.insert("X-Clacks-Overhead", "GNU ph");
headers.insert("X-Forwarded-For", "110.54.224.195, 82.145.210.235");
headers.insert("X-Operamini-Features", "advanced, camera, download, file_system, folding, httpping, pingback, routing, touch, viewport");
headers.insert("X-Operamini-Phone", "Android #");
headers.insert("X-Operamini-Phone-Ua", "Mozilla/5.0 (Linux; Android 8.1.0; SM-J610G Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36");

let device_res = client.lookup_headers(headers);
if device_res.is_err(){
println!("Unable to detect device from the given HTTP headers: {}", device_res.err().unwrap().to_string());
return;
}
// No error, let's get the device data
let device = device_res.unwrap();
let wurfl_id_opt = device.capabilities.get("wurfl_id");
if wurfl_id_opt.is_some() {
println!("-----------------------------------------------------------------------------------");
println!("Sample device detection using sample headers");
println!("WURFL device ID : {}", wurfl_id_opt.unwrap());
}
// If you are sure the capability you're querying exists and is in your required set, just unwrap the capability option
println!("This device is a : {} {}", device.capabilities.get("brand_name").unwrap(), device.capabilities.get("model_name").unwrap());

// check if device is a smartphone (a virtual capability)
if device.capabilities.get("is_smartphone").unwrap() == "true" {
println!("This is a smartphone")
}
println!("This device form_factor is: {}", device.capabilities.get("form_factor").unwrap());

// Get all the device manufacturers, and print the first twenty
let makes_res = client.get_all_device_makes();
if makes_res.is_err() {
let err_mk = makes_res.as_ref().err().unwrap();
println!("Error getting device makes data {}", err_mk.to_string());
}


let mut device_makes = makes_res.unwrap();
device_makes.sort();
let limit = 20;
println!("-----------------------------------------------------------------------------------");
println!("Print the first {} brand_names of {} found", limit, device_makes.len());
println!("-----------------------------------------------------------------------------------");

for _i in 0..limit {
println!("{}", device_makes.get(_i).unwrap());
}

// Now call the WM server to get all device model and marketing names produced by Apple
let model_marketing_names_opt = client.get_all_devices_for_make("Apple".to_string());
if model_marketing_names_opt.is_err(){
let err_mmkt = model_marketing_names_opt.as_ref().err().unwrap();
println!("Error getting device model and marketing data for Apple: {}", err_mmkt.to_string());
}

let mut model_marketing_names = model_marketing_names_opt.unwrap();
// Sort model_marketing_names structs by their model name, using natural ordering (thus Uppercase names come first, then lowercase)
model_marketing_names.sort_by(|a,b| a.model_name.cmp(&b.model_name));
println!("-----------------------------------------------------------------------------------");
println!("Printing all model and marketing names for Apple brand");
println!("-----------------------------------------------------------------------------------");
for name in model_marketing_names{
println!("- {} {}", name.model_name, name.marketing_name);
}

// Now call the WM server to get all operating system names
println!("-----------------------------------------------------------------------------------");
println!("Print the list of OSes");
println!("-----------------------------------------------------------------------------------");
let os_opt = client.get_all_oses();
if os_opt.is_err(){
let os_err = os_opt.as_ref().err().unwrap();
println!("Unable to get the list of operating systems: {}", os_err.to_string());
}
let mut os_list = os_opt.unwrap();
os_list.sort();
for os in os_list {
println!("- {}", os);
}

println!("-----------------------------------------------------------------------------------");
println!("Print all version numbers for Android OS");
println!("-----------------------------------------------------------------------------------");
let android_ver_opt = client.get_all_versions_for_os("Android");
if android_ver_opt.is_err(){
let ver_err = android_ver_opt.as_ref().err().unwrap();
println!("Unable to get versions for Android OS: {}", ver_err.to_string());
}
let android_versions = android_ver_opt.unwrap();
for v in android_versions {
println!("- {}", v);
}
}
```
3 changes: 3 additions & 0 deletions THIRD_PARTY_LICENSES
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lru - https://crates.io/crates/lru - MIT License
ureq - https://crates.io/crates/ureq - MIT and Apache 2.0

142 changes: 142 additions & 0 deletions examples/example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::collections::HashMap;
use wmclient::WmClient;

fn main() {
// Let's create the WURFL microservice client by setting the connection data of out WURFL Microservice server
let client_res = WmClient::new("http", "localhost", "8080", "");
// Client is mutable because because some its internal can be modified depending on its interaction with the user or
// the server.
let mut client: WmClient;
if client_res.is_ok(){
client = client_res.unwrap();
println!("-----------------------------------------------------------------------------------");
println!("WURFL Microservice client created successfully. Rust client API version: {}", client.get_api_version());
println!("-----------------------------------------------------------------------------------");
} else {
println!("Unable to create WURFL Microservice client: {}", client_res.err().unwrap().to_string());
return;
}
// Let's add the caching layer to the client
client.set_cache_size(10000);
// Let's gather some server info.
let info_res = client.get_info();
if info_res.is_err(){
println!("Unable to get server info. Exiting.");
return;
}
let info = info_res.unwrap();
println!("WURFL Microservice information:");
println!("Server version: {}", info.wm_version);
println!("WURFL API version: {}", info.wurfl_api_version);
println!("WURFL file info: {}", info.wurfl_info);

// set the capabilities we want to receive from WM server
// Static capabilities
let static_caps = vec! {"model_name brand_name"};
client.set_requested_static_capabilities(Some(static_caps));
// Virtual capabilities
let virtual_caps = vec! {"is_smartphone form_factor"};
client.set_requested_virtual_capabilities(Some(virtual_caps));

// use this headers to perform a device detection.
let mut headers = HashMap::new();
headers.insert("Content-Type", "application/json");
headers.insert("Accept", "text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1");
headers.insert("Accept-Encoding", "gzip, deflate");
headers.insert("Accept-Language", "en");
headers.insert("Device-Stock-Ua", "Mozilla/5.0 (Linux; Android 8.1.0; SM-J610G Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36");
headers.insert("Forwarded", "for=\"110.54.224.195:36350\"");
headers.insert("Referer", "https://www.cram.com/flashcards/labor-and-delivery-questions-889210");
headers.insert("User-Agent", "Opera/9.80 (Android; Opera Mini/51.0.2254/184.121; U; en) Presto/2.12.423 Version/12.16");
headers.insert("X-Clacks-Overhead", "GNU ph");
headers.insert("X-Forwarded-For", "110.54.224.195, 82.145.210.235");
headers.insert("X-Operamini-Features", "advanced, camera, download, file_system, folding, httpping, pingback, routing, touch, viewport");
headers.insert("X-Operamini-Phone", "Android #");
headers.insert("X-Operamini-Phone-Ua", "Mozilla/5.0 (Linux; Android 8.1.0; SM-J610G Build/M1AJQ; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/69.0.3497.100 Mobile Safari/537.36");

let device_res = client.lookup_headers(headers);
if device_res.is_err(){
println!("Unable to detect device from the given HTTP headers: {}", device_res.err().unwrap().to_string());
return;
}
// No error, let's get the device data
let device = device_res.unwrap();
let wurfl_id_opt = device.capabilities.get("wurfl_id");
if wurfl_id_opt.is_some() {
println!("-----------------------------------------------------------------------------------");
println!("Sample device detection using sample headers");
println!("WURFL device ID : {}", wurfl_id_opt.unwrap());
}
// If you are sure the capability you're querying exists and is in your required set, just unwrap the capability option
println!("This device is a : {} {}", device.capabilities.get("brand_name").unwrap(), device.capabilities.get("model_name").unwrap());

// check if device is a smartphone (a virtual capability)
if device.capabilities.get("is_smartphone").unwrap() == "true" {
println!("This is a smartphone")
}
println!("This device form_factor is: {}", device.capabilities.get("form_factor").unwrap());

// Get all the device manufacturers, and print the first twenty
let makes_res = client.get_all_device_makes();
if makes_res.is_err() {
let err_mk = makes_res.as_ref().err().unwrap();
println!("Error getting device makes data {}", err_mk.to_string());
}


let mut device_makes = makes_res.unwrap();
device_makes.sort();
let limit = 20;
println!("-----------------------------------------------------------------------------------");
println!("Print the first {} brand_names of {} found", limit, device_makes.len());
println!("-----------------------------------------------------------------------------------");

for _i in 0..limit {
println!("{}", device_makes.get(_i).unwrap());
}

// Now call the WM server to get all device model and marketing names produced by Apple
let model_marketing_names_opt = client.get_all_devices_for_make("Apple".to_string());
if model_marketing_names_opt.is_err(){
let err_mmkt = model_marketing_names_opt.as_ref().err().unwrap();
println!("Error getting device model and marketing data for Apple: {}", err_mmkt.to_string());
}

let mut model_marketing_names = model_marketing_names_opt.unwrap();
// Sort model_marketing_names structs by their model name, using natural ordering (thus Uppercase names come first, then lowercase)
model_marketing_names.sort_by(|a,b| a.model_name.cmp(&b.model_name));
println!("-----------------------------------------------------------------------------------");
println!("Printing all model and marketing names for Apple brand");
println!("-----------------------------------------------------------------------------------");
for name in model_marketing_names{
println!("- {} {}", name.model_name, name.marketing_name);
}

// Now call the WM server to get all operating system names
println!("-----------------------------------------------------------------------------------");
println!("Print the list of OSes");
println!("-----------------------------------------------------------------------------------");
let os_opt = client.get_all_oses();
if os_opt.is_err(){
let os_err = os_opt.as_ref().err().unwrap();
println!("Unable to get the list of operating systems: {}", os_err.to_string());
}
let mut os_list = os_opt.unwrap();
os_list.sort();
for os in os_list {
println!("- {}", os);
}

println!("-----------------------------------------------------------------------------------");
println!("Print all version numbers for Android OS");
println!("-----------------------------------------------------------------------------------");
let android_ver_opt = client.get_all_versions_for_os("Android");
if android_ver_opt.is_err(){
let ver_err = android_ver_opt.as_ref().err().unwrap();
println!("Unable to get versions for Android OS: {}", ver_err.to_string());
}
let android_versions = android_ver_opt.unwrap();
for v in android_versions {
println!("- {}", v);
}
}
53 changes: 53 additions & 0 deletions examples/web_server_example.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use std::convert::Infallible;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use hyper::{Body, Request, Response, Server};
use hyper::service::{make_service_fn, service_fn};
use wmclient::WmClient;

#[tokio::main]
pub async fn main() {
// First, create a Wurfl microservice client instance and set a cache for it.
// change your server data to your preferred settings
let wmclient_res = WmClient::new("http", "localhost", "8080","");
let wm_client = match wmclient_res {
Ok(wm_client) => wm_client,
Err(error) => panic!("Problem initializing wurfl microservice client: {:?}", error),
};
println!("Created WURFL microservice client API for Rust version: {}", wm_client.get_api_version());
// The wurfl microservice client is mutable because contains some updatable internal state, so we need to
// wrap it into a Mutex to use it in the detect function
let safe_wm_client = Arc::new(Mutex::new(wm_client));
// A `Service` is needed for every connection, so this
// creates one wrapping our `detect` function.
let make_svc = make_service_fn(move |_conn| {
let mut safe_wm_client_clone = Arc::clone(&safe_wm_client);
async {
Ok::<_, Infallible>(service_fn(move |req| {
let response = detect(req, &mut safe_wm_client_clone);
async { Ok::<_, Infallible>(Response::new(Body::from(response))) }
}))
}
});

// We'll bind the server to 127.0.0.1:3000
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let server = Server::bind(&addr).serve(make_svc);
match server.await {
Err(_) => panic!("An error occurred while running WURFL microservice hyper server example, shutting down"),
_ => (),
}
}

// Actual device detection: returns a string with wurfl_id and virtual capability complete_device_name
fn detect(_req: Request<Body>, safe_client: &mut Arc<Mutex<WmClient>>) -> String {
let mut client_guard = safe_client.lock().unwrap();
let device = match client_guard.lookup_headers(_req.headers()) {
Ok(d) => d,
Err(_) => panic!("Error during lookup")
};
drop(client_guard);
let body = format!("Detected device: {} - {} ", device.capabilities.get("wurfl_id").unwrap(),
device.capabilities.get("complete_device_name").unwrap());
return body;
}
Loading

0 comments on commit 7cce69d

Please sign in to comment.