Finished Prototype
This commit is contained in:
parent
ea54a27796
commit
6de476260d
30 changed files with 2189 additions and 0 deletions
11
1602787120 Baseline Measurement.csv
Normal file
11
1602787120 Baseline Measurement.csv
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Iteration,Start,End
|
||||||
|
1,1602787120,1602787240
|
||||||
|
2,1602787240,1602787360
|
||||||
|
3,1602787360,1602787480
|
||||||
|
4,1602787480,1602787600
|
||||||
|
5,1602787600,1602787720
|
||||||
|
6,1602787720,1602787840
|
||||||
|
7,1602787840,1602787961
|
||||||
|
8,1602787961,1602788081
|
||||||
|
9,1602788081,1602788201
|
||||||
|
10,1602788201,1602788321
|
|
36
config/config.go
Normal file
36
config/config.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ServerName string = "Hoernschen's ActivityPub Server"
|
||||||
|
var Version string = "0.1"
|
||||||
|
|
||||||
|
var Homeserver string
|
||||||
|
var Port string
|
||||||
|
|
||||||
|
var PrivateKey []byte
|
||||||
|
var PublicKey []byte
|
||||||
|
var KeyId string
|
||||||
|
var VerifyKeys map[string]map[string][]byte
|
||||||
|
|
||||||
|
var Packetloss int
|
||||||
|
var UnavailableTill int64
|
||||||
|
var Consensus bool
|
||||||
|
var AuthentificationCheck bool
|
||||||
|
var Signing bool
|
||||||
|
var Encryption bool
|
||||||
|
var HttpString string
|
||||||
|
|
||||||
|
//var BackoffPolicy *backoff.Exponential
|
||||||
|
|
||||||
|
func SetDefaultParams() {
|
||||||
|
Packetloss = 0.0
|
||||||
|
UnavailableTill = time.Now().Unix()
|
||||||
|
Consensus = true
|
||||||
|
AuthentificationCheck = true
|
||||||
|
Signing = true
|
||||||
|
Encryption = true
|
||||||
|
HttpString = "https"
|
||||||
|
}
|
69
config/configController.go
Normal file
69
config/configController.go
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SetParamBody struct {
|
||||||
|
Packetloss int `json:"packetloss,omitempty"`
|
||||||
|
UnavailableTill int64 `json:"unavailableTill,omitempty"`
|
||||||
|
Consensus bool `json:"consensus,omitempty"`
|
||||||
|
AuthentificationCheck bool `json:"authentificationCheck,omitempty"`
|
||||||
|
Signing bool `json:"signing,omitempty"`
|
||||||
|
Encryption bool `json:"encryption,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetParams(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||||
|
request := SetParamBody{}
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
err := decoder.Decode(&request)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Could not parse JSON: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Packetloss = request.Packetloss
|
||||||
|
UnavailableTill = request.UnavailableTill
|
||||||
|
Consensus = request.Consensus
|
||||||
|
AuthentificationCheck = request.AuthentificationCheck
|
||||||
|
Signing = request.Signing
|
||||||
|
Encryption = request.Signing
|
||||||
|
HttpString = "https"
|
||||||
|
if !Encryption {
|
||||||
|
HttpString = "http"
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Reset(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||||
|
|
||||||
|
/*
|
||||||
|
if err := device.InitServerSigningKey(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
config.VerifyKeys = make(map[string]map[string][]byte)
|
||||||
|
*/
|
||||||
|
|
||||||
|
database.DB.Close()
|
||||||
|
os.Remove("sqlite.db")
|
||||||
|
|
||||||
|
if err := database.InitDB("sqlite.db"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDefaultParams()
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
13
entities/activity/activity.go
Normal file
13
entities/activity/activity.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package activity
|
||||||
|
|
||||||
|
import "git.nutfactory.org/hoernschen/ActivityPub/entities/object"
|
||||||
|
|
||||||
|
type Activity struct {
|
||||||
|
Context string `json:"@context,omitempty"`
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Actor string `json:"actor,omitempty"`
|
||||||
|
Object *object.Object `json:"object,omitempty"`
|
||||||
|
Published int64 `json:"published,omitempty"`
|
||||||
|
To string `json:"to,omitempty"`
|
||||||
|
}
|
29
entities/activity/activityController.go
Normal file
29
entities/activity/activityController.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package activity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/object"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(id string, actorOfActivity string, objectOfActivity *object.Object, userId string) (newActivity *Activity) {
|
||||||
|
published := objectOfActivity.Published
|
||||||
|
to := objectOfActivity.To
|
||||||
|
if published == 0 {
|
||||||
|
published = time.Now().Unix()
|
||||||
|
}
|
||||||
|
if to == "" {
|
||||||
|
to = utils.GenerateFollowersUrl(userId)
|
||||||
|
}
|
||||||
|
newActivity = &Activity{
|
||||||
|
Context: utils.GetDefaultContext(),
|
||||||
|
Id: id,
|
||||||
|
Type: "Create",
|
||||||
|
Actor: actorOfActivity,
|
||||||
|
Object: objectOfActivity,
|
||||||
|
Published: published,
|
||||||
|
To: to,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
15
entities/actor/actor.go
Normal file
15
entities/actor/actor.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package actor
|
||||||
|
|
||||||
|
type Actor struct {
|
||||||
|
Context string `json:"@context,omitempty"`
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
PreferredUsername string `json:"preferredUsername,omitempty"`
|
||||||
|
Summary string `json:"summary,omitempty"`
|
||||||
|
Inbox string `json:"inbox,omitempty"`
|
||||||
|
Outbox string `json:"outbox,omitempty"`
|
||||||
|
Followers string `json:"followers,omitempty"`
|
||||||
|
Following string `json:"following,omitempty"`
|
||||||
|
Liked string `json:"liked,omitempty"`
|
||||||
|
}
|
75
entities/actor/actorController.go
Normal file
75
entities/actor/actorController.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package actor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/config"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(userId string) (newActor *Actor) {
|
||||||
|
id := utils.GenerateProfileUrl(userId)
|
||||||
|
newActor = GenerateProfile(id, userId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateProfile(id string, userId string) (profile *Actor) {
|
||||||
|
profile = &Actor{
|
||||||
|
Context: utils.GetDefaultContext(),
|
||||||
|
Id: id,
|
||||||
|
Name: userId,
|
||||||
|
PreferredUsername: userId,
|
||||||
|
Type: "Person",
|
||||||
|
Inbox: utils.GenerateInboxUrl(userId),
|
||||||
|
Outbox: utils.GenerateOutboxUrl(userId),
|
||||||
|
Followers: utils.GenerateFollowersUrl(userId),
|
||||||
|
Following: utils.GenerateFollowingUrl(userId),
|
||||||
|
Liked: utils.GenerateLikedUrl(userId),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProfileHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", utils.GetContentTypeString())
|
||||||
|
foundActor, err := ReadActor(fmt.Sprintf("%s://%s%s", config.HttpString, config.Homeserver, r.URL.RequestURI()))
|
||||||
|
if foundActor == nil || err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Actor not found: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
profile := GenerateProfile(foundActor.Id, foundActor.Name)
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(profile); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProfile(actorId string) (err error, profile *Actor) {
|
||||||
|
client := &http.Client{Timeout: 2 * time.Second}
|
||||||
|
req, err := http.NewRequest(http.MethodGet, actorId, bytes.NewBuffer(nil))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header["Content-Type"] = []string{utils.GetContentTypeString()}
|
||||||
|
r, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r.StatusCode != http.StatusOK {
|
||||||
|
err = utils.HandleHTTPError(r)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
err = decoder.Decode(&profile)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
76
entities/actor/actorDatabaseConnector.go
Normal file
76
entities/actor/actorDatabaseConnector.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package actor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateActor(actor *Actor) (err error) {
|
||||||
|
sqlStmt := fmt.Sprintf(`INSERT INTO actor
|
||||||
|
(id, type, name, preferredUsername, summary, inbox, outbox, followers, following, liked)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
||||||
|
|
||||||
|
tx, err := database.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stmt, err := tx.Prepare(sqlStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
_, err = stmt.Exec(
|
||||||
|
actor.Id,
|
||||||
|
actor.Type,
|
||||||
|
actor.Name,
|
||||||
|
actor.PreferredUsername,
|
||||||
|
actor.Summary,
|
||||||
|
actor.Inbox,
|
||||||
|
actor.Outbox,
|
||||||
|
actor.Followers,
|
||||||
|
actor.Following,
|
||||||
|
actor.Liked,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadActor(id string) (foundActor *Actor, err error) {
|
||||||
|
queryStmt := fmt.Sprintf(`SELECT id, type, name, preferredUsername, summary, inbox, outbox, followers, following, liked
|
||||||
|
FROM actor
|
||||||
|
WHERE id = '%s'`, id)
|
||||||
|
|
||||||
|
rows, err := database.DB.Query(queryStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if rows.Next() {
|
||||||
|
foundActor = &Actor{}
|
||||||
|
err = rows.Scan(
|
||||||
|
&foundActor.Id,
|
||||||
|
&foundActor.Type,
|
||||||
|
&foundActor.Name,
|
||||||
|
&foundActor.PreferredUsername,
|
||||||
|
&foundActor.Summary,
|
||||||
|
&foundActor.Inbox,
|
||||||
|
&foundActor.Outbox,
|
||||||
|
&foundActor.Followers,
|
||||||
|
&foundActor.Following,
|
||||||
|
&foundActor.Liked,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
5
entities/collection/collection.go
Normal file
5
entities/collection/collection.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
type OutboxResponse struct {
|
||||||
|
Location string `json:"Location,omitempty"`
|
||||||
|
}
|
350
entities/collection/collectionController.go
Normal file
350
entities/collection/collectionController.go
Normal file
|
@ -0,0 +1,350 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/config"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/activity"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/actor"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/object"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/user"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils"
|
||||||
|
|
||||||
|
"github.com/cenkalti/backoff/v4"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PostInboxHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", utils.GetContentTypeString())
|
||||||
|
newActivity := &activity.Activity{}
|
||||||
|
err := utils.CheckRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(err); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token, err := utils.GetAccessToken(r)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(err); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if token == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode("Missing Token"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
actorName := vars["actorName"]
|
||||||
|
if actorName == "" {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode("Missing Actor"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
err = decoder.Decode(&newActivity)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Could not parse JSON: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if newActivity.Type == "Follow" {
|
||||||
|
err = CreateCollectionObject(utils.GenerateFollowersUrl(actorName), newActivity.Actor)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Creating Collection Object: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("%s follows %s", newActivity.Actor, newActivity.To)
|
||||||
|
} else if newActivity.Type == "Create" {
|
||||||
|
foundObject, err := object.ReadObject(newActivity.Object.Id)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Reading Object: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if foundObject == nil {
|
||||||
|
_ = object.CreateObject(newActivity.Object)
|
||||||
|
/*
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Creating Object: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
_ = CreateCollectionObject(utils.GenerateInboxUrl(actorName), newActivity.Object.Id)
|
||||||
|
/*
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Creating Collection Object: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
log.Printf("%s to %s: %s", newActivity.Actor, newActivity.To, newActivity.Object.Content)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode("Unsupported Activity Type"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostOutboxHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
packetLossNumber := rand.Intn(100)
|
||||||
|
if packetLossNumber > config.Packetloss {
|
||||||
|
w.Header().Set("Content-Type", utils.GetContentTypeString())
|
||||||
|
newActivity := &activity.Activity{}
|
||||||
|
err := utils.CheckRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(err); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token, err := utils.GetAccessToken(r)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(err); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
foundUser, err := user.ReadUserFromToken(token)
|
||||||
|
if err != nil || foundUser == nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Unknown Token: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(r.Body)
|
||||||
|
err = json.Unmarshal(buf.Bytes(), newActivity)
|
||||||
|
if err != nil || newActivity.Type == "Note" {
|
||||||
|
postedObject := &object.Object{}
|
||||||
|
err = json.Unmarshal(buf.Bytes(), postedObject)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Could not parse JSON: %s")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var newObject *object.Object
|
||||||
|
if postedObject.Id == "" {
|
||||||
|
err, newObject = object.New(
|
||||||
|
postedObject.Type,
|
||||||
|
postedObject.AttributedTo,
|
||||||
|
postedObject.Content,
|
||||||
|
postedObject.Published,
|
||||||
|
postedObject.To,
|
||||||
|
foundUser.Id,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Error Creating Object: %s")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = object.CreateObject(newObject)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Creating Object: %s")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newObject = postedObject
|
||||||
|
}
|
||||||
|
newActivity = activity.New(
|
||||||
|
newObject.Id,
|
||||||
|
newObject.AttributedTo,
|
||||||
|
newObject,
|
||||||
|
foundUser.Id,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var recipients []string
|
||||||
|
if newActivity.Type == "Follow" {
|
||||||
|
err = CreateCollectionObject(utils.GenerateFollowingUrl(foundUser.Id), newActivity.To)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Creating Collection Object: %s")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
recipients = append(recipients, newActivity.To)
|
||||||
|
} else if newActivity.Type == "Create" {
|
||||||
|
err = CreateCollectionObject(utils.GenerateOutboxUrl(foundUser.Id), newActivity.Id)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Creating Collection Object: %s")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err, recipients = GetReciepientsForActivity(newActivity)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, recipient := range recipients {
|
||||||
|
log.Printf("Send Activity to Recipient %s", recipient)
|
||||||
|
|
||||||
|
operation := func() error {
|
||||||
|
return SendActivity(newActivity, recipient, token)
|
||||||
|
}
|
||||||
|
notify := func(err error, duration time.Duration) {
|
||||||
|
log.Printf("Error Sending Activity, retrying in %ss: %s", duration/1000000000, err)
|
||||||
|
}
|
||||||
|
backoff.RetryNotify(operation, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 16), notify)
|
||||||
|
|
||||||
|
//go retryActivity(newActivity, recipient, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := OutboxResponse{
|
||||||
|
Location: newActivity.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func retryActivity(activityToSend *activity.Activity, recipient string, token string) (err error) {
|
||||||
|
b, cancel := config.BackoffPolicy.Start(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
for backoff.Continue(b) {
|
||||||
|
err := SendActivity(activityToSend, recipient, token)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = errors.New("Not able to send activity")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func GetReciepientsForActivity(activityToSend *activity.Activity) (err error, recipientsWithoutDuplicates []string) {
|
||||||
|
recipients := []string{}
|
||||||
|
if strings.Contains(activityToSend.To, config.Homeserver) {
|
||||||
|
var foundActor *actor.Actor
|
||||||
|
foundActor, err = actor.ReadActor(activityToSend.To)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if foundActor == nil {
|
||||||
|
var foundCollectionObjects []string
|
||||||
|
foundCollectionObjects, err = ReadCollectionObjects(activityToSend.To)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(foundCollectionObjects) <= 0 {
|
||||||
|
err = errors.New("No Recipients")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
recipients = append(recipients, foundCollectionObjects...)
|
||||||
|
} else {
|
||||||
|
recipients = append(recipients, foundActor.Id)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not Implemented
|
||||||
|
}
|
||||||
|
recipientsWithoutDuplicates = utils.RemoveDuplicates(recipients)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendActivity(activityToSend *activity.Activity, recipient string, token string) (err error) {
|
||||||
|
if strings.Contains(recipient, config.Homeserver) {
|
||||||
|
if activityToSend.Type == "Follow" {
|
||||||
|
followers := fmt.Sprintf("%sfollowers/", activityToSend.To)
|
||||||
|
err = CreateCollectionObject(followers, activityToSend.Actor)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("%s follows %s", activityToSend.Actor, activityToSend.To)
|
||||||
|
} else if activityToSend.Type == "Create" {
|
||||||
|
id := activityToSend.Id
|
||||||
|
if activityToSend.Object != nil {
|
||||||
|
id = activityToSend.Object.Id
|
||||||
|
}
|
||||||
|
inbox := fmt.Sprintf("%sinbox", recipient)
|
||||||
|
err = CreateCollectionObject(inbox, id)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("%s to %s: %s", activityToSend.Actor, recipient, activityToSend.Object.Content)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
activityToSend.To = recipient
|
||||||
|
var profile *actor.Actor
|
||||||
|
err, profile = actor.GetProfile(recipient)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var reqBody []byte
|
||||||
|
reqBody, err = json.Marshal(activityToSend)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &http.Client{Timeout: 2 * time.Second}
|
||||||
|
var req *http.Request
|
||||||
|
log.Printf("Inbox: %s", profile.Inbox)
|
||||||
|
req, err = http.NewRequest(http.MethodPost, profile.Inbox, bytes.NewBuffer(reqBody))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header["Content-Type"] = []string{utils.GetContentTypeString()}
|
||||||
|
req.Header["Authorization"] = []string{fmt.Sprintf("Bearer %s")}
|
||||||
|
var res *http.Response
|
||||||
|
res, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
err = utils.HandleHTTPError(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
62
entities/collection/collectionDatabaseConnector.go
Normal file
62
entities/collection/collectionDatabaseConnector.go
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
package collection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateCollectionObject(collectionId string, objectId string) (err error) {
|
||||||
|
sqlStmt := fmt.Sprintf(`INSERT INTO collectionObject
|
||||||
|
(collectionId, objectId)
|
||||||
|
VALUES
|
||||||
|
(?, ?)`)
|
||||||
|
|
||||||
|
tx, err := database.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(sqlStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
_, err = stmt.Exec(
|
||||||
|
collectionId,
|
||||||
|
objectId,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadCollectionObjects(collectionId string) (collectionObjects []string, err error) {
|
||||||
|
queryStmt := fmt.Sprintf(`SELECT objectId
|
||||||
|
FROM collectionObject
|
||||||
|
WHERE collectionId = '%s'`, collectionId)
|
||||||
|
|
||||||
|
rows, err := database.DB.Query(queryStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var collectionObject string
|
||||||
|
err = rows.Scan(
|
||||||
|
&collectionObject,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
collectionObjects = append(collectionObjects, collectionObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
11
entities/object/object.go
Normal file
11
entities/object/object.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package object
|
||||||
|
|
||||||
|
type Object struct {
|
||||||
|
Context string `json:"@context,omitempty"`
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
AttributedTo string `json:"attributedTo,omitempty"`
|
||||||
|
Content string `json:"content,omitempty"`
|
||||||
|
Published int64 `json:"published,omitempty"`
|
||||||
|
To string `json:"to,omitempty"`
|
||||||
|
}
|
49
entities/object/objectController.go
Normal file
49
entities/object/objectController.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/config"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(objectType string, attributedTo string, content string, published int64, to string, userId string) (err error, newObject *Object) {
|
||||||
|
err, id := utils.CreateUUID()
|
||||||
|
url := fmt.Sprintf("%s://%s/%s/posts/%s", config.HttpString, config.Homeserver, userId, id)
|
||||||
|
if published == 0 {
|
||||||
|
published = time.Now().Unix()
|
||||||
|
}
|
||||||
|
if attributedTo == "" {
|
||||||
|
attributedTo = utils.GenerateProfileUrl(userId)
|
||||||
|
}
|
||||||
|
newObject = &Object{
|
||||||
|
Context: utils.GetDefaultContext(),
|
||||||
|
Id: url,
|
||||||
|
Type: objectType,
|
||||||
|
AttributedTo: attributedTo,
|
||||||
|
Content: content,
|
||||||
|
Published: published,
|
||||||
|
To: to,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPostHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", utils.GetContentTypeString())
|
||||||
|
foundObject, err := ReadObject(r.URL.RequestURI())
|
||||||
|
if foundObject == nil || err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Post not found: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
foundObject.Context = utils.GetDefaultContext()
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(foundObject); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
70
entities/object/objectDatabaseConnector.go
Normal file
70
entities/object/objectDatabaseConnector.go
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateObject(object *Object) (err error) {
|
||||||
|
sqlStmt := fmt.Sprintf(`INSERT INTO object
|
||||||
|
(id, type, attributedTo, content, published, toActor)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?, ?, ?, ?)`)
|
||||||
|
|
||||||
|
tx, err := database.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(sqlStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
_, err = stmt.Exec(
|
||||||
|
object.Id,
|
||||||
|
object.Type,
|
||||||
|
object.AttributedTo,
|
||||||
|
object.Content,
|
||||||
|
object.Published,
|
||||||
|
object.To,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadObject(id string) (foundObject *Object, err error) {
|
||||||
|
queryStmt := fmt.Sprintf(`SELECT id, type, attributedTo, content, published, toActor
|
||||||
|
FROM object
|
||||||
|
WHERE id = '%s'`, id)
|
||||||
|
|
||||||
|
rows, err := database.DB.Query(queryStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if rows.Next() {
|
||||||
|
foundObject = &Object{}
|
||||||
|
err = rows.Scan(
|
||||||
|
&foundObject.Id,
|
||||||
|
&foundObject.Type,
|
||||||
|
&foundObject.AttributedTo,
|
||||||
|
&foundObject.Content,
|
||||||
|
&foundObject.Published,
|
||||||
|
&foundObject.To,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
19
entities/user/user.go
Normal file
19
entities/user/user.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Id string `json:"id,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
Actor string `json:"actor,omitempty"`
|
||||||
|
Sessions []string `json:"sessions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterRequest struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RegisterResponse struct {
|
||||||
|
UserId string `json:"user_id,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
Actor string `json:"actor,omitempty"`
|
||||||
|
}
|
103
entities/user/userController.go
Normal file
103
entities/user/userController.go
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/actor"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(id string, password string) (err error, newUser *User) {
|
||||||
|
err, hashedPassword := utils.Hash([]byte(password))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newUser = &User{
|
||||||
|
Id: id,
|
||||||
|
Password: hashedPassword,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||||
|
request := RegisterRequest{}
|
||||||
|
err := utils.CheckRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(err); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
decoder := json.NewDecoder(r.Body)
|
||||||
|
err = decoder.Decode(&request)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode("Could not parse JSON: %s"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err, newUser := New(request.Username, request.Password)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Error creating User Object: %s")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
foundUser, err := ReadUser(newUser.Id)
|
||||||
|
if foundUser != nil || err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Username already in use: %s", err)); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newActor := actor.New(newUser.Id)
|
||||||
|
err = actor.CreateActor(newActor)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode(fmt.Sprintf("Database Error Creating Actor: %s")); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newUser.Actor = newActor.Id
|
||||||
|
err = CreateUser(newUser)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode("Database Error Creating User: %s"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err, token := utils.CreateToken()
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode("Error Creating Token: %s"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = CreateSession(newUser.Id, token)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
if err := json.NewEncoder(w).Encode("Error Creating Session: %s"); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
response := RegisterResponse{
|
||||||
|
UserId: newUser.Id,
|
||||||
|
Token: token,
|
||||||
|
Actor: newActor.Id,
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
132
entities/user/userDatabaseConnector.go
Normal file
132
entities/user/userDatabaseConnector.go
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateUser(user *User) (err error) {
|
||||||
|
sqlStmt := fmt.Sprintf(`INSERT INTO user
|
||||||
|
(id, password, actor)
|
||||||
|
VALUES
|
||||||
|
(?, ?, ?)`)
|
||||||
|
|
||||||
|
tx, err := database.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(sqlStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
_, err = stmt.Exec(user.Id, user.Password, user.Actor)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSession(userId string, token string) (err error) {
|
||||||
|
sqlStmt := fmt.Sprintf(`INSERT INTO session
|
||||||
|
(token, userId)
|
||||||
|
VALUES
|
||||||
|
(?, ?)`)
|
||||||
|
|
||||||
|
tx, err := database.DB.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := tx.Prepare(sqlStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
_, err = stmt.Exec(token, userId)
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadUser(id string) (foundUser *User, err error) {
|
||||||
|
queryStmt := fmt.Sprintf(`SELECT id, password, actor
|
||||||
|
FROM user
|
||||||
|
WHERE id = '%s'`, id)
|
||||||
|
|
||||||
|
rows, err := database.DB.Query(queryStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if rows.Next() {
|
||||||
|
foundUser = &User{}
|
||||||
|
err = rows.Scan(&foundUser.Id, &foundUser.Password, &foundUser.Actor)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
foundUser.Sessions, err = ReadSessionsForUser(foundUser.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadUserFromToken(token string) (foundUser *User, err error) {
|
||||||
|
queryStmt := fmt.Sprintf(`SELECT u.id, u.password, u.actor
|
||||||
|
FROM user as u
|
||||||
|
join session as s on u.id = s.userId
|
||||||
|
WHERE s.token = '%s'`, token)
|
||||||
|
|
||||||
|
rows, err := database.DB.Query(queryStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if rows.Next() {
|
||||||
|
foundUser = &User{}
|
||||||
|
err = rows.Scan(&foundUser.Id, &foundUser.Password, &foundUser.Actor)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
foundUser.Sessions, err = ReadSessionsForUser(foundUser.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadSessionsForUser(userId string) (sessions []string, err error) {
|
||||||
|
queryStmt := fmt.Sprintf(`SELECT token
|
||||||
|
FROM session
|
||||||
|
WHERE userId = '%s'`, userId)
|
||||||
|
|
||||||
|
rows, err := database.DB.Query(queryStmt)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var session string
|
||||||
|
err = rows.Scan(&session)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sessions = append(sessions, session)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
10
go.mod
Normal file
10
go.mod
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module git.nutfactory.org/hoernschen/ActivityPub
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.0
|
||||||
|
github.com/gorilla/mux v1.8.0
|
||||||
|
github.com/lestrrat-go/backoff v1.0.0
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.4
|
||||||
|
)
|
55
go.sum
Normal file
55
go.sum
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
git.nutfactory.org/hoernschen/Matrix v0.0.0-20201012141628-da9196f38986 h1:kR6rjqDOGSfjWYQqTiy1HGLrAnbZvAwSkum4DMzj+5U=
|
||||||
|
git.nutfactory.org/hoernschen/Matrix v0.0.0-20201012141628-da9196f38986/go.mod h1:ewiecRT3bLcsnjp5kKuEXOrOGk/0nojoJFNnGaAyGXw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/cenkalti/backoff v1.1.0 h1:QnvVp8ikKCDWOsFheytRCoYWYPO/ObCTBGxT19Hc+yE=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||||
|
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.0 h1:c8LkOFQTzuO0WBM/ae5HdGQuZPfPxp7lqBRwQRm4fSc=
|
||||||
|
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/lestrrat-go/backoff v1.0.0 h1:nR+UgAhdhwfw2i+xznuHRlj81oMYa7u3lXun0xcsXUU=
|
||||||
|
github.com/lestrrat-go/backoff v1.0.0/go.mod h1:c7OnDlnHsFXbH1vyIS8+txH+THcc+QFlSQTrJVe4EIM=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.4 h1:4rQjbDxdu9fSgI/r3KN72G3c2goxknAqHHgPWWs8UlI=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.4/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/urfave/cli v1.22.4 h1:u7tSpNPPswAFymm8IehJhy4uJMlUuU/GmqSkvJ1InXA=
|
||||||
|
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d h1:/iIZNFGxc/a7C3yWjGcnboV+Tkc7mxr+p6fDztwoxuM=
|
||||||
|
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.5 h1:nI5egYTGJakVyOryqLs1cQO5dO0ksin5XXs2pspk75k=
|
||||||
|
honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
88
main.go
Normal file
88
main.go
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/config"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/actor"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/collection"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/object"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/user"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils/database"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils/router"
|
||||||
|
)
|
||||||
|
|
||||||
|
var keyPath = "./ssl.key"
|
||||||
|
var certPath = "./ssl.crt"
|
||||||
|
|
||||||
|
var routes = router.Routes{
|
||||||
|
// General
|
||||||
|
router.Route{"Reset", "GET", "/reset", config.Reset},
|
||||||
|
router.Route{"SetParams", "GET", "/setparams", config.SetParams},
|
||||||
|
|
||||||
|
// Users
|
||||||
|
router.Route{"Register", "POST", "/register", user.RegisterHandler},
|
||||||
|
|
||||||
|
// Actors
|
||||||
|
router.Route{"GetProfile", "GET", "/{actorName}/", actor.GetProfileHandler},
|
||||||
|
|
||||||
|
// Objects
|
||||||
|
router.Route{"GetPost", "GET", "/{actorName}/posts/{postId}", object.GetPostHandler},
|
||||||
|
|
||||||
|
// Collections
|
||||||
|
/*
|
||||||
|
router.Route{"GetInbox", "GET", "/{actorName}/inbox", collection.GetInboxHandler},
|
||||||
|
router.Route{"GetOutbox", "GET", "/{actorName}/outbox", collection.GetOutboxHandler},
|
||||||
|
router.Route{"GetFollowers", "GET", "/{actorName}/followers", collection.GetFollowersHandler},
|
||||||
|
router.Route{"GetFollowing", "GET", "/{actorName}/following", collection.GetFollowingHandler},
|
||||||
|
router.Route{"GetLiked", "GET", "/{actorName}/liked", collection.GetLikedHandler},
|
||||||
|
*/
|
||||||
|
router.Route{"PostInbox", "POST", "/{actorName}/inbox/", collection.PostInboxHandler},
|
||||||
|
router.Route{"PostOutbox", "POST", "/{actorName}/outbox/", collection.PostOutboxHandler},
|
||||||
|
/*
|
||||||
|
router.Route{"PostFollowers", "POST", "/{actorName}/followers", collection.PostFollowersHandler},
|
||||||
|
router.Route{"PostFollowing", "POST", "/{actorName}/following", collection.PostFollowingHandler},
|
||||||
|
router.Route{"PostLiked", "POST", "/{actorName}/liked", collection.PostLikedHandler},
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
config.Homeserver = "localhost"
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
config.Homeserver = os.Args[1]
|
||||||
|
}
|
||||||
|
log.Printf("Start homeserver on name %s", config.Homeserver)
|
||||||
|
|
||||||
|
/*
|
||||||
|
if err := device.InitServerSigningKey(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
config.VerifyKeys = make(map[string]map[string][]byte)
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
config.BackoffPolicy = backoff.NewExponential(
|
||||||
|
backoff.WithInterval(500*time.Millisecond),
|
||||||
|
backoff.WithMaxRetries(16),
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
os.Remove("sqlite.db")
|
||||||
|
|
||||||
|
if err := database.InitDB("sqlite.db"); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer database.DB.Close()
|
||||||
|
|
||||||
|
config.SetDefaultParams()
|
||||||
|
|
||||||
|
router := router.NewRouter(routes)
|
||||||
|
|
||||||
|
httpErr := http.ListenAndServeTLS(":443", certPath, keyPath, router)
|
||||||
|
if httpErr != nil {
|
||||||
|
log.Fatal(httpErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
sqlite.db
Normal file
BIN
sqlite.db
Normal file
Binary file not shown.
24
ssl.crt
Normal file
24
ssl.crt
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEGDCCAwCgAwIBAgIJANNCajLtjHULMA0GCSqGSIb3DQEBCwUAMIGgMQswCQYD
|
||||||
|
VQQGEwJERTEWMBQGA1UECAwNTmllZGVyc2FjaHNlbjERMA8GA1UEBwwIQnJhbXNj
|
||||||
|
aGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UEAwwK
|
||||||
|
aG9lcm5zY2hlbjEuMCwGCSqGSIb3DQEJARYfanVsaWFuLmhvZXJuc2NoZW1leWVy
|
||||||
|
QGdtYWlsLmNvbTAeFw0xODA4MTAxMzQ0MDJaFw0yODA4MDcxMzQ0MDJaMIGgMQsw
|
||||||
|
CQYDVQQGEwJERTEWMBQGA1UECAwNTmllZGVyc2FjaHNlbjERMA8GA1UEBwwIQnJh
|
||||||
|
bXNjaGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDETMBEGA1UE
|
||||||
|
AwwKaG9lcm5zY2hlbjEuMCwGCSqGSIb3DQEJARYfanVsaWFuLmhvZXJuc2NoZW1l
|
||||||
|
eWVyQGdtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL7O
|
||||||
|
m1fevM12EI2vP+4h7E+FpOXNVzTaG7dK1m65NTU6qgf5EV/My92S6xn0OVWVYtQ+
|
||||||
|
VqiXmGlm8MK17ssu2+6DK7NKv6PqNBXvVacEOmBElL9F4NHR/s5XBR2AyqnWWy8D
|
||||||
|
EnhZkm1rEaEYe3Nb+savnZfjkBKKOGGuQykMbeFziw6Ba57n3uZ8kgOqb/2koWyq
|
||||||
|
QDqmlpfCaUD328MyuluZkc3UVdv7kQzRqwWI4QmXNsr2gbJv/lCmte7rcKoSMDyD
|
||||||
|
xDsN/JbnN45k0ct3Ix4aCTnS9bQv0qgrEXREbnB5xkV+9SaW1GgQewFx5R8Miebu
|
||||||
|
EZsCdSNui2CoeoBHqJMCAwEAAaNTMFEwHQYDVR0OBBYEFNAdK12auACumWcsOCsS
|
||||||
|
sEvwZXS+MB8GA1UdIwQYMBaAFNAdK12auACumWcsOCsSsEvwZXS+MA8GA1UdEwEB
|
||||||
|
/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBALnNSNiMympZcFjhyO8uTxfPQK4x
|
||||||
|
2XdWPiGV+1N/zOn9LXfB5O2NY0g4cfuo9pZLpXMCF06qbc4yJtuqr2urCUhlVoYn
|
||||||
|
FzKJTVMMKP90XzC0hSMic/kTh+N5pKDlhGhrAmjktHs+4GOqi7oY1SIrvo0KWM3s
|
||||||
|
KpaOAJYeZNg44RisbRz1JDOoqpjb7bipF6oaNogC8uhRj3qJ1nkT2UlZjdZB5at2
|
||||||
|
k1hFkQi1SjJxSg4+fkHVwY/wxzCfYmk4QHt2Zc8wWLpDM6TOePX/eXenphSs0v7P
|
||||||
|
DO9gVD45i612oiBSJ9UtjHLwZrLm008SglZTudtlz6EDYhCAsSmNZKppaPs=
|
||||||
|
-----END CERTIFICATE-----
|
27
ssl.key
Normal file
27
ssl.key
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEpQIBAAKCAQEAvs6bV968zXYQja8/7iHsT4Wk5c1XNNobt0rWbrk1NTqqB/kR
|
||||||
|
X8zL3ZLrGfQ5VZVi1D5WqJeYaWbwwrXuyy7b7oMrs0q/o+o0Fe9VpwQ6YESUv0Xg
|
||||||
|
0dH+zlcFHYDKqdZbLwMSeFmSbWsRoRh7c1v6xq+dl+OQEoo4Ya5DKQxt4XOLDoFr
|
||||||
|
nufe5nySA6pv/aShbKpAOqaWl8JpQPfbwzK6W5mRzdRV2/uRDNGrBYjhCZc2yvaB
|
||||||
|
sm/+UKa17utwqhIwPIPEOw38luc3jmTRy3cjHhoJOdL1tC/SqCsRdERucHnGRX71
|
||||||
|
JpbUaBB7AXHlHwyJ5u4RmwJ1I26LYKh6gEeokwIDAQABAoIBAQC7S72K7JZyLIGl
|
||||||
|
QrDDhUMc8DfkZ8NBmxN3wZtpxp2nKXW8K83VNweq8UucB3K8Qs5nPuX7ygsO88BD
|
||||||
|
sSi9A7tZjiK4dRhWw0/rdCqkrm7LDqbgdqxv6e1wCFV6F3FYc5TAOgjIYExu4ZnF
|
||||||
|
g22y2Ef6/mn4raU/vbQIlnFQeuXlVb4V5UxJRFyOd5Bd270qIE2Y2uDrNjAjaPzz
|
||||||
|
H3Vdmr39XvTAoXi5Z+ANwPZV+haVWMrTSW0Qtk3crNnO/GggDv/ugAL/UurNsm6r
|
||||||
|
B1IEVyqFf4inkugDnajkszVoHfoVMhyf91J3R5u3ZltDElXizamQc/+Pr+r6wvwf
|
||||||
|
gnMM69kRAoGBAOt7tuhXzP/wLTljcJoord8/eSrElPxtdX47/0P7km0CJFLyrmsa
|
||||||
|
PasyhpadggVHwtj3VdOinrY/2B/71fi4flMP/HUFvtwD5ViWODE6bbVwijZsyEH/
|
||||||
|
wPM594QG9yuVZ0za+M+pQdqnMxQRT79k4SLlR7CskzP5vhbOPIv8YCA5AoGBAM9u
|
||||||
|
a+/s8trHzErrSQlcpvrvMcj9BLUek5tlEH/Rx/6WnSmfnnzu6/Y3sbRgEPbDg+0E
|
||||||
|
jbl8dESs3nYxxldg02Mw2bWRyodM7cYBNCPifqutvJdfMxxSWH+ayVBAA7VxQzsY
|
||||||
|
GDOK4KlC+siTSYUY8PJ9FM+6RTy4F8oVbwmS5jcrAoGBAK4prusC3SzCH0Cdqk9q
|
||||||
|
HMbL9DrMcACOmGKHz1EhhHe5KNJsiNHP86Jl2SMWVW7AV30O2VyQnt/eMmPdZ7Dw
|
||||||
|
CwY2AZsvZ6zj+MFfQSovs6qJFMASDr65gKSjz8vHNxH2CxPNtE4qOfmUxfNmplvB
|
||||||
|
Kb4cY7xotuqvIIdPe3pxa0sJAoGBAMflLXdE7LQRHrqECxpOg0wG/f8mdUbldHGn
|
||||||
|
70J+MzEQi9v0ypKy3AmmmkWs3iwvNg9O+BTr7k/QF4Hnba/+yzcneGYVXQsOA4Vw
|
||||||
|
24JJXrCq+LcXMvX0FPzDeYUwa2KLB7MHASuKhf4XYf2wkoUFCA1mpIuageaFscc4
|
||||||
|
6IxdWCWJAoGAXXk8bBRj0YksNCP41KVmxLqoky+vd115BjBxmiEK8K9NK1eFUGZe
|
||||||
|
K8/FeX3JmFrxAS+N+LeP+XVgyF1triDZ0ix8gn2bSY3skZHsUw62B6w7xWXzqgx4
|
||||||
|
rZM+GfS/QY2N9ubqze1m/vROSf65iHakZf+mxE+uf2BCi1WOxp7KARE=
|
||||||
|
-----END RSA PRIVATE KEY-----
|
117
utils/database/databaseConnector.go
Normal file
117
utils/database/databaseConnector.go
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DB *sql.DB
|
||||||
|
|
||||||
|
// TODO: Change DB Structure
|
||||||
|
func InitDB(filepath string) (err error) {
|
||||||
|
DB, err = sql.Open("sqlite3", filepath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if DB == nil {
|
||||||
|
panic("DB couldn't be initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
handleError(initObjectTable())
|
||||||
|
handleError(initCollectionTable())
|
||||||
|
handleError(initActorTable())
|
||||||
|
handleError(initUserTable())
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initObjectTable() (err error) {
|
||||||
|
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS object (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
type TEXT,
|
||||||
|
attributedTo TEXT,
|
||||||
|
content TEXT,
|
||||||
|
published INTEGER,
|
||||||
|
toActor TEXT
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statement.Exec()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initCollectionTable() (err error) {
|
||||||
|
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS collection (
|
||||||
|
id TEXT PRIMARY KEY
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statement.Exec()
|
||||||
|
|
||||||
|
statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS collectionObject (
|
||||||
|
collectionId TEXT,
|
||||||
|
objectId TEXT,
|
||||||
|
PRIMARY KEY (collectionId, objectId)
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statement.Exec()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initActorTable() (err error) {
|
||||||
|
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS actor (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
type TEXT,
|
||||||
|
name TEXT,
|
||||||
|
preferredUsername TEXT,
|
||||||
|
summary TEXT,
|
||||||
|
inbox TEXT,
|
||||||
|
outbox TEXT,
|
||||||
|
followers TEXT,
|
||||||
|
following TEXT,
|
||||||
|
liked TEXT
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statement.Exec()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initUserTable() (err error) {
|
||||||
|
statement, err := DB.Prepare(`CREATE TABLE IF NOT EXISTS user (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
password TEXT,
|
||||||
|
actor TEXT
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statement.Exec()
|
||||||
|
|
||||||
|
statement, err = DB.Prepare(`CREATE TABLE IF NOT EXISTS session (
|
||||||
|
token TEXT PRIMARY KEY,
|
||||||
|
userId TEXT
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statement.Exec()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Could not execute Database Query: %s", err))
|
||||||
|
}
|
||||||
|
}
|
76
utils/encryptionService.go
Normal file
76
utils/encryptionService.go
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CreateToken() (err error, token string) {
|
||||||
|
b := make([]byte, 8)
|
||||||
|
_, err = rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token = fmt.Sprintf("%x", b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUUID() (err error, uuid string) {
|
||||||
|
b := make([]byte, 16)
|
||||||
|
_, err = rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uuid = fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if needed
|
||||||
|
func Hash(s []byte) (err error, hashString string) {
|
||||||
|
h := sha256.New()
|
||||||
|
_, err = h.Write(s)
|
||||||
|
if nil != err {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hash := h.Sum(nil)
|
||||||
|
hashString = base64.StdEncoding.EncodeToString(hash)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Signing Mechanism?
|
||||||
|
func GenerateKeyPair() (publicKey ed25519.PublicKey, privateKey ed25519.PrivateKey, err error) {
|
||||||
|
publicKey, privateKey, err = ed25519.GenerateKey(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Sign(message []byte) string {
|
||||||
|
signatureBytes := ed25519.Sign(config.PrivateKey, message)
|
||||||
|
return base64.RawStdEncoding.EncodeToString(signatureBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignContent(content []byte) (signatures map[string]map[string]string) {
|
||||||
|
if !config.Signing {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
signatures = make(map[string]map[string]string)
|
||||||
|
signatures[config.Homeserver] = make(map[string]string)
|
||||||
|
signatures[config.Homeserver][config.KeyId] = Sign(content)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func VerifySignature(publicKey []byte, message []byte, signature string) bool {
|
||||||
|
signatureBytes, err := base64.RawStdEncoding.DecodeString(signature)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ed25519.Verify(config.PublicKey, message, signatureBytes)
|
||||||
|
return true
|
||||||
|
}
|
23
utils/logger.go
Normal file
23
utils/logger.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func APILogger(inner http.Handler, name string) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
inner.ServeHTTP(w, r)
|
||||||
|
|
||||||
|
log.Printf(
|
||||||
|
"%s\t%s\t%s\t%s",
|
||||||
|
r.Method,
|
||||||
|
r.RequestURI,
|
||||||
|
name,
|
||||||
|
time.Since(start),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
89
utils/requestChecker.go
Normal file
89
utils/requestChecker.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorResponse struct {
|
||||||
|
ErrorCode string `json:"errcode,omitempty"`
|
||||||
|
ErrorMessage string `json:"error,omitempty"`
|
||||||
|
RetryTime int `json:"retry_after_ms,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckRequest(r *http.Request) (err error) {
|
||||||
|
if !strings.Contains(r.Header.Get("Content-Type"), "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"") {
|
||||||
|
err = errors.New("Content Type not JSON")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetContentTypeString() string {
|
||||||
|
return "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultContext() string {
|
||||||
|
return "https://www.w3.org/ns/activitystreams"
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateProfileUrl(userId string) string {
|
||||||
|
return fmt.Sprintf("%s://%s/%s/", config.HttpString, config.Homeserver, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateInboxUrl(userId string) string {
|
||||||
|
return fmt.Sprintf("%s://%s/%s/inbox/", config.HttpString, config.Homeserver, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateOutboxUrl(userId string) string {
|
||||||
|
return fmt.Sprintf("%s://%s/%s/outbox/", config.HttpString, config.Homeserver, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateFollowersUrl(userId string) string {
|
||||||
|
return fmt.Sprintf("%s://%s/%s/followers/", config.HttpString, config.Homeserver, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateFollowingUrl(userId string) string {
|
||||||
|
return fmt.Sprintf("%s://%s/%s/following/", config.HttpString, config.Homeserver, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateLikedUrl(userId string) string {
|
||||||
|
return fmt.Sprintf("%s://%s/%s/liked/", config.HttpString, config.Homeserver, userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAccessToken(r *http.Request) (token string, err error) {
|
||||||
|
token = r.URL.Query().Get("access_token")
|
||||||
|
if token == "" {
|
||||||
|
token = r.Header.Get("Authorization")
|
||||||
|
if token == "" || !strings.Contains(token, "Bearer") {
|
||||||
|
err = errors.New("Missing Token")
|
||||||
|
} else {
|
||||||
|
token = strings.Split(token, " ")[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleHTTPError(res *http.Response) (err error) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(res.Body)
|
||||||
|
err = errors.New(fmt.Sprintf("%s (%s)", buf.String(), res.Status))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveDuplicates(array []string) []string {
|
||||||
|
keys := make(map[string]bool)
|
||||||
|
list := []string{}
|
||||||
|
|
||||||
|
for _, entry := range array {
|
||||||
|
if _, value := keys[entry]; !value {
|
||||||
|
keys[entry] = true
|
||||||
|
list = append(list, entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
12
utils/router/route.go
Normal file
12
utils/router/route.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
type Route struct {
|
||||||
|
Name string
|
||||||
|
Method string
|
||||||
|
Pattern string
|
||||||
|
HandlerFunc http.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
type Routes []Route
|
27
utils/router/router.go
Normal file
27
utils/router/router.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils"
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRouter(routes Routes) *mux.Router {
|
||||||
|
router := mux.NewRouter().StrictSlash(true)
|
||||||
|
|
||||||
|
for _, route := range routes {
|
||||||
|
var handler http.Handler
|
||||||
|
handler = route.HandlerFunc
|
||||||
|
handler = utils.APILogger(handler, route.Name)
|
||||||
|
|
||||||
|
router.
|
||||||
|
Methods(route.Method).
|
||||||
|
Path(route.Pattern).
|
||||||
|
Name(route.Name).
|
||||||
|
Handler(handler)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return router
|
||||||
|
}
|
516
workloadGenerator.go
Normal file
516
workloadGenerator.go
Normal file
|
@ -0,0 +1,516 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/csv"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/config"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/activity"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/object"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/entities/user"
|
||||||
|
"git.nutfactory.org/hoernschen/ActivityPub/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var BaseLineTest = false
|
||||||
|
|
||||||
|
var systemParamsIndex = 0
|
||||||
|
|
||||||
|
type SystemParams struct {
|
||||||
|
Id string
|
||||||
|
BytesToSend int
|
||||||
|
MessagesPerSecond float32
|
||||||
|
Distribution map[string][]string
|
||||||
|
Packetloss int
|
||||||
|
MinutesNotAvailable int
|
||||||
|
Consensus bool
|
||||||
|
AuthentificationCheck bool
|
||||||
|
Signing bool
|
||||||
|
Encryption bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpString string
|
||||||
|
var users = []map[string][]string{
|
||||||
|
map[string][]string{
|
||||||
|
"143.93.38.208": []string{
|
||||||
|
"user1",
|
||||||
|
"user2",
|
||||||
|
"user3",
|
||||||
|
"user4",
|
||||||
|
"user5",
|
||||||
|
"user6",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string][]string{
|
||||||
|
"143.93.38.207": []string{
|
||||||
|
"user1",
|
||||||
|
"user2",
|
||||||
|
"user3",
|
||||||
|
"user4",
|
||||||
|
},
|
||||||
|
"143.93.38.208": []string{
|
||||||
|
"user1",
|
||||||
|
},
|
||||||
|
"143.93.38.209": []string{
|
||||||
|
"user1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string][]string{
|
||||||
|
"143.93.38.207": []string{
|
||||||
|
"user1",
|
||||||
|
"user2",
|
||||||
|
"user3",
|
||||||
|
"user4",
|
||||||
|
},
|
||||||
|
"143.93.38.208": []string{
|
||||||
|
"user1",
|
||||||
|
"user2",
|
||||||
|
"user3",
|
||||||
|
"user4",
|
||||||
|
},
|
||||||
|
"143.93.38.209": []string{
|
||||||
|
"user1",
|
||||||
|
"user2",
|
||||||
|
"user3",
|
||||||
|
"user4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var servers = []string{
|
||||||
|
"143.93.38.207",
|
||||||
|
"143.93.38.208",
|
||||||
|
"143.93.38.209",
|
||||||
|
}
|
||||||
|
|
||||||
|
var systemParams = []SystemParams{
|
||||||
|
SystemParams{
|
||||||
|
Id: "111110000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "011110000",
|
||||||
|
BytesToSend: 8,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "211110000",
|
||||||
|
BytesToSend: 512,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "101110000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 0.1,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "121110000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 10.0,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "110110000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[0],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "112110000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[2],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "111010000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 0,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
SystemParams{
|
||||||
|
Id: "111210000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 20,
|
||||||
|
MinutesNotAvailable: 0,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
SystemParams{
|
||||||
|
Id: "111120000",
|
||||||
|
BytesToSend: 280,
|
||||||
|
MessagesPerSecond: 1.0,
|
||||||
|
Distribution: users[1],
|
||||||
|
Packetloss: 1,
|
||||||
|
MinutesNotAvailable: 1,
|
||||||
|
Consensus: true,
|
||||||
|
AuthentificationCheck: true,
|
||||||
|
Signing: true,
|
||||||
|
Encryption: true,
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
var userIds []string
|
||||||
|
var accessTokens map[string]string
|
||||||
|
var collectionId string
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
httpString = "https"
|
||||||
|
iteration := 0
|
||||||
|
if BaseLineTest {
|
||||||
|
file, err := os.Create(fmt.Sprintf("%s Baseline Measurement ActivityPub.csv", strconv.FormatInt(time.Now().Unix(), 10)))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error Creating CSV: %s", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
writer := csv.NewWriter(file)
|
||||||
|
defer writer.Flush()
|
||||||
|
err = writer.Write([]string{"Iteration", "Start", "End"})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Writing CSV: %s", err)
|
||||||
|
}
|
||||||
|
for iteration < 10 {
|
||||||
|
start := time.Now().Unix()
|
||||||
|
time.Sleep(2 * time.Minute)
|
||||||
|
end := time.Now().Unix()
|
||||||
|
iteration++
|
||||||
|
err = writer.Write([]string{strconv.Itoa(iteration), strconv.FormatInt(start, 10), strconv.FormatInt(end, 10)})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Writing CSV: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for _, systemParam := range systemParams {
|
||||||
|
file, err := os.Create(fmt.Sprintf("%s %s Measurement ActivityPub.csv", strconv.FormatInt(time.Now().Unix(), 10), systemParam.Id))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error Creating CSV: %s", err)
|
||||||
|
}
|
||||||
|
writer := csv.NewWriter(file)
|
||||||
|
err = writer.Write([]string{"Iteration", "Start", "End", "Actions Send"})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Writing CSV: %s", err)
|
||||||
|
}
|
||||||
|
millisecondsToWait := 1000 / systemParam.MessagesPerSecond
|
||||||
|
iteration = 0
|
||||||
|
for iteration < 30 {
|
||||||
|
err := setUp(systemParam)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in SetUp: %s", err)
|
||||||
|
}
|
||||||
|
b := make([]byte, systemParam.BytesToSend)
|
||||||
|
_, err = rand.Read(b)
|
||||||
|
message := base64.RawStdEncoding.EncodeToString(b)
|
||||||
|
start := time.Now()
|
||||||
|
end := start.Add(2 * time.Minute).Unix()
|
||||||
|
log.Printf("Id: %s - Iteration: %s - Start: %s - End: %s", systemParam.Id, strconv.Itoa(iteration), strconv.FormatInt(start.Unix(), 10), strconv.FormatInt(end, 10))
|
||||||
|
actionsSend := 0
|
||||||
|
for time.Now().Unix() < end {
|
||||||
|
time.Sleep(time.Duration(millisecondsToWait) * time.Millisecond)
|
||||||
|
|
||||||
|
err = sendMessage(message)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error sending Message %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actionsSend++
|
||||||
|
}
|
||||||
|
iteration++
|
||||||
|
err = writer.Write([]string{strconv.Itoa(iteration), strconv.FormatInt(start.Unix(), 10), strconv.FormatInt(end, 10), strconv.Itoa(actionsSend)})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Writing CSV: %s", err)
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
err = reset()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Error in Reset: %s", err)
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
writer.Flush()
|
||||||
|
file.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendMessage(message string) (err error) {
|
||||||
|
userId := userIds[0]
|
||||||
|
accessToken := accessTokens[userId]
|
||||||
|
requestUrl := fmt.Sprintf("%soutbox/", userId)
|
||||||
|
messageObject := object.Object{
|
||||||
|
Type: "Note",
|
||||||
|
Content: message,
|
||||||
|
}
|
||||||
|
reqBody, err := json.Marshal(messageObject)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &http.Client{}
|
||||||
|
var req *http.Request
|
||||||
|
req, err = http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(reqBody))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header["Content-Type"] = []string{utils.GetContentTypeString()}
|
||||||
|
req.Header["Authorization"] = []string{fmt.Sprintf("Bearer %s", accessToken)}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
handleError(res)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setParams(systemParamsToUse SystemParams) (err error) {
|
||||||
|
serverNotAvailableIndex := 1
|
||||||
|
for i, server := range servers {
|
||||||
|
minutesNotAvailable := 0
|
||||||
|
if serverNotAvailableIndex == i {
|
||||||
|
minutesNotAvailable = systemParamsToUse.MinutesNotAvailable
|
||||||
|
}
|
||||||
|
requestUrl := fmt.Sprintf("%s://%s/setparams", httpString, server)
|
||||||
|
request := config.SetParamBody{
|
||||||
|
Packetloss: systemParamsToUse.Packetloss,
|
||||||
|
UnavailableTill: time.Now().Add(time.Duration(minutesNotAvailable) * time.Minute).Unix(),
|
||||||
|
Consensus: systemParamsToUse.Consensus,
|
||||||
|
AuthentificationCheck: systemParamsToUse.AuthentificationCheck,
|
||||||
|
Signing: systemParamsToUse.Signing,
|
||||||
|
Encryption: systemParamsToUse.Encryption,
|
||||||
|
}
|
||||||
|
var reqBody []byte
|
||||||
|
reqBody, err = json.Marshal(request)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &http.Client{Timeout: 2 * time.Second}
|
||||||
|
var req *http.Request
|
||||||
|
req, err = http.NewRequest(http.MethodGet, requestUrl, bytes.NewBuffer(reqBody))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header["Content-Type"] = []string{"application/json"}
|
||||||
|
var res *http.Response
|
||||||
|
res, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
handleError(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func setUp(systemParamsToUse SystemParams) (err error) {
|
||||||
|
accessTokens = make(map[string]string)
|
||||||
|
err = createUsers(systemParamsToUse.Distribution)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error in User-Creation: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow User
|
||||||
|
log.Printf("Userids: %s", userIds)
|
||||||
|
err = followUser(userIds[0], userIds[1:])
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error follow User: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setParams(systemParamsToUse)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset() (err error) {
|
||||||
|
userIds = []string{}
|
||||||
|
accessTokens = make(map[string]string)
|
||||||
|
for _, server := range servers {
|
||||||
|
requestUrl := fmt.Sprintf("%s://%s/reset", httpString, server)
|
||||||
|
client := &http.Client{Timeout: 2 * time.Second}
|
||||||
|
var req *http.Request
|
||||||
|
req, err = http.NewRequest(http.MethodGet, requestUrl, bytes.NewBuffer(nil))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header["Content-Type"] = []string{"application/json"}
|
||||||
|
var res *http.Response
|
||||||
|
res, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
handleError(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func followUser(userIdToFollow string, userIdsThatFollow []string) (err error) {
|
||||||
|
for _, userId := range userIdsThatFollow {
|
||||||
|
log.Printf("UserIdThatFollow: %s", userId)
|
||||||
|
accessToken := accessTokens[userId]
|
||||||
|
requestUrl := fmt.Sprintf("%soutbox/", userId)
|
||||||
|
followActivity := activity.Activity{
|
||||||
|
Type: "Follow",
|
||||||
|
Actor: userId,
|
||||||
|
To: userIdToFollow,
|
||||||
|
}
|
||||||
|
var reqBody []byte
|
||||||
|
reqBody, err = json.Marshal(followActivity)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &http.Client{Timeout: 2 * time.Second}
|
||||||
|
var req *http.Request
|
||||||
|
req, err = http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(reqBody))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header["Content-Type"] = []string{utils.GetContentTypeString()}
|
||||||
|
req.Header["Authorization"] = []string{fmt.Sprintf("Bearer %s", accessToken)}
|
||||||
|
var res *http.Response
|
||||||
|
res, err = client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
err = utils.HandleHTTPError(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//time.Sleep(time.Duration(1) * time.Second)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUsers(serverUserMap map[string][]string) (err error) {
|
||||||
|
log.Println("Create Users")
|
||||||
|
for server, usersToCreate := range serverUserMap {
|
||||||
|
for _, userToCreate := range usersToCreate {
|
||||||
|
var userId string
|
||||||
|
var accessToken string
|
||||||
|
userId, accessToken, err = createUser(userToCreate, server)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if userId != "" && accessToken != "" {
|
||||||
|
log.Printf("%s created - AccessToken: %s", userId, accessToken)
|
||||||
|
accessTokens[userId] = accessToken
|
||||||
|
userIds = append(userIds, userId)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(20) * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func createUser(userToCreate string, homeserver string) (userId string, accessToken string, err error) {
|
||||||
|
requestUrl := fmt.Sprintf("%s://%s/register", httpString, homeserver)
|
||||||
|
request := user.RegisterRequest{
|
||||||
|
Username: userToCreate,
|
||||||
|
Password: "password",
|
||||||
|
}
|
||||||
|
reqBody, err := json.Marshal(request)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := &http.Client{Timeout: 2 * time.Second}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, requestUrl, bytes.NewBuffer(reqBody))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.Header["Content-Type"] = []string{utils.GetContentTypeString()}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
err = utils.HandleHTTPError(res)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
response := user.RegisterResponse{}
|
||||||
|
decoder := json.NewDecoder(res.Body)
|
||||||
|
err = decoder.Decode(&response)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userId = response.Actor
|
||||||
|
accessToken = response.Token
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleError(res *http.Response) {
|
||||||
|
err := utils.HandleHTTPError(res)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("%s", err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue