Info
Backend is developed using Laravel 5.5 framework
and APIs are developed following REST principles. For authentication, we are using
firebase. You will need to send the token recieved from firebase in Authorization
header as Bearer {token}
for authentication purpose.
Comment
List of comments on a post
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/posts/1/comments" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts/1/comments");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"current_page": 1,
"data": [
{
"id": 1,
"user_profile_id": {
"id": 1,
"name": "Test User",
"gender": "m",
"image": ""
},
"post_id": 1,
"text": "Test comment",
"deleted_at": null,
"created_at": "2019-12-07 16:52:55",
"updated_at": "2019-12-07 16:52:55",
"like_count": 0,
"dislike_count": 0,
"liked": 0,
"disliked": 0,
"post": {
"id": 1,
"user_profile_id": {
"id": 1,
"name": "Test User",
"gender": "m",
"image": ""
},
"text": "This is a sample post",
"media_url": "https:\/\/via.placeholder.com\/50",
"type": "text",
"share_count": 1,
"video_thumbnail_url": null,
"text_location_on_video": null,
"is_story": 0,
"deleted_at": null,
"created_at": "2019-12-07 14:29:27",
"updated_at": "2019-12-07 16:27:55",
"title": "Sample post title"
}
}
],
"first_page_url": "http:\/\/localhost\/api\/posts\/1\/comments?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http:\/\/localhost\/api\/posts\/1\/comments?page=1",
"next_page_url": null,
"path": "http:\/\/localhost\/api\/posts\/1\/comments",
"per_page": "15",
"prev_page_url": null,
"to": 1,
"total": 1
}
HTTP Request
GET api/posts/{post}/comments
Create a comment
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/posts/1/comments" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"text":"est"}'
const url = new URL("http://localhost/weshare/public/api/posts/1/comments");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
let body = {
"text": "est"
}
fetch(url, {
method: "POST",
headers: headers,
body: body
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/posts/{post}/comments
Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| text | string | required | Comment text |
Like a comment
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/comments/1/like" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/comments/1/like");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "POST",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/comments/{comment}/like
Dislike a comment
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/comments/1/dislike" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/comments/1/dislike");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "POST",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/comments/{comment}/dislike
Post
List of posts
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/posts?treding=0&user_profile_id=-1&type=text" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts");
let params = {
"treding": "0",
"user_profile_id": "-1",
"type": "text",
};
Object.keys(params).forEach(key => url.searchParams.append(key, params[key]));
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"current_page": 1,
"data": [
{
"id": 1,
"user_profile_id": {
"id": 1,
"name": "Test User",
"gender": "m",
"image": ""
},
"text": "This is a sample post",
"media_url": "https:\/\/via.placeholder.com\/50",
"type": "text",
"share_count": 1,
"video_thumbnail_url": null,
"text_location_on_video": null,
"is_story": 0,
"deleted_at": null,
"created_at": "2019-12-07 14:29:27",
"updated_at": "2019-12-07 16:27:55",
"title": "Sample post title",
"like_count": 0,
"dislike_count": 1,
"comment_count": 1,
"liked": 0,
"disliked": 1,
"commented": 1
}
],
"first_page_url": "http:\/\/localhost\/api\/posts?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http:\/\/localhost\/api\/posts?page=1",
"next_page_url": null,
"path": "http:\/\/localhost\/api\/posts",
"per_page": "15",
"prev_page_url": null,
"to": 1,
"total": 1
}
HTTP Request
GET api/posts
Query Parameters
| Parameter | Status | Description |
|---|---|---|
| treding | optional | optional Show trending posts. |
| user_profile_id | optional | string Show posts of a users. |
| type | optional | string Filter posts on the basis of type. |
Create a new post
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/posts" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"title":"quis","text":"nihil","media_url":"aut","type":"laborum","is_story":true}'
const url = new URL("http://localhost/weshare/public/api/posts");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
let body = {
"title": "quis",
"text": "nihil",
"media_url": "aut",
"type": "laborum",
"is_story": true
}
fetch(url, {
method: "POST",
headers: headers,
body: body
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/posts
Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| title | string | optional | optional Title of the post |
| text | string | optional | optional Text(body) of the post, required if parameter media_url is not set |
| media_url | string | optional | optional Media for the post, required if parameter text is not set |
| type | string | required | Type of the post. Possible values: text, image, video, audio, gif |
| is_story | boolean | optional | optional If post is a story. Default value is false |
List of posts posted by current user
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/posts/me" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts/me");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"current_page": 1,
"data": [
{
"id": 2,
"user_profile_id": {
"id": 1,
"name": "Test User",
"gender": "m",
"image": ""
},
"text": "This is a sample story",
"media_url": "https:\/\/via.placeholder.com\/50",
"type": "text",
"share_count": 0,
"video_thumbnail_url": null,
"text_location_on_video": null,
"is_story": 1,
"deleted_at": null,
"created_at": "2019-12-08 14:12:44",
"updated_at": "2019-12-08 14:12:44",
"title": "Sample story",
"like_count": 0,
"dislike_count": 0,
"comment_count": 0,
"liked": 0,
"disliked": 0,
"commented": 0
},
{
"id": 1,
"user_profile_id": {
"id": 1,
"name": "Test User",
"gender": "m",
"image": ""
},
"text": "This is a sample post",
"media_url": "https:\/\/via.placeholder.com\/50",
"type": "text",
"share_count": 1,
"video_thumbnail_url": null,
"text_location_on_video": null,
"is_story": 0,
"deleted_at": null,
"created_at": "2019-12-07 14:29:27",
"updated_at": "2019-12-07 16:27:55",
"title": "Sample post title",
"like_count": 0,
"dislike_count": 1,
"comment_count": 1,
"liked": 0,
"disliked": 1,
"commented": 1
}
],
"first_page_url": "http:\/\/localhost\/api\/posts\/me?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http:\/\/localhost\/api\/posts\/me?page=1",
"next_page_url": null,
"path": "http:\/\/localhost\/api\/posts\/me",
"per_page": "15",
"prev_page_url": null,
"to": 2,
"total": 2
}
HTTP Request
GET api/posts/me
Get a post by id
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/posts/1/show" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts/1/show");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"id": 1,
"user_profile_id": {
"id": 1,
"name": "Test User",
"gender": "m",
"image": ""
},
"text": "This is a sample post",
"media_url": "https:\/\/via.placeholder.com\/50",
"type": "text",
"share_count": 1,
"video_thumbnail_url": null,
"text_location_on_video": null,
"is_story": 0,
"deleted_at": null,
"created_at": "2019-12-07 14:29:27",
"updated_at": "2019-12-07 16:27:55",
"title": "Sample post title",
"like_count": 0,
"dislike_count": 1,
"comment_count": 1,
"liked": 0,
"disliked": 1,
"commented": 1
}
HTTP Request
GET api/posts/{post}/show
Like a post
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/posts/1/like" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts/1/like");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "POST",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/posts/{post}/like
Dislike a post
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/posts/1/dislike" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts/1/dislike");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "POST",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/posts/{post}/dislike
Share a post
Share a post, this will increase share count, actually sharing is being handled at client side
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/posts/1/share" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts/1/share");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "POST",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/posts/{post}/share
Report a post
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/posts/1/report" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/posts/1/report");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"message": "Spam",
"post_id": 1,
"user_profile_id": 1,
"updated_at": "2019-12-08 14:26:38",
"created_at": "2019-12-08 14:26:38",
"id": 25
}
HTTP Request
GET api/posts/{post}/report
List of users who have posted stories
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/stories/users" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/stories/users");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
[
{
"id": 1,
"user_id": "ABZ-TU",
"name": "Test User",
"image": "",
"gender": "m",
"fcm_registration_id": "dummy-id",
"notification_on_like": true,
"notification_on_dislike": true,
"notification_on_comment": true,
"is_admin": 0,
"created_at": "2019-12-07 11:50:25",
"updated_at": "2019-12-07 11:50:25",
"is_blocked": 0,
"is_private": 1
}
]
HTTP Request
GET api/stories/users
List of stories by a user
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/stories/users/1" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/stories/users/1");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
[
{
"id": 2,
"user_profile_id": {
"id": 1,
"name": "Test User",
"gender": "m",
"image": ""
},
"text": "This is a sample story",
"media_url": "https:\/\/via.placeholder.com\/50",
"type": "text",
"share_count": 0,
"video_thumbnail_url": null,
"text_location_on_video": null,
"is_story": 1,
"deleted_at": null,
"created_at": "2019-12-08 14:12:44",
"updated_at": "2019-12-08 14:12:44",
"title": "Sample story"
}
]
HTTP Request
GET api/stories/users/{userProfile}
PostActivity
Activities on posts posted by user
Requires authentication
Example request:
curl -X GET -G "http://localhost/weshare/public/api/activities" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/activities");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"current_page": 1,
"data": [],
"first_page_url": "http:\/\/localhost\/api\/activities?page=1",
"from": null,
"last_page": 1,
"last_page_url": "http:\/\/localhost\/api\/activities?page=1",
"next_page_url": null,
"path": "http:\/\/localhost\/api\/activities",
"per_page": "15",
"prev_page_url": null,
"to": null,
"total": 0
}
HTTP Request
GET api/activities
UserProfile
Create or update profile
Requires authentication
Example request:
curl -X POST "http://localhost/weshare/public/api/profile" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"update":"et","name":"dolore","image":"qui","gender":"m or f","fcm_registration_id":"quaerat","notification_on_like":"est","notification_on_dislike":"est","notification_on_comment":"et","is_private":true}'
const url = new URL("http://localhost/weshare/public/api/profile");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
let body = {
"update": "et",
"name": "dolore",
"image": "qui",
"gender": "m or f",
"fcm_registration_id": "quaerat",
"notification_on_like": "est",
"notification_on_dislike": "est",
"notification_on_comment": "et",
"is_private": true
}
fetch(url, {
method: "POST",
headers: headers,
body: body
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/profile
Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| update | string | required | Update or create profile. Send 1 for update and 0 for create |
| name | string | optional | optional Name of the user, if not given will retrieved from firebase token |
| image | string | optional | optional Image of the user, if not given will retrieved from firebase token |
| gender | string | required | Gender of user. |
| fcm_registration_id | string | required | One signal id |
| notification_on_like | string | required | Send notification or not when post is liked |
| notification_on_dislike | string | required | Send notification or not when post is disliked |
| notification_on_comment | string | required | Send notification or not on new comment |
| is_private | boolean | required | Profile is provate or not |
List of followers
Example request:
curl -X GET -G "http://localhost/weshare/public/api/profile/followers/1" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/profile/followers/1");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"current_page": 1,
"data": [
{
"id": 1,
"user_id": "ABZ-TU",
"name": "Test User",
"image": "",
"gender": "m",
"fcm_registration_id": "dummy-id",
"notification_on_like": true,
"notification_on_dislike": true,
"notification_on_comment": true,
"is_admin": 0,
"created_at": "2019-12-07 11:50:25",
"updated_at": "2019-12-07 11:50:25",
"is_blocked": 0,
"is_private": 1,
"is_following": 1,
"following_count": 1,
"followers_count": 1
}
],
"first_page_url": "http:\/\/localhost\/api\/profile\/followers\/1?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http:\/\/localhost\/api\/profile\/followers\/1?page=1",
"next_page_url": null,
"path": "http:\/\/localhost\/api\/profile\/followers\/1",
"per_page": "15",
"prev_page_url": null,
"to": 1,
"total": 1
}
HTTP Request
GET api/profile/followers/{userProfile}
List of users current user is following
Example request:
curl -X GET -G "http://localhost/weshare/public/api/profile/following/1" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/profile/following/1");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"current_page": 1,
"data": [
{
"id": 1,
"user_id": "ABZ-TU",
"name": "Test User",
"image": "",
"gender": "m",
"fcm_registration_id": "dummy-id",
"notification_on_like": true,
"notification_on_dislike": true,
"notification_on_comment": true,
"is_admin": 0,
"created_at": "2019-12-07 11:50:25",
"updated_at": "2019-12-07 11:50:25",
"is_blocked": 0,
"is_private": 1,
"is_following": 1,
"following_count": 1,
"followers_count": 1
}
],
"first_page_url": "http:\/\/localhost\/api\/profile\/following\/1?page=1",
"from": 1,
"last_page": 1,
"last_page_url": "http:\/\/localhost\/api\/profile\/following\/1?page=1",
"next_page_url": null,
"path": "http:\/\/localhost\/api\/profile\/following\/1",
"per_page": "15",
"prev_page_url": null,
"to": 1,
"total": 1
}
HTTP Request
GET api/profile/following/{userProfile}
Search profiles
Example request:
curl -X POST "http://localhost/weshare/public/api/profile/search" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/profile/search");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "POST",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/profile/search
Follow User
Toogle follow state for user.
Example request:
curl -X POST "http://localhost/weshare/public/api/profile/follow/1" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/profile/follow/1");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "POST",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/profile/follow/{userProfile}
List of follow requests
Example request:
curl -X GET -G "http://localhost/weshare/public/api/profile/follow-requests" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/profile/follow-requests");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
[]
HTTP Request
GET api/profile/follow-requests
Send follow request to a private profile
Example request:
curl -X GET -G "http://localhost/weshare/public/api/profile/follow-requests/follow/1" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/profile/follow-requests/follow/1");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"follow_request": true
}
HTTP Request
GET api/profile/follow-requests/follow/{userprofile}
Accept/Reject follow request
Example request:
curl -X POST "http://localhost/weshare/public/api/profile/follow-requests/1/review" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"accept":true}'
const url = new URL("http://localhost/weshare/public/api/profile/follow-requests/1/review");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
let body = {
"accept": true
}
fetch(url, {
method: "POST",
headers: headers,
body: body
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/profile/follow-requests/{followrequest}/review
Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| accept | boolean | required | Accept or not follow request |
Show user profile
Example request:
curl -X GET -G "http://localhost/weshare/public/api/profile/1" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json"
const url = new URL("http://localhost/weshare/public/api/profile/1");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
fetch(url, {
method: "GET",
headers: headers,
})
.then(response => response.json())
.then(json => console.log(json));
Example response (200):
{
"id": 1,
"user_id": "ABZ-TU",
"name": "Test User",
"image": "",
"gender": "m",
"fcm_registration_id": "dummy-id",
"notification_on_like": true,
"notification_on_dislike": true,
"notification_on_comment": true,
"is_admin": 0,
"created_at": "2019-12-07 11:50:25",
"updated_at": "2019-12-07 11:50:25",
"is_blocked": 0,
"is_private": 1,
"posts_count": 1,
"like_count": 0,
"dislike_count": 1,
"comment_count": 1,
"is_following": 1,
"is_follow_requested": 0,
"following_count": 1,
"followers_count": 1
}
HTTP Request
GET api/profile/{userProfile}
Report a user
Example request:
curl -X POST "http://localhost/weshare/public/api/report/1" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{"message":"quos"}'
const url = new URL("http://localhost/weshare/public/api/report/1");
let headers = {
"Authorization": "Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json",
}
let body = {
"message": "quos"
}
fetch(url, {
method: "POST",
headers: headers,
body: body
})
.then(response => response.json())
.then(json => console.log(json));
HTTP Request
POST api/report/{reportUser}
Body Parameters
| Parameter | Type | Status | Description |
|---|---|---|---|
| message | text | required | Reason for reporting |


Synergistically repurpose performance based best practices rather than technically sound collaboration and idea-sharing.


Login Remind me
If you can’t remember your password, fill the input below with your e-mail and we’ll send you a new one:
New user? Yay!


About
|
Contact
|
Feedback
{{ user.display_name }}
{% for index in range(fav|length) %}
{{ fav[index].creation_date }}
{{ fav[index].title}}
Asked by
{{ fav[index].owner.display_name }}
{% for answer in range(fav[index].answers|length) %}
{{ answer }}
{% endfor %}
{% endfor %}
Top Stories
My Feed
Ask a Question
Answer a Question
Notifications
Requests
About me
About disKuss
Contact
Feedback


Only one module/namespace: Skylink.
The public API and events appear by default.
This demo uses your API test account to create a video conference room. New users are displayed below your personnal video element.
This demo presents the different states a web real time communication can have.
This demo presents the different possibilities to send messages with SkylinkJS.
This demo presents the different possibilities to send files with SkylinkJS.
This demo presents an IRC like chat powered by SkylinkJS.
This demo shows how to focus on one video in a conference demo. It allows to send private secure message and files.
This demo presents the possibilities using another third party library: Gridster. It is an advanced version of the Jumbotron Demo.
This demo presents the use of privileged concept
This demo shows you most of the functionality SkylinkJS can provide. All the states and information provided by the API are displayed.


This adorable hairstyle features cornrows in the front which retain moisture into the hair promoting growth. Only the back of the hair will require daily maintenance.
Locs keeps the moisture into the hair allowing length retention. It also requires little maintenance so this reduces hair breakage that occurs from combing the hair.
The braided buns at the back retain moisture into the hair whilst the springy curls at the front gives the hairstyle a gorgeous finish.
This trendy hairstyle protects your curls as they are wrapped up allowing them to stay moisturised. It is just as beneficial for curly hair as it looks!
This two strand twist is a cute and protective style for girls with natural curls. A versatile look, these twists can be worn down,or tied up in a bun, ponytail or pigtails.
How cute is this side braided style! This braided hairstyle keeps hair in place whilst the colourful beads are the perfect way to involve her sense of style in the process.
One of the most classic braided French styles.If you're an active girl this is easy to both install and maintain.


6 pieces. Steamed/pan fried, ponzu ginger sauce
3 pieces.
Deep fried. Crab stick, onion, cream cheese(6 pcs)
green onipn,tofu,seaweed
choice of sea salt or spicy sriracha
yuzuyogury,bonito


|
VirtualSCADA
1.0
VirtualSCADA Senior Design Project
|
Public Member Functions | |
| __construct (FilesystemInterface $driver) | |
| exists ($path) | |
| get ($path) | |
| put ($path, $contents, $visibility=null) | |
| getVisibility ($path) | |
| setVisibility ($path, $visibility) | |
| prepend ($path, $data) | |
| append ($path, $data) | |
| delete ($paths) | |
| copy ($from, $to) | |
| move ($from, $to) | |
| size ($path) | |
| mimeType ($path) | |
| lastModified ($path) | |
| files ($directory=null, $recursive=false) | |
| allFiles ($directory=null) | |
| directories ($directory=null, $recursive=false) | |
| allDirectories ($directory=null, $recursive=false) | |
| makeDirectory ($path) | |
| deleteDirectory ($directory) | |
| getDriver () | |
| Public Member Functions inherited from Filesystem | |
| allDirectories ($directory=null) | |
Protected Member Functions | |
| filterContentsByType ($contents, $type) | |
| parseVisibility ($visibility) | |
Protected Attributes | |
| $driver | |
Additional Inherited Members | |
| Data Fields inherited from Filesystem | |
| const | VISIBILITY_PUBLIC = 'public' |
| const | VISIBILITY_PRIVATE = 'private' |
Definition at line 12 of file FilesystemAdapter.php.
| __construct | ( | FilesystemInterface | $driver | ) |
Create a new filesystem adapter instance.
| \League\Flysystem\FilesystemInterface | $driver |
Definition at line 27 of file FilesystemAdapter.php.
| allDirectories | ( | $directory = null , |
|
$recursive = false |
|||
| ) |
Get all (recursive) of the directories within a given directory.
| string | null | $directory | |
| bool | $recursive |
Definition at line 249 of file FilesystemAdapter.php.
| allFiles | ( | $directory = null | ) |
Get all of the files from the given directory (recursive).
| string | null | $directory |
Implements Filesystem.
Definition at line 223 of file FilesystemAdapter.php.
| append | ( | $path, | |
| $data | |||
| ) |
Append to a file.
| string | $path | |
| string | $data |
Implements Filesystem.
Definition at line 123 of file FilesystemAdapter.php.
| copy | ( | $from, | |
| $to | |||
| ) |
Copy a file to a new location.
| string | $from | |
| string | $to |
Implements Filesystem.
Definition at line 153 of file FilesystemAdapter.php.
| delete | ( | $paths | ) |
Delete the file at a given path.
| string | array | $paths |
Implements Filesystem.
Definition at line 134 of file FilesystemAdapter.php.
| deleteDirectory | ( | $directory | ) |
Recursively delete a directory.
| string | $directory |
Implements Filesystem.
Definition at line 271 of file FilesystemAdapter.php.
| directories | ( | $directory = null , |
|
$recursive = false |
|||
| ) |
Get all of the directories within a given directory.
| string | null | $directory | |
| bool | $recursive |
Implements Filesystem.
Definition at line 235 of file FilesystemAdapter.php.
| exists | ( | $path | ) |
Determine if a file exists.
| string | $path |
Implements Filesystem.
Definition at line 38 of file FilesystemAdapter.php.
| files | ( | $directory = null , |
|
$recursive = false |
|||
| ) |
Get an array of all files in a directory.
| string | null | $directory | |
| bool | $recursive |
Implements Filesystem.
Definition at line 210 of file FilesystemAdapter.php.
|
protected |
Filter directory contents by type.
| array | $contents | |
| string | $type |
Definition at line 293 of file FilesystemAdapter.php.
| get | ( | $path | ) |
Get the contents of a file.
| string | $path |
Implements Filesystem.
Definition at line 51 of file FilesystemAdapter.php.
| getDriver | ( | ) |
| getVisibility | ( | $path | ) |
Get the visibility for the given path.
| string | $path |
Implements Filesystem.
Definition at line 82 of file FilesystemAdapter.php.
| lastModified | ( | $path | ) |
Get the file's last modification time.
| string | $path |
Implements Filesystem.
Definition at line 198 of file FilesystemAdapter.php.
| makeDirectory | ( | $path | ) |
Create a directory.
| string | $path |
Implements Filesystem.
Definition at line 260 of file FilesystemAdapter.php.
| mimeType | ( | $path | ) |
Get the mime-type of a given file.
| string | $path |
Definition at line 187 of file FilesystemAdapter.php.
| move | ( | $from, | |
| $to | |||
| ) |
Move a file to a new location.
| string | $from | |
| string | $to |
Implements Filesystem.
Definition at line 165 of file FilesystemAdapter.php.
|
protected |
Parse the given visibility value.
| string | null | $visibility |
Definition at line 308 of file FilesystemAdapter.php.
| prepend | ( | $path, | |
| $data | |||
| ) |
Prepend to a file.
| string | $path | |
| string | $data |
Implements Filesystem.
Definition at line 111 of file FilesystemAdapter.php.
| put | ( | $path, | |
| $contents, | |||
$visibility = null |
|||
| ) |
Write the contents of a file.
| string | $path | |
| string | $contents | |
| string | $visibility |
Implements Filesystem.
Definition at line 71 of file FilesystemAdapter.php.
| setVisibility | ( | $path, | |
| $visibility | |||
| ) |
Set the visibility for the given path.
| string | $path | |
| string | $visibility |
Implements Filesystem.
Definition at line 99 of file FilesystemAdapter.php.
| size | ( | $path | ) |
Get the file size of a given file.
| string | $path |
Implements Filesystem.
Definition at line 176 of file FilesystemAdapter.php.
|
protected |
Definition at line 19 of file FilesystemAdapter.php.


Your browser doesn't appear to support the HTML5
<canvas>
element.
Your browser doesn't appear to support the HTML5
<canvas>
element.


Trade in the financial markets with a multi-asset trading platform: Meta Trader 5.
The choice of the modern trader. One platform, many markets.
Join a licensed broker with a clear vision and unwavering commitment to treating all its clients like champions.
Our power and versatility gives you a full arsenal of tools to trade successfully in any financial market.
Gain access to our multi-asset trading platform and you can trade Forex and other markets
all through one platform. We provide you with all the necessary tools for financial trading, including:
Technical and
fundamental analysis
Alogorithmic and
copy trading
Custom technical indicators
and trading robots
Trade the world’s most popular Forex pairs, combining ultra-low or fixed spreads and No Dealing Desk (NDD) execution in a high-speed trading environment.
Trade a variety of binary options on currencies, indices, OTC stocks, and commodities with the same rates of return as traders in the interbank market.
Explore a range of other markets, including metals, futures, and CFDs.


Organization
{{ org.orgName }}
Organization is requiredPipeline Name
{{ pipeline.name }}
Pipeline name is requiredSteps
{{ step.stepName }}
There is no step during this period for this pipeline! Please change the search time in the Config page!
Pipeline step is required


Log-in to your account
Authenticate
Please Verify
Liveness Check
Voice
Enrollment
Verification
Please Enroll First
Face
Enrollment
Verification
Please Enroll First
Video
Enrollment
Verification
Please Enroll First


Token field cannot be empty.
About: The access token allows GradeStat to import data about your classes from bCourses to be integrated with the app's functionality. This data is never used by GradeStat for any purposes other than providing you with our service. This data is not viewed or shared by GradeStat.
Instructions: To obtain a new access token, go the "Settings" page in your bCourses account. Under "Approved Integrations", click "New Access Token". Copy and paste the token value from the box that appears in your window into this form and click "Submit". This will update your course info from bCourses.
Click this link to get a token:
https://bcourses.berkeley.edu/profile/settings


#include <uwoptical-channel.h>
UwOpticalChannel extends Miracle channel class and implements the underwater optical channel.
Definition at line 50 of file uwoptical-channel.h.
◆ UwOpticalChannel()
| UwOpticalChannel::UwOpticalChannel |
( |
| ) |
|
◆ ~UwOpticalChannel()
| virtual UwOpticalChannel::~UwOpticalChannel |
( |
| ) |
|
|
inlinevirtual |
◆ command()
| int UwOpticalChannel::command |
( |
int |
argc, |
|
|
const char *const * |
argv |
|
) |
| |
|
virtual |
TCL command interpreter.
It implements the following OTcl methods:
- Parameters
-
| argc | Number of arguments in argv. |
| argv | Array of strings which are the command parameters (Note that argv[0] is the name of the object). |
- Returns
- TCL_OK or TCL_ERROR whether the command has been dispatched successfully or not.
Definition at line 80 of file uwoptical-channel.cpp.
◆ getPropDelay()
| double UwOpticalChannel::getPropDelay |
( |
Position * |
s, |
|
|
Position * |
d |
|
) |
| |
|
protectedvirtual |
Returns the underwater optical propagation delay between the two given Position pointers.
- Parameters
-
| Position* | s pointer to the first Position object |
| Position* | d pointer to the second Position object |
- Returns
- underwater optical propagation delay [s]
Definition at line 120 of file uwoptical-channel.cpp.
◆ recv()
| void UwOpticalChannel::recv |
( |
Packet * |
p, |
|
|
ChSAP * |
chsap |
|
) |
| |
|
virtual |
Performs the reception of packets from upper and lower layers.
- Parameters
-
| Packet* | Pointer to the packet received. |
| ChSAP* | Pointer to the channel Service Access Point (SAP) |
Definition at line 114 of file uwoptical-channel.cpp.
◆ sendUpPhy()
| void UwOpticalChannel::sendUpPhy |
( |
Packet * |
p, |
|
|
ChSAP * |
chsap |
|
) |
| |
|
protected |
Sends the given Packet* to the upper PHY layer.
- Parameters
-
| Packet* | Pointer to the packet received. |
| ChSAP* | Pointer to the channel Service Access Point (SAP) |
Definition at line 87 of file uwoptical-channel.cpp.
◆ refractive_index
| double UwOpticalChannel::refractive_index |
|
protected |
◆ speed_of_light
| double UwOpticalChannel::speed_of_light |
|
protected |
The documentation for this class was generated from the following files:


Home
Scope
Normative references
Controlled vocabularies
Checklists
Environmental packages
Syntax data types
{{ content }}
Genomic Standards Consortium


--- date: 2011-10-06T00:00:00Z published: true title: How To Live Life LIKE A BOSS IV [Gifs] | Smosh url: /2011/10/06/how-to-live-life-like-a-boss-iv-gifs-smosh/ ---
In life we are given a choice. Do we do things normally, or do we do things... LIKE A BOSS?


Brett Zamir
on 2010-09-25 17:46:19
@Lazy eyes: Thanks for the input! Fixed in Git. The problem was/is that while the default flag, SORT_REGULAR in PHP, by relying on original type, is able to distinguish numbers from strings, while in JS, using an object to simulate associative arrays, leads to the issue that all object keys being strings. I've now changed SORT_REGULAR to assume that any numeric string gets treated as though its original type were numeric. If you want to always treat as strings, or always treat as numbers, you can choose from the different flags instead.
Lazy eyes
on 2010-09-24 14:38:07
I've got a set of keys
[1000000] => 1
[1100000] => 1
[850000] => 5
[880000] => 2
[890000] => 4
it doesn't seem to work with different key lengths? Any fix for this?
Kevin van Zonneveld
on 2010-06-19 16:22:07
@ ALastorZ: We won't include it as it isn't a PHP feature, but you make a good point as I think PHP doesn't care for strings when doing a SORT_NUMERIC. Fixed: https://github.com/kvz/phpjs/commit/c175338a592db72bc4140627c9b044702ceed26a
ALastorZ
on 2010-05-31 16:41:04
I've needed this useful addition:
case 'SORT_STRNUMERIC': // compare items by first number in string
sorter = function (a, b) {
a = Number(String(a).match(/(\d*)/)[1]);
b = Number(String(b).match(/(\d*)/)[1]);
return (a - b);
};
break;
Brett Zamir
on 2009-01-18 04:21:16
No, no steroids, but maybe the (healthy) mental equivalent... I was having come concentration problems, and my doctor had blood tests done which showed a borderline B12 deficiency. I started taking LIQUID vitamin B12 (16,666% percent of RDA!) and it has increased my mental endurance enormously. I've taken vitamin B6 in the past which very significantly helps my atrocious working/auditory memory, but I have to find the right balance, or otherwise, daily B6 in the regular dose really does have a counterproductive effect for me (not for B12 apparently in my experience).
A better solution to the latter problem has been DHA (in omega-3 fatty acids, especially fish oil), and that has also been just awesome for working memory (though I think it can be a little dehydrating to take it every day). I really think there are inadequacies in our food supply, with current farming practices only replenishing the soil with the nutrients needed for mere growth (like nitrogen), from what I've heard.
Just heard a TV special about how a particularly long-lived group of Asians lived longer due to vitamins in their soil or something (the special was in Chinese (I'm living in China), and my Chinese is still just so-so).
Now how's that for really off-topic?
Kevin van Zonneveld
on 2009-01-14 11:55:24
@ Brett Zamir: Added! You're on steroids or something? ;)
Brett Zamir
on 2009-01-14 04:10:49
Lastly for now (I hope), sort() and rsort() (and usort()) need to be changed along the lines of asort() (and uasort()) to allow object sorting...
Brett Zamir
on 2009-01-14 04:03:14
Sorry, I guess that wasn't all. You can also get arsort() by changing this line in asort():
if (inputArr[j+1] < inputArr[j]) {
to
if (inputArr[j+1] > inputArr[j]) {
And we can also trivially get usort() and uksort() by changing sort flags in the corresponding functions to callbacks (uasort() would not be as easy though, since my version presently depends on the sorter for internal uses as well):
function usort( array, sorter ) {
if (typeof sorter === 'string') {
sorter = window[sorter];
}
if (sorter !== false) {
array.sort(sorter);
} else {
array.sort();
}
return true;
}
and
function uksort(array, sorter) {
if (typeof sorter === 'string') {
sorter = window[sorter];
}
var tmp_arr = {}, keys = [];
// Make a list of key names
for (var key in array) {
keys.push(key);
}
// Sort key names
try {
if (sorter) {
keys.sort(sorter);
} else {
keys.sort();
}
}
catch (e) {
return false;
}
// Rebuild array with sorted key names
for (var i = 0; i < keys.length; i++) {
key = keys[i];
tmp_arr[key] = array[key];
delete array[key];
}
for (i in tmp_arr) {
array[i] = tmp_arr[i]
}
return true;
}
var arr = {"d" : "lemon", "a" : "orange", "b" : "banana", "c" : "apple"};
uksort(arr, function (a, b) {
if (a > b) return 1;
if (b < a) return -1;
return 0;
});
The only other array functions left (besides uasort() as mentioned above) now are natsort, natcasesort, and array_multisort. (Of course, we have to still support the flags on the existing ones, etc.)
Brett Zamir
on 2009-01-14 03:40:31
Lastly, while it looks like your other sort functions are fine, krsort() does need to be changed too. For that, you can use the ksort() I sent two posts before with the line "keys.reverse();" added after the try-catch block.
Brett Zamir
on 2009-01-14 03:27:02
Whoops, sorry the function just given works, but the example doesn't! (I was playing around with the values and forgot to switch back). The example section should be:
$fruits = {"d" : "lemon", "a" : "orange", "b" : "banana", "c" : "apple"};
asort($fruits);
var $output = '';
for (var $key in $fruits) {
$val = $fruits[$key];
$output += $key+' = '+$val+"\n";
}
alert($output);
/*
c = apple
b = banana
d = lemon
a = orange
*/
Brett Zamir
on 2009-01-14 03:24:14
I also used the same basic approach just mentioned in this asort() as well, but I needed a slightly different approach since I could not rely on values being unique, as keys were for ksort().
function asort (inputArr) {
function bubbleSort(keyArr, inputArr) {
for (var i = inputArr.length-2; i >= 0; i--) {
for (var j=0; j <= i; j++) {
if (inputArr[j+1] < inputArr[j]) {
var tempValue = inputArr[j];
inputArr[j] = inputArr[j+1];
inputArr[j+1] = tempValue;
var tempKeyVal = keyArr[j];
keyArr[j] = keyArr[j+1];
keyArr[j+1] = tempKeyVal;
}
}
}
}
var valArr = [], keyArr=[];
for (var k in inputArr) { // Get key and value arrays
valArr.push(inputArr[k]);
keyArr.push(k);
delete inputArr[k] ;
}
try {
bubbleSort(keyArr, valArr); // Sort our new temporary arrays
}
catch(e) {
return false;
}
for (i=0; i < valArr.length; i++) { // Repopulate the old array
inputArr[keyArr[i]] = valArr[i];
}
return true;
}
$fruits = {"d" : "lemon", "a" : "orange", "b" : "orange", "c" : "orange"};
asort($fruits);
var $output = '';
for (var $key in $fruits) {
$val = $fruits[$key];
$output += $key+' = '+$val+"\n";
}
alert($output);
/*
c = apple
b = banana
d = lemon
a = orange
*/
Brett Zamir
on 2009-01-14 02:55:24
Hi,
I don't think that this function is working. The last line does not replace the array, since in JavaScript, as a whole object, the arrays (though their values are passed in by reference), are passed in by value (you cannot, for example, reassign or delete the passed-in array as a whole).
You can confirm this with something like this:
function changeArr (arr) {
arr = [3, 2, 1];
}
var a = [1, 2, 3];
changeArr(a);
alert(a); // still 1, 2, 3
However, you can delete and then rebuild its contents, modifying the function as such:
function ksort(array, sort_flags) {
// http://kevin.vanzonneveld.net
// + original by: GeekFG (http://geekfg.blogspot.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// % note: The examples are correct, this is a new way
// * example 1: data = {2: 'van', 3: 'Zonneveld', 1: 'Kevin'};
// * example 1: ksort(data);
// * results 1: data == {1: 'Kevin', 2: 'van', 3: 'Zonneveld'}
// * returns 1: true
var tmp_arr = {}, keys = [], sorter = false;
// For now only SORT_NUMERIC has a custom sorter
// and SORT_REGULAR, SORT_STRING, and SORT_LOCALE_STRING
// are all handled with the default sorter
if (sort_flags == 'SORT_NUMERIC') {
sorter = function (a, b) {
return(a - b);
};
}
// Make a list of key names
for (var key in array) {
keys.push(key);
}
// Sort key names
try {
if (sorter !== false) {
keys.sort(sorter);
} else {
keys.sort();
}
}
catch (e) {
return false;
}
// Rebuild array with sorted key names
for (var i = 0; i < keys.length; i++) {
key = keys[i];
tmp_arr[key] = array[key];
delete array[key];
}
for (i in tmp_arr) {
array[i] = tmp_arr[i]
}
return true;
}
Abhijeet Bagul
on 2008-07-29 07:02:35
Your all functions are nice...
It really helped me lot.


org.apache.axis
Class SimpleChain
java.lang.Object
|
+--org.apache.axis.handlers.BasicHandler
|
+--org.apache.axis.SimpleChain
- All Implemented Interfaces:
- Chain, Handler, java.io.Serializable
- Direct Known Subclasses:
- SimpleTargetedChain
- public class SimpleChain
- extends BasicHandler
- implements Chain
A Simple Chain is a 'composite' Handler in that it aggregates a collection
of Handlers and also acts as a Handler which delegates its operations to
the collection.
A Simple Chain initially has no Handlers. Handlers may be added until the
chain is invoke()d after which Handlers may not be added (and any attempt
to do so will throw an exception).
- Author:
- Doug Davis (dug@us.ibm.com), Glyn Normington<EMAIL_ADDRESS>- See Also:
- Serialized Form
|
Field Summary |
protected java.util.Vector |
handlers
|
protected boolean |
invoked
|
protected static org.apache.commons.logging.Log |
log
|
|
Method Summary |
void |
addHandler(Handler handler)
Adds a handler to the end of the chain. |
boolean |
canHandleBlock(QName qname)
Can this Handler process this QName? |
void |
cleanup()
Cleanup is called when the chain containing this Handler object
is done processing the chain. |
boolean |
contains(Handler handler)
Is this handler in the chain? |
void |
generateWSDL(MessageContext msgContext)
Iterate over the chain letting each handler have a crack at
contributing to a WSDL description. |
org.w3c.dom.Element |
getDeploymentData(org.w3c.dom.Document doc)
This will return the root element of an XML doc that describes the
deployment information about this handler. |
Handler[] |
getHandlers()
Get the list of handlers in the chain - is Handler[] the right form? |
void |
init()
Init is called when the chain containing this Handler object
is instantiated. |
void |
invoke(MessageContext msgContext)
Iterate over the chain invoking each handler. |
void |
onFault(MessageContext msgContext)
Notify the handlers in this chain because some handler
later on has faulted - in reverse order. |
| Methods inherited from class java.lang.Object |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
log
protected static org.apache.commons.logging.Log log
handlers
protected java.util.Vector handlers
invoked
protected boolean invoked
SimpleChain
public SimpleChain()
init
public void init()
- Description copied from interface:
Handler
- Init is called when the chain containing this Handler object
is instantiated.
- Specified by:
init
in interface Handler
- Overrides:
init
in class BasicHandler
cleanup
public void cleanup()
- Description copied from interface:
Handler
- Cleanup is called when the chain containing this Handler object
is done processing the chain.
- Specified by:
cleanup
in interface Handler
- Overrides:
cleanup
in class BasicHandler
invoke
public void invoke(MessageContext msgContext)
throws AxisFault
- Iterate over the chain invoking each handler. If there's a fault
then call 'onFault' for each completed handler in reverse order, then
rethrow the exception.
- Specified by:
invoke
in interface Handler
- Overrides:
invoke
in class BasicHandler
generateWSDL
public void generateWSDL(MessageContext msgContext)
throws AxisFault
- Iterate over the chain letting each handler have a crack at
contributing to a WSDL description.
- Specified by:
generateWSDL
in interface Handler
- Overrides:
generateWSDL
in class BasicHandler
onFault
public void onFault(MessageContext msgContext)
- Notify the handlers in this chain because some handler
later on has faulted - in reverse order. If any handlers
have been added since we visited the chain, they will get
notified too!
- Specified by:
onFault
in interface Handler
- Overrides:
onFault
in class BasicHandler
canHandleBlock
public boolean canHandleBlock(QName qname)
- Description copied from interface:
Handler
- Can this Handler process this QName?
- Specified by:
canHandleBlock
in interface Handler
- Overrides:
canHandleBlock
in class BasicHandler
addHandler
public void addHandler(Handler handler)
- Description copied from interface:
Chain
- Adds a handler to the end of the chain. May not be called after invoke.
- Specified by:
addHandler
in interface Chain
contains
public boolean contains(Handler handler)
- Description copied from interface:
Chain
- Is this handler in the chain?
- Specified by:
contains
in interface Chain
getHandlers
public Handler[] getHandlers()
- Description copied from interface:
Chain
- Get the list of handlers in the chain - is Handler[] the right form?
- Specified by:
getHandlers
in interface Chain
getDeploymentData
public org.w3c.dom.Element getDeploymentData(org.w3c.dom.Document doc)
- Description copied from interface:
Handler
- This will return the root element of an XML doc that describes the
deployment information about this handler. This is NOT the WSDL,
this is all of the static internal data use by Axis - WSDL takes into
account run-time information (like which service we're talking about)
this is just the data that's stored in the registry. Used by the
'list' Admin function.
- Specified by:
getDeploymentData
in interface Handler
- Overrides:
getDeploymentData
in class BasicHandler
Copyright © 2001 Apache XML Project. All Rights Reserved.


Processes And Threading¶
Apache can operate in a number of different modes dependent on the platform being used and the way in which it is configured. This ranges from multiple processes being used, with only one request being handled at a time within each process, to one or more processes being used, with concurrent requests being handled in distinct threads executing within those processes.
The combinations possible are further increased by mod_wsgi through its ability to create groups of daemon processes to which WSGI applications can be delegated. As with Apache itself, each process group can consist of one or more processes and optionally make use of multithreading. Unlike Apache, where some combinations are only possible based on how Apache was compiled, the mod_wsgi daemon processes can operate in any mode based only on runtime configuration settings.
This article provides background information on how Apache and mod_wsgi makes use of processes and threads to handle requests, and how Python sub interpreters are used to isolate WSGI applications. The implications of the various modes of operation on data sharing is also discussed.
WSGI Process/Thread Flags¶
Although Apache can make use of a combination of processes and/or threads to handle requests, this is not unique to the Apache web server and the WSGI specification acknowledges this fact. This acknowledgement is in the form of specific key/value pairs which must be supplied as part of the WSGI environment to a WSGI application. The purpose of these key/value pairs is to indicate whether the underlying web server does or does not make use of multiple processes and/or multiple threads to handle requests.
These key/value pairs are defined as follows in the WSGI specification.
- wsgi.multithread
- This value should evaluate true if the application object may be simultaneously invoked by another thread in the same process, and should evaluate false otherwise.
- wsgi.multiprocess
- This value should evaluate true if an equivalent application object may be simultaneously invoked by another process, and should evaluate false otherwise.
A WSGI application which is not written to take into consideration the different combinations of process and threading models may not be portable and potentially may not be robust when deployed to an alternate hosting platform or configuration.
Although you may not need an application or application component to work under all possible combinations for these values initially, it is highly recommended that any application component still be designed to work under any of the different operating modes. If for some reason this cannot be done due to the very nature of what functionality the component provides, the component should validate if it is being run within a compatible configuration and return a HTTP 500 internal server error response if it isn’t.
An example of a component for which restrictions would apply is one providing an interactive browser based debugger session in response to an internal failure of a WSGI application. In this scenario, for the component to work correctly, subsequent HTTP requests must be processed by the same process. As such, the component can only be used with a web server that uses a single process. In other words, the value of ‘wsgi.multiprocess’ would have to evaluate to be false.
Multi-Processing Modules¶
The main factor which determines how Apache operates is which multi-processing module (MPM) is built into Apache at compile time. Although runtime configuration can customise the behaviour of the MPM, the choice of MPM will dictate whether or not multithreading is available.
On UNIX based systems, Apache defaults to being built with the ‘prefork’
MPM. If Apache 1.3 is being used this is actually the only choice, but for
later versions of Apache, this can be overridden at build time by supplying
an appropriate value in conjunction with the --with-mpm
option when
running the ‘configure’ script for Apache. The main alternative to the
‘prefork’ MPM which can be used on UNIX systems is the ‘worker’ MPM.
If you are unsure which MPM is built into Apache, it can be determined
by running the Apache web server executable with the -V
option. The
output from running the web server executable with this option will be
information about how it was configured when built:
Server version: Apache/2.2.1
Server built: Mar 4 2007 20:48:15
Server's Module Magic Number: 20051115:1
Server loaded: APR 1.2.6, APR-Util 1.2.6
Compiled using: APR 1.2.6, APR-Util 1.2.6
Architecture: 32-bit
Server MPM: Worker
threaded: yes (fixed thread count)
forked: yes (variable process count)
Server compiled with....
-D APACHE_MPM_DIR="server/mpm/worker"
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=128
-D HTTPD_ROOT="/usr/local/apache-2.2"
-D SUEXEC_BIN="/usr/local/apache-2.2/bin/suexec"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="conf/mime.types"
-D SERVER_CONFIG_FILE="conf/httpd.conf"
Which MPM is being used can be determined from the ‘Server MPM’ field.
On the Windows platform the only available MPM is ‘winnt’.
The UNIX ‘prefork’ MPM¶
This MPM is the most commonly used. It was the only mode of operation available in Apache 1.3 and is still the default mode on UNIX systems in later versions of Apache. In this configuration, the main Apache process will at startup create multiple child processes. When a request is received by the parent process, it will be processed by which ever of the child processes is ready.
Each child process will only handle one request at a time. If another request arrives at the same time, it will be handled by the next available child process. When it is detected that the number of available processes is running out, additional child processes will be created as necessary. If a limit is specified as to the number of child processes which may be created and the limit is reached, plus there are sufficient requests arriving to fill up the listener socket queue, the client may instead receive an error resulting from not being able to establish a connection with the web server.
Where additional child processes have to be created due to a peak in the number of current requests arriving and where the number of requests has subsequently dropped off, the excess child processes may be shutdown and killed off. Child processes may also be shutdown and killed off after they have handled some set number of requests.
Although threads are not used to service individual requests, this does not preclude an application from creating separate threads to perform some specific task.
For the typical ‘prefork’ configuration where multiple processes are used, the WSGI environment key/value pairs indicating how processes and threads are being used will be as follows.
- wsgi.multithread
- False
- wsgi.multiprocess
- True
Because multiple processes are being used, a WSGI middleware component such as the interactive browser based debugger described would not be able to be used. If during development and testing of a WSGI application, use of such a debugger was required, the only option which would exist would be to limit the number of processes being used. This could be achieved using the Apache configuration:
StartServers 1
ServerLimit 1
With this configuration, only one process will be started, with no additional processes ever being created. The WSGI environment key/value pairs indicating how processes and threads are being used will for this configuration be as follows.
- wsgi.multithread
- False
- wsgi.multiprocess
- False
In effect, this configuration has the result of serialising all requests through a single process. This will allow an interactive browser based debugger to be used, but may prevent more complex WSGI applications which make use of AJAX techniques from working. This could occur where a web page initiates a sequence of AJAX requests and expects later requests to be able to complete while a response for an initial request is still pending. In other words, problems may occur where requests overlap, as subsequent requests will not be able to be executed until the initial request has completed.
The UNIX ‘worker’ MPM¶
The ‘worker’ MPM is similar to ‘prefork’ mode except that within each child process there will exist a number of worker threads. Instead of a request only being able to be processed by the next available idle child process and with the handling of the request being the only thing the child process is then doing, the request may be processed by a worker thread within a child process which already has other worker threads handling other requests at the same time.
It is possible that a WSGI application could be executed at the same time from multiple worker threads within the one child process. This means that multiple worker threads may want to access common shared data at the same time. As a consequence, such common shared data must be protected in a way that will allow access and modification in a thread safe manner. Normally this would necessitate the use of some form of synchronisation mechanism to ensure that only one thread at a time accesses and or modifies the common shared data.
If all worker threads within a child process were busy when a new request arrives the request would be processed by an idle worker thread in another child process. Apache may still create new child processes on demand if necessary. Apache may also still shutdown and kill off excess child processes, or child processes that have handled more than a set number of requests.
Overall, use of ‘worker’ MPM will result in less child processes needing to be created, but resource usage of individual child processes will be greater. On modern computer systems, the ‘worker’ MPM would in general be the prefered MPM to use and should if possible be used in preference to the ‘prefork’ MPM.
Although contention for the global interpreter lock (GIL) in Python can causes issues for pure Python programs, it is not generally as big an issue when using Python within Apache. This is because all the underlying infrastructure for accepting requests and mapping the URL to a WSGI application, as well as the handling of requests against static files are all performed by Apache in C code. While this code is being executed the thread will not be holding the Python GIL, thus allowing a greater level of overlapping execution where a system has multiple CPUs or CPUs with multiple cores.
This ability to make good use of more than processor, even when using multithreading, is further enchanced by the fact that Apache uses multiple processes for handling requests and not just a single process. Thus, even when there is some contention for the GIL within a specific process, it doesn’t stop other processes from being able to run as the GIL is only local to a process and does not extend across processes.
For the typical ‘worker’ configuration where multiple processes and multiple threads are used, the WSGI environment key/value pairs indicating how processes and threads are being used will be as follows.
- wsgi.multithread
- True
- wsgi.multiprocess
- True
Similar to the ‘prefork’ MPM, the number of processes can be restricted to just one if required using the configuration:
StartServers 1
ServerLimit 1
With this configuration, only one process will be started, with no additional processes ever being created, but that one process would still make use of multiple threads.
The WSGI environment key/value pairs indicating how processes and threads are being used will for this configuration be as follows.
- wsgi.multithread
- True
- wsgi.multiprocess
- False
Because multiple threads are being used, there would be no problem with overlapping requests generated by an AJAX based web page.
The Windows ‘winnt’ MPM¶
On the Windows platform the ‘winnt’ MPM is the only option available. With this MPM, multiple worker threads within a child process are used to handle all requests. The ‘winnt’ MPM is different to the ‘worker’ mode however in that there is only one child process.  Because there is only one child process, the maximum number of threads used is much greater.
The WSGI environment key/value pairs indicating how processes and threads are being used will for this configuration be as follows.
- wsgi.multithread
- True
- wsgi.multiprocess
- False
The mod_wsgi Daemon Processes¶
When using ‘daemon’ mode of mod_wsgi, each process group can be individually configured so as to run in a manner similar to either ‘prefork’, ‘worker’ or ‘winnt’ MPMs for Apache. This is achieved by controlling the number of processes and threads within each process using the ‘processes’ and ‘threads’ options of the WSGIDaemonProcess directive.
To emulate the same process/thread model as the ‘winnt’ MPM, that is, a single process with multiple threads, the following configuration would be used:
WSGIDaemonProcess example threads=25
The WSGI environment key/value pairs indicating how processes and threads are being used will for this configuration be as follows.
- wsgi.multithread
- True
- wsgi.multiprocess
- False
Note that by not specifying the ‘processes’ option only a single process is created within the process group. Although providing ‘processes=1’ as an option would also result in a single process being created, this has a slightly different meaning and so you should only do this if necessary.
The difference between not specifying the ‘processes’ option and defining ‘processes=1’ will be that WSGI environment attribute called ‘wsgi.multiprocess’ will be set to be True when the ‘processes’ option is defined, whereas not providing the option at all will result in the attribute being set to be False. This distinction is to allow for where some form of mapping mechanism might be used to distribute requests across multiple process groups and thus in effect it is still a multiprocess application.
In other words, if you use the configuration:
WSGIDaemonProcess example processes=1 threads=25
the WSGI environment key/value pairs indicating how processes and threads are being used will instead be:
- wsgi.multithread
- True
- wsgi.multiprocess
- True
If you need to ensure that ‘wsgi.multiprocess’ is False so that interactive debuggers do not complain about an incompatible configuration, simply do not specify the ‘processes’ option and allow the default behaviour of a single daemon process to apply.
To emulate the same process/thread model as the ‘worker’ MPM, that is, multiple processes with multiple threads, the following configuration would be used:
WSGIDaemonProcess example processes=2 threads=25
The WSGI environment key/value pairs indicating how processes and threads are being used will for this configuration be as follows.
- wsgi.multithread
- True
- wsgi.multiprocess
- True
To emulate the same process/thread model as the ‘prefork’ MPM, that is, multiple processes with only a single thread running in each, the following configuration would be used:
WSGIDaemonProcess example processes=5 threads=1
The WSGI environment key/value pairs indicating how processes and threads are being used will for this configuration be as follows.
- wsgi.multithread
- False
- wsgi.multiprocess
- True
Note that when using mod_wsgi daemon processes, the processes are only used to execute the Python based WSGI application. The processes are not in any way used to serve static files, or host applications implemented in other languages.
Unlike the normal Apache child processes when ‘embedded’ mode of mod_wsgi is used, the configuration as to the number of daemon processes within a process group is fixed. That is, when the server experiences additional load, no more daemon processes are created than what is defined. You should therefore always plan ahead and make sure the number of processes and threads defined is adequate to cope with the expected load.
Sharing Of Global Data¶
When the ‘winnt’ MPM is being used, or the ‘prefork’ or ‘worker’ MPM are forced to run with only a single process, all request handlers within a specific WSGI application will always be accessing the same global data. This global data will persist in memory until Apache is shutdown or restarted, or in the case of the ‘prefork’ or ‘worker’ MPM until the child process is recycled due to reaching a predefined request limit.
This ability to access the same global data and for that data to persist for the lifetime of the child process is not present when either of the ‘prefork’ or ‘worker’ MPM are used in multiprocess mode. In other words, where the WSGI environment key/value pair indicating how processes are used is set to:
- wsgi.multiprocess
- True
This is because request handlers can execute within the context of distinct child processes, each with their own set of global data unique to that child process.
The consequences of this are that you cannot assume that separate invocations of a request handler will have access to the same global data if that data only resides within the memory of the child process. If some set of global data must be accessible by all invocations of a handler, that data will need to be stored in a way that it can be accessed from multiple child processes. Such sharing could be achieved by storing the global data within an external database, the filesystem or in shared memory accessible by all child processes.
Since the global data will be accessible from multiple child processes at the same time, there must be adequate locking mechanisms in place to prevent distinct child processes from trying to modify the same data at the same time. The locking mechanisms need to also be able to deal with the case of multiple threads within one child process accessing the global data at the same time, as will be the case for the ‘worker’ and ‘winnt’ MPM.
Python Sub Interpreters¶
The default behaviour of mod_wsgi is to create a distinct Python sub interpreter for each WSGI application. Thus, where Apache is being used to host multiple WSGI applications a process will contain multiple sub interpreters. When Apache is run in a mode whereby there are multiple child processes, each child process will contain sub interpreters for each WSGI application.
When a sub interpreter is created for a WSGI application, it would then normally persist for the life of the process. The only exception to this would be where interpreter reloading is enabled, in which case the sub interpreter would be destroyed and recreated when the WSGI application script file has been changed.
For the sub interpreter created for each WSGI application, they will each have their own set of Python modules. In other words, a change to the global data within the context of one sub interpreter will not be seen from the sub interpreter corresponding to a different WSGI application. This will be the case whether or not the sub interpreters are in the same process.
This behaviour can be modified and multiple applications grouped together using the WSGIApplicationGroup directive. Specifically, the directive indicates that the marked WSGI applications should be run within the context of a common sub interpreter rather than being run in their own sub interpreters. By doing this, each WSGI application will then have access to the same global data. Do note though that this doesn’t change the fact that global data will not be shared between processes.
The only other way of sharing data between sub interpreters within the one child process would be to use an external data store, or a third party C extension module for Python which allows communication or sharing of data between multiple interpreters within the same process.
Building A Portable Application¶
Taking into consideration the different process models used by Apache and the manner in which interpreters are used by mod_wsgi, to build a portable and robust application requires the following therefore be satisified.
1. Where shared data needs to be visible to all application instances, regardless of which child process they execute in, and changes made to the data by one application are immediately available to another, including any executing in another child process, an external data store such as a database or shared memory must be used. Global variables in normal Python modules cannot be used for this purpose.
2. Access to and modification of shared data in an external data store must be protected so as to prevent multiple threads in the same or different processes from interfering with each other. This would normally be achieved through a locking mechanism visible to all child processes.
3. An application must be re-entrant, or simply put, be able to be called concurrently by multiple threads at the same time. Data which needs to exist for the life of the request, would need to be stored as stack based data, thread local data, or cached in the WSGI application environment. Global variables within the actual application module cannot be used for this purpose.
4. Where global data in a module local to a child process is still used, for example as a cache, access to and modification of the global data must be protected by local thread locking mechanisms.


About Gwen
...some fun facts.
I love learning!
I've made continuing education an anchor point in my life.
I have a deeply ingrained need for continuous growth.
Accordingly, I've earned and enjoyed a steep upward trajectory with increasing scope and impact throughout my career.
- At Boeing, I led mechanical, aerospace, and electrical engineers covering an assortment of airplane environmental control systems.
- My Greenpoint team consisted primarily of mechanical engineers, but spanning both systems and structural disciplines.
- At HKX, I directed all of Engineering and Technical Publications.
How do I have fun?
Well, I've got a hobby of collecting hobbies. The chronology of that tidbit:
- autocross
- skiing & snowboarding
- windsurfing
- sailing (Certified thru Bareboat Catamaran, thanks to San Juan Sailing!
- cooking (Deb Perelman is my idol!
- SCUBA (PADI certified
- gardening
- First Robotics (I had great fun mentoring this teamfor a couple years.
- dance (Who's heard of west coast swing?)
- coding (Come see my bit twiddling on Github!


{% extends '../layouts/default.html' %} {% block content %}
I have 10 years of experience designing for the web, and specialize in the areas of user interface design, interaction design, visual design and prototyping. I’ve worked with notable startups including Pearl Street Software.


|
PieDock
1.6.3
|
#include <Application.h>
Public Member Functions | |
| Application (Settings &) | |
| virtual | ~Application () |
| Display * | getDisplay () const |
| Settings * | getSettings () |
| bool | remote (const char *=0) const |
| int | run (bool *) |
PieDock application
| Application::Application | ( | Settings & | s | ) |
Initialize application
| s | - settings object |
|
virtual |
Clean up
| bool Application::remote | ( | const char * | menu = 0 | ) | const |
Try to forward request to an already running instance; returns true if successful
| menu | - name of menu to open |
| int Application::run | ( | bool * | stopFlag | ) |


I'm looking for a reified version of Gecode.distinct() but there doesn't appear to be one. Can anyone suggest a reasonable alternative? Malcolm -- "The modern man in revolt has become practically useless for all purposes of revolt. By rebelling against everything he has lost his right to rebel against anything." - G.K.Chesterton, Orthodoxy


Press Try to load some data and start using ForecastDualKc. You can also load your own climate and weather files. Use this Example CSV dataset to get started.
Use this option to load FDK projects.
Use this option to share, store, or further analyze FDK projects. Selecting "Settings" + "Inputs" will make your project fully reproducible by anyone. Select "Outputs" to save model results for analysis outside FDK.
Press Try to load some data and start using ForecastDualKc. You can also load your own climate and weather files. Use this Example CSV dataset to get started.


| include fileName |
These definitions can be included in any program in the following manner:
const upArrow := 200
const downArrow := 208
const rightArrow := 205
const leftArrow := 203
include "arrows"
var ch : string ( 1 )
getch ( ch ) % Read one character
case ord ( ch ) of
label upArrow :
handle up arrow
label downArrow :
handle down arrow
label rightArrow :
handle right arrow
label leftArrow :
handle left arrow
label :
handle any other key
end case
It is common to save procedures, functions and modules in separate files. The files are collected together using include.
will include the file "C:\TURING\SORTING.T" in the program.
include "%sorting.t"
will include the file "/usr/local/lib/oot/teacher/sorting.t" in the program.
include "%oot/teacher/sorting.t"


Before you can use this device, you need to register it. Enter a description below so you can identify this device. OwnPass will send you an e-mail with an activation code which can be used to activate this device.
Description


Mutual TLS with Otoroshi
Otoroshi support mutual TLS out of the box. mTLS from client to Otoroshi and from Otoroshi to targets are supported. In this article we will see how to configure Otoroshi to use end-to-end mTLS. All code and files used in this articles can be found on the Otoroshi github
Dynamic Mutual TLS is an experimental feature. It can change until it becomess an official feature
End-to-end mTLS
The use case is the following :
for this demo you will have to edit your /etc/hosts
file to add the following entries
<IP_ADDRESS> api.backend.lol api.frontend.lol www.backend.lol www.frontend.lol validation.backend.lol
Create certificates
But first we need to generate some certificates to make the demo work
mkdir mtls-demo
cd mtls-demo
mkdir ca
mkdir server
mkdir client
# create a certificate authority key, use password as pass phrase
openssl genrsa -out ./ca/ca-backend.key 4096
# remove pass phrase
openssl rsa -in ./ca/ca-backend.key -out ./ca/ca-backend.key
# generate the certificate authority cert
openssl req -new -x509 -sha256 -days 730 -key ./ca/ca-backend.key -out ./ca/ca-backend.cer -subj "/CN=MTLSB"
# create a certificate authority key, use password as pass phrase
openssl genrsa -out ./ca/ca-frontend.key 2048
# remove pass phrase
openssl rsa -in ./ca/ca-frontend.key -out ./ca/ca-frontend.key
# generate the certificate authority cert
openssl req -new -x509 -sha256 -days 730 -key ./ca/ca-frontend.key -out ./ca/ca-frontend.cer -subj "/CN=MTLSF"
# now create the backend cert key, use password as pass phrase
openssl genrsa -out ./server/_.backend.lol.key 2048
# remove pass phrase
openssl rsa -in ./server/_.backend.lol.key -out ./server/_.backend.lol.key
# generate the csr for the certificate
openssl req -new -key ./server/_.backend.lol.key -sha256 -out ./server/_.backend.lol.csr -subj "/CN=*.backend.lol"
# generate the certificate
openssl x509 -req -days 365 -sha256 -in ./server/_.backend.lol.csr -CA ./ca/ca-backend.cer -CAkey ./ca/ca-backend.key -set_serial 1 -out ./server/_.backend.lol.cer
# verify the certificate, should output './server/_.backend.lol.cer: OK'
openssl verify -CAfile ./ca/ca-backend.cer ./server/_.backend.lol.cer
# now create the frontend cert key, use password as pass phrase
openssl genrsa -out ./server/_.frontend.lol.key 2048
# remove pass phrase
openssl rsa -in ./server/_.frontend.lol.key -out ./server/_.frontend.lol.key
# generate the csr for the certificate
openssl req -new -key ./server/_.frontend.lol.key -sha256 -out ./server/_.frontend.lol.csr -subj "/CN=*.frontend.lol"
# generate the certificate
openssl x509 -req -days 365 -sha256 -in ./server/_.frontend.lol.csr -CA ./ca/ca-frontend.cer -CAkey ./ca/ca-frontend.key -set_serial 1 -out ./server/_.frontend.lol.cer
# verify the certificate, should output './server/_.frontend.lol.cer: OK'
openssl verify -CAfile ./ca/ca-frontend.cer ./server/_.frontend.lol.cer
# now create the client cert key for backend, use password as pass phrase
openssl genrsa -out ./client/_.backend.lol.key 2048
# remove pass phrase
openssl rsa -in ./client/_.backend.lol.key -out ./client/_.backend.lol.key
# generate the csr for the certificate
openssl req -new -key ./client/_.backend.lol.key -out ./client/_.backend.lol.csr -subj "/CN=*.backend.lol"
# generate the certificate
openssl x509 -req -days 365 -sha256 -in ./client/_.backend.lol.csr -CA ./ca/ca-backend.cer -CAkey ./ca/ca-backend.key -set_serial 2 -out ./client/_.backend.lol.cer
# generate a pkcs12 version of the cert and key, use password as password
openssl pkcs12 -export -clcerts -in client/_.backend.lol.cer -inkey client/_.backend.lol.key -out client/_.backend.lol.p12
# now create the client cert key for frontend, use password as pass phrase
openssl genrsa -out ./client/_.frontend.lol.key 2048
# remove pass phrase
openssl rsa -in ./client/_.frontend.lol.key -out ./client/_.frontend.lol.key
# generate the csr for the certificate
openssl req -new -key ./client/_.frontend.lol.key -out ./client/_.frontend.lol.csr -subj "/CN=*.frontend.lol"
# generate the certificate
openssl x509 -req -days 365 -sha256 -in ./client/_.frontend.lol.csr -CA ./ca/ca-frontend.cer -CAkey ./ca/ca-frontend.key -set_serial 2 -out ./client/_.frontend.lol.cer
# generate a pkcs12 version of the cert and key, use password as password
openssl pkcs12 -export -clcerts -in client/_.frontend.lol.cer -inkey client/_.frontend.lol.key -out client/_.frontend.lol.p12
once it’s done, you should have something like
$ tree
.
├── backend.js
├── ca
│ ├── ca-backend.cer
│ ├── ca-backend.key
│ ├── ca-frontend.cer
│ └── ca-frontend.key
├── client
│ ├── _.backend.lol.cer
│ ├── _.backend.lol.csr
│ ├── _.backend.lol.key
│ ├── _.backend.lol.p12
│ ├── _.frontend.lol.cer
│ ├── _.frontend.lol.csr
│ ├── _.frontend.lol.key
│ └── _.frontend.lol.p12
└── server
├── _.backend.lol.cer
├── _.backend.lol.csr
├── _.backend.lol.key
├── _.frontend.lol.cer
├── _.frontend.lol.csr
└── _.frontend.lol.key
3 directories, 18 files
The backend service
now, let’s create a backend service using nodejs. Create a file named backend.js
touch backend.js
and put the following content
const fs = require('fs');
const https = require('https');
const options = {
key: fs.readFileSync('./server/_.backend.lol.key'),
cert: fs.readFileSync('./server/_.backend.lol.cer'),
ca: fs.readFileSync('./ca/ca-backend.cer'),
};
https.createServer(options, (req, res) => {
res.writeHead(200, {
'Content-Type': 'application/json'
});
res.end(JSON.stringify({ message: 'Hello World!' }) + "\n");
}).listen(8444);
to run the server, just do
node ./backend.js
now you can try your server with
curl --cacert ./ca/ca-backend.cer https://api.backend.lol:8444/
# will print {"message":"Hello World!"}
now modify your backend server to ensure that the client provides a client certificate like:
const fs = require('fs');
const https = require('https');
const options = {
key: fs.readFileSync('./server/_.backend.lol.key'),
cert: fs.readFileSync('./server/_.backend.lol.cer'),
ca: fs.readFileSync('./ca/ca-backend.cer'),
requestCert: true,
rejectUnauthorized: true
};
https.createServer(options, (req, res) => {
console.log('Client certificate CN: ', req.socket.getPeerCertificate().subject.CN);
res.writeHead(200, {
'Content-Type': 'application/json'
});
res.end(JSON.stringify({ message: 'Hello World!' }) + "\n");
}).listen(8444);
you can test your new server with
curl --cacert ./ca/ca-backend.cer --cert-type pkcs12 --cert ./client/_.backend.lol.p12:password https://api.backend.lol:8444/
# will print {"message":"Hello World!"}
Otoroshi setup
Download the latest version of the Otoroshi jar and run it like
java -jar otoroshi.jar
[info] otoroshi-env - Admin API exposed on http://otoroshi-api.oto.tools:8080
[info] otoroshi-env - Admin UI exposed on http://otoroshi.oto.tools:8080
[info] otoroshi-in-memory-datastores - Now using InMemory DataStores
[info] otoroshi-env - The main datastore seems to be empty, registering some basic services
[info] otoroshi-env - You can log into the Otoroshi admin console with the following credentials<EMAIL_ADDRESS>/ xxxxxxxxxxxx
[info] play.api.Play - Application started (Prod)
[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:8080
[info] p.c.s.AkkaHttpServer - Listening for HTTPS on /0:0:0:0:0:0:0:0:8443
[info] otoroshi-env - Generating a self signed SSL certificate for https://*.oto.tools ...
and log into otoroshi with the tuple<EMAIL_ADDRESS>/ xxxxxxxxxxxx
displayed in the logs. Once logged in, create a new public service exposed on http://api.frontend.lol
that targets ahttps://api.backend.lol:8444/
.
and test it
curl http://api.frontend.lol:8080/
# the following error should be returned: {"Otoroshi-Error":"Something went wrong, you should try later. Thanks for your understanding."}
As seen before, the target of the otoroshi service is ahttps://api.backend.lol:8444/
. ahttps://
is not a typo and is intended. This tells otoroshi to use its experimental http client
with dynamic tls support to fetch this resource.
you should get an error due to the fact that Otoroshi doesn’t know about the server certificate or the client certificate expected by the server.
We have to add the client certificate for https://api.backend.lol
to Otoroshi. Go to http://otoroshi.oto.tools:8080/bo/dashboard/certificates and create a new item. Copy and paste the content of ./client/_.backend.lol.cer
and ./client/_.backend.lol.key
respectively in Certificate full chain
and Certificate private key
.
and retry the following curl command
curl http://api.frontend.lol:8080/
# the output should be: {"message":"Hello World!"}
now we have to expose https://api.frontend.lol:8443
using otoroshi. Go to http://otoroshi.oto.tools:8080/bo/dashboard/certificates and create a new item. Copy and paste the content of ./server/_.frontend.lol.cer
and ./server/_.frontend.lol.key
respectively in Certificate full chain
and Certificate private key
.
and try the following command
curl --cacert ./ca/ca-frontend.cer https://api.frontend.lol:8443/
# the output should be: {"message":"Hello World!"}
now we have to enforce the fact that we want client certificate for api.frontend.lol
. To do that, we have to create a Validation authority
in otoroshi and use it on the api.frontend.lol
service. Go to http://otoroshi.oto.tools:8080/bo/dashboard/validation-authorities and create a new item. A validation authority is supposed to be a remote service that will say if the client certificate is valid. Here we don’t really care if the certificate is valid or not, but we want to enforce the fact that there is a client certificate. So just check the All cert. valid button
.
now go back on your api.frontend.lol
service, in the Validation authority
section and select the authority you just created.
now if you retry
curl --cacert ./ca/ca-frontend.cer https://api.frontend.lol:8443/
# the output should be: {"Otoroshi-Error":"You're not authorized here !"}
you should get an error because no client cert. is passed with the request. But if you pass the ./client/_.frontend.lol.p12
client cert in your curl call
curl --cacert ./ca/ca-frontend.cer --cert-type pkcs12 --cert ./client/_.frontend.lol.p12:password https://api.frontend.lol:8443/
# the output should be: {"message":"Hello World!"}
End to end test
Now we can try to write a small nodejs client that uses our client certificates. Create a client.js
file with the following code
const fs = require('fs');
const https = require('https');
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
const options = {
hostname: 'api.frontend.lol',
port: 8443,
path: '/',
method: 'GET',
key: fs.readFileSync('./client/_.frontend.lol.key'),
cert: fs.readFileSync('./client/_.frontend.lol.cer'),
ca: fs.readFileSync('./ca/ca-frontend.cer'),
};
const req = https.request(options, (res) => {
console.log('statusCode', res.statusCode);
console.log('headers', res.headers);
console.log('body:');
res.on('data', (data) => {
process.stdout.write(data);
});
});
req.end();
req.on('error', (e) => {
console.error(e);
});
and run the following command
$ node client.js
# statusCode 200
# headers { date: 'Mon, 10 Dec 2018 16:01:11 GMT',
# connection: 'close',
# 'transfer-encoding': 'chunked',
# 'content-type': 'application/json' }
# body:
# {"message":"Hello World!"}
And that’s it
Validating client certificates based on user identity
Validation authorities is an experimental feature. It can change until it becomess an official feature
The use case is the following :
the idea here is to provide a unique client certificate per device that can access Otoroshi and use a validation authority to check if the user is allowed to access the underlying app with a specific device.
Generate client certificates for devices
To do that we are going to create two client certificates, one per device (let say for a laptop and a desktop computer). We are going to use the device serial number as common name of the certificate to be able to identify the device behind the certificate.
openssl genrsa -out ./client/device-1.key 2048
openssl rsa -in ./client/device-1.key -out ./client/device-1.key
openssl req -new -key ./client/device-1.key -out ./client/device-1.csr -subj "/CN=mbp-123456789"
openssl x509 -req -days 365 -sha256 -in ./client/device-1.csr -CA ./ca/ca-frontend.cer -CAkey ./ca/ca-frontend.key -set_serial 3 -out ./client/device-1
openssl pkcs12 -export -clcerts -in client/device-1 -inkey client/device-1.key -out client/device-1.p12
openssl genrsa -out ./client/device-2.key 2048
openssl rsa -in ./client/device-2.key -out ./client/device-2.key
openssl req -new -key ./client/device-2.key -out ./client/device-2.csr -subj "/CN=nuc-987654321"
openssl x509 -req -days 365 -sha256 -in ./client/device-2.csr -CA ./ca/ca-frontend.cer -CAkey ./ca/ca-frontend.key -set_serial 4 -out ./client/device-2
openssl pkcs12 -export -clcerts -in client/device-2 -inkey client/device-2.key -out client/device-2.p12
Setup actual validation
now we are going to write an validation authority (with mTLS too) that is going to respond on https://validation.backend.lol:8445
. The server has access to a list of apps, users and devices to check if everything is correct. In this implementation, the lists are hardcoded, but you can write your own implementation that will fetch data from your corporate LDAP, CA, etc. Create a validation.js
file and add the following content. Don’t forget to do yarn add x509
before running the server with node validation.js
const fs = require('fs');
const https = require('https');
const x509 = require('x509');
// list of knwon apps
const apps = [
{
"id": "iogOIDH09EktFhydTp8xspGvdaBq961DUDr6MBBNwHO2EiBMlOdafGnImhbRGy8z",
"name": "my-web-service",
"description": "A service that says hello",
"host": "www.frontend.lol"
}
];
// list of known users
const users = [
{
"name": "Mathieu",
"email"<EMAIL_ADDRESS>"appRights": [
{
"id": "iogOIDH09EktFhydTp8xspGvdaBq961DUDr6MBBNwHO2EiBMlOdafGnImhbRGy8z",
"profile": "user",
"forbidden": false
},
{
"id": "PqgOIDH09EktFhydTp8xspGvdaBq961DUDr6MBBNwHO2EiBMlOdafGnImhbRGy8z",
"profile": "none",
"forbidden": true
},
],
"ownedDevices": [
"mbp-123456789",
"nuc-987654321",
]
}
];
// list of known devices
const devices = [
{
"serialNumber": "mbp-123456789",
"hardware": "Macbook Pro 2018 13 inc. with TouchBar, 2.6 GHz, 16 Gb",
"acquiredAt": "2018-10-01",
},
{
"serialNumber": "nuc-987654321",
"hardware": "Intel NUC i7 3.0 GHz, 32 Gb",
"acquiredAt": "2018-09-01",
},
{
"serialNumber": "iphone-1234",
"hardware": "Iphone XS, 256 Gb",
"acquiredAt": "2018-12-01",
}
];
const options = {
key: fs.readFileSync('./server/_.backend.lol.key'),
cert: fs.readFileSync('./server/_.backend.lol.cer'),
ca: fs.readFileSync('./ca/ca-backend.cer'),
requestCert: true,
rejectUnauthorized: true
};
function readBody(request) {
return new Promise((success, failure) => {
const body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
const bodyStr = Buffer.concat(body).toString();
success(JSON.parse(bodyStr));
});
});
}
function chainIsValid(chain) {
// validate cert dates
// validate cert against clr
// validate whatever you want here
return true;
}
function call(req, res) {
readBody(req).then(body => {
const service = body.service;
const email = (body.user || { email<EMAIL_ADDRESS>}).email; // here, should not be null if used with an otoroshi auth. module
// common name should be device serial number
const commonName = x509.getSubject(body.chain).commonName
// search for a known device
const device = devices.filter(d => d.serialNumber === commonName)[0];
// search for a known user
const user = users.filter(d => d.email === email)[0];
// search for a known application
const app = apps.filter(d => d.id === service.id)[0];
res.writeHead(200, { 'Content-Type': 'application/json' });
if (chainIsValid(body.chain.map(x509.parseCert)) && user && device && app) {
// check if the user actually owns the device
const userOwnsDevice = user.ownedDevices.filter(d => d === device.serialNumber)[0];
// check if the user has rights to access the app
const rights = user.appRights.filter(d => d.id === app.id)[0];
const hasRightToUseApp = !rights.forbidden
if (userOwnsDevice && hasRightToUseApp) {
// yeah !!!!
console.log(`Call from user "${user.email}" with device "${device.hardware}" on app "${app.name}" with profile "${rights.profile}" authorized`)
res.end(JSON.stringify({ status: 'good', profile: rights.profile }) + "\n");
} else {
// nope !!! nope, nope nope
console.log(`Call from user "${user.email}" with device "${device.hardware}" on app "${app.name}" unauthorized because user doesn't owns the hardware or has no rights`)
res.end(JSON.stringify({ status: 'unauthorized' }) + "\n");
}
} else {
console.log(`Call unauthorized`)
res.end(JSON.stringify({ status: 'unauthorized' }) + "\n");
}
});
}
https.createServer(options, call).listen(8445);
the corresponding authority validation can be created in Otoroshi like
{
"id": "r7m8j31rh66hhdia3ormfm0wfevu1kvg0zgaxsp3oxb6ivf7fy8kvygmvnrlxv81",
"name": "Actual validation authority",
"description": "Actual validation authority",
"url": "ahttps://validation.backend.lol:8445",
"host": "validation.backend.lol",
"goodTtl": 600000,
"badTtl": 60000,
"method": "POST",
"path": "/certificates/_validate",
"timeout": 10000,
"noCache": false,
"alwaysValid": false,
"headers": {}
}
but you don’t need to create it right now.
Typically, a validation authority server is a server with a route on POST /certificates/_validate
that accepts application/json
and returns application/json
with a body like
{
"apikey": nullable {
"clientId": String,
"clientName": String,
"authorizedGroup": String,
"enabled": Boolean,
"readOnly": Boolean,
"allowClientIdOnly": Boolean,
"throttlingQuota": Long,
"dailyQuota": Long,
"monthlyQuota": Long,
"metadata": Map[String, String]
},
"user": nullable {
"email": String,
"name": String,
},
"service": {
"id": String,
"name": String,
"groupId": String,
"domain": String,
"env": String,
"subdomain": String,
"root": String,
"metadata": String
},
"chain": PemFormattedCertificateChainString,
"fingerprints": Array[String]
}
Setup Otoroshi
You can start Otoroshi and import data from the state.json
file in the demo folder. The login tuple is<EMAIL_ADDRESS>/ password
. The state.json
file contains everything you need for the demo, like certificates, service descriptors, auth. modules, etc …
java -Dapp.importFrom=$(pwd)/state.json -Dapp.privateapps.port=8080 -jar otoroshi.jar
[info] otoroshi-env - Admin API exposed on http://otoroshi-api.oto.tools:8080
[info] otoroshi-env - Admin UI exposed on http://otoroshi.oto.tools:8080
[info] otoroshi-in-memory-datastores - Now using InMemory DataStores
[info] otoroshi-env - The main datastore seems to be empty, registering some basic services
[info] otoroshi-env - Importing from: /pwd/state.json
[info] play.api.Play - Application started (Prod)
[info] otoroshi-env - Successful import !
[info] p.c.s.AkkaHttpServer - Listening for HTTP on /0:0:0:0:0:0:0:0:8080
[info] p.c.s.AkkaHttpServer - Listening for HTTPS on /0:0:0:0:0:0:0:0:8443
Testing
You can test the service with curl like
curl --cacert ./ca/ca-frontend.cer --cert-type pkcs12 --cert ./client/device-1.p12:password https://www.frontend.lol:8443/
# output:
Hello World !!!

curl --cacert ./ca/ca-frontend.cer --cert-type pkcs12 --cert ./client/device-2.p12:password https://www.frontend.lol:8443/
# output:
Hello World !!!

curl --cacert ./ca/ca-frontend.cer --cert-type pkcs12 --cert ./client/_.frontend.lol.p12:password https://www.frontend.lol:8443/
# output: {"Otoroshi-Error":"You're not authorized here !"}
as expected, the first two call works as their common name is known by the validation server. The last one fails as it’s not known.
Validate user identity
Now let’s try to setup firefox to provide the client certificate. Open firefox settings, go to privacy settings and security
and click on display certificates
at the bottom of the page. Here you can add the frontend CA (./ca/ca-frontend.cer
) in the Authorities
tab, check the ‘authorize this CA to identify websites’, and then in the certificates
tab, import one of the devices .p12
file (like ./client/device-1.p12
). Firefox will ask for the files password (it should be password
).
Now restart firefox.
Next, go to the my-web-service
service in otoroshi (log in with<EMAIL_ADDRESS>/ password
) and activate Enforce user login
in the Authentication section. It means that now, you’ll have to log in when you’ll go to https://www.frontend.lol:8443. With authentication activated on otoroshi, the user identity will be sent to the validation authority, so you can change the following line in the file validation.js
const email = (body.user || { email<EMAIL_ADDRESS>}).email; // here, should not be null if used with an otoroshi auth. module
to
const email = body.user.email;
Then, in Firefox, go to https://www.frontend.lol:8443/, firefox will ask which client certificate to use. Select the one you imported (in the process, maybe firefox will warn you that the certificate of the site is auto signed, just ignore it and continue ;) )
then, you’ll see a login screen from otoroshi. You can log in with<EMAIL_ADDRESS>/ password
and then you should see the hello world message.
Going further with user authentication
For stronger user authentication, you can try to use an auth. module baked by a keycloak instance with yubikey as a strong second factor authentication instead of the basic auth. module we used previously in this article.


OsmAnd 1.6
October 26, 2013
New major release is rolled out. It has lots of small & useful features. We completely redesigned how the map rendered, now you would not see view freezes while dragging the map. Here is the short list of new features :
- Support of Full HD devices
- Support transparent map background (allow to see map over satellites view)
- Show downloaded maps and facilitate downloading missing maps directly from the map view
- Contour line map files now bundled for countries/regions
- New world basemap with major roads, railroads, and major areal features like forests
- Map magnifier feature (long click on zoom button) - Useful both for tile and vector maps
- Define/edit custom online tile sources (and specify expiration time)
- Support of time conditional speed limits (Netherlands for example)
- Faster search address input
- Bug fixes with routing and rendering