Skip to content

Commit

Permalink
Merge pull request #70 from parkr/log-path-from-js
Browse files Browse the repository at this point in the history
pingv2: create a mechanism to send an XHR request with full path
  • Loading branch information
parkr authored Apr 12, 2024
2 parents fb91bb2 + b03804b commit 18ff96e
Show file tree
Hide file tree
Showing 11 changed files with 568 additions and 79 deletions.
2 changes: 1 addition & 1 deletion cors.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (c *corsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}

func addCorsHeaders(w http.ResponseWriter, r *http.Request) {
w.Header().Set(CorsAccessControlAllowMethodsHeaderName, "GET")
w.Header().Set(CorsAccessControlAllowMethodsHeaderName, "GET, POST")
if allowCORSOrigin(r.Header.Get("Origin")) {
w.Header().Set(CorsAccessControlAllowOriginHeaderName, r.Header.Get("Origin"))
} else if allowCORSOrigin(r.Referer()) {
Expand Down
8 changes: 4 additions & 4 deletions cors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestAddCorsHeaders_OriginRequestHeader_Success(t *testing.T) {

addCorsHeaders(recorder, request)

expectedAllowedMethods := "GET"
expectedAllowedMethods := "GET, POST"
actual := recorder.Header().Get(CorsAccessControlAllowMethodsHeaderName)
if actual != expectedAllowedMethods {
t.Errorf("expected %s: %v, got: %v", CorsAccessControlAllowMethodsHeaderName, expectedAllowedMethods, actual)
Expand All @@ -43,7 +43,7 @@ func TestAddCorsHeaders_RefererRequestHeader_Success(t *testing.T) {

addCorsHeaders(recorder, request)

expectedAllowedMethods := "GET"
expectedAllowedMethods := "GET, POST"
actual := recorder.Header().Get(CorsAccessControlAllowMethodsHeaderName)
if actual != expectedAllowedMethods {
t.Errorf("expected %s: %v, got: %v", CorsAccessControlAllowMethodsHeaderName, expectedAllowedMethods, actual)
Expand All @@ -68,7 +68,7 @@ func TestAddCorsHeaders_NeitherRequestHeader_Success(t *testing.T) {

addCorsHeaders(recorder, request)

expectedAllowedMethods := "GET"
expectedAllowedMethods := "GET, POST"
actual := recorder.Header().Get(CorsAccessControlAllowMethodsHeaderName)
if actual != expectedAllowedMethods {
t.Errorf("expected %s: %v, got: %v", CorsAccessControlAllowMethodsHeaderName, expectedAllowedMethods, actual)
Expand All @@ -93,7 +93,7 @@ func TestAddCorsHeaders_UnparseableRequestHeader_Success(t *testing.T) {

addCorsHeaders(recorder, request)

expectedAllowedMethods := "GET"
expectedAllowedMethods := "GET, POST"
actual := recorder.Header().Get(CorsAccessControlAllowMethodsHeaderName)
if actual != expectedAllowedMethods {
t.Errorf("expected %s: %v, got: %v", CorsAccessControlAllowMethodsHeaderName, expectedAllowedMethods, actual)
Expand Down
16 changes: 15 additions & 1 deletion database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package database

import (
"fmt"
"log"
"os"

"github.com/jmoiron/sqlx"
Expand All @@ -22,6 +23,7 @@ const (
);`
checkIfSchemaExists = `SELECT COUNT(*) as does_exist FROM sqlite_master WHERE type='table' AND name='visits';`
insertVisit = `INSERT INTO visits (ip, host, path, user_agent, created_at) VALUES (:ip, :host, :path, :user_agent, :created_at)`
selectVisit = `SELECT ip, host, path, user_agent, created_at FROM visits WHERE id = ?`
)

type TableCheck struct {
Expand All @@ -48,6 +50,16 @@ func Initialize() (*sqlx.DB, error) {
return db, nil
}

func Get(db *sqlx.DB, id int) (Visit, error) {
row := db.QueryRow(selectVisit, id)
if row.Err() != nil {
return Visit{}, row.Err()
}
visit := Visit{}
err := row.Scan(&visit.IP, &visit.Host, &visit.Path, &visit.UserAgent, &visit.CreatedAt)
return visit, err
}

type Visit struct {
IP string `db:"ip"`
Host string `db:"host"`
Expand All @@ -61,6 +73,8 @@ func (v *Visit) String() string {
}

func (v *Visit) Save(db *sqlx.DB) error {
_, err := db.NamedExec(insertVisit, v)
result, err := db.NamedExec(insertVisit, v)
lastInsertedId, _ := result.LastInsertId()
log.Println("inserted id", lastInsertedId)
return err
}
14 changes: 14 additions & 0 deletions dnt/do_not_track.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dnt

import "net/http"

const DoNotTrackHeaderName = "DNT"
const DoNotTrackHeaderValue = "1"

func RequestsDoNotTrack(r *http.Request) bool {
return r.Header.Get(DoNotTrackHeaderName) == DoNotTrackHeaderValue
}

func SetDoNotTrack(w http.ResponseWriter) {
w.Header().Set(DoNotTrackHeaderName, DoNotTrackHeaderValue)
}
10 changes: 5 additions & 5 deletions do_not_track_test.go → dnt/do_not_track_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package main
package dnt

import (
"net/http"
Expand All @@ -17,7 +17,7 @@ func TestRequestsDoNotTrack(t *testing.T) {
"Dnt": {DoNotTrackHeaderValue},
})}

if !requestsDoNotTrack(req) {
if !RequestsDoNotTrack(req) {
t.Fatalf("expected DNT header to be respected, headers: %+v, %s header: %+v",
req.Header,
textproto.CanonicalMIMEHeaderKey(DoNotTrackHeaderName),
Expand All @@ -27,16 +27,16 @@ func TestRequestsDoNotTrack(t *testing.T) {
// Header is not set. We can track this request.
req = &http.Request{Header: map[string][]string{}}

if requestsDoNotTrack(req) {
if RequestsDoNotTrack(req) {
t.Fatalf("expected lack of DNT header to mean we can track")
}

// Header is not set correctly. We can track this request.
req = &http.Request{Header: map[string][]string{
"Dnt": []string{"!"},
"Dnt": {"!"},
}}

if requestsDoNotTrack(req) {
if RequestsDoNotTrack(req) {
t.Fatalf("expected value of DNT header to mean we can track")
}
}
10 changes: 0 additions & 10 deletions do_not_track.go

This file was deleted.

32 changes: 32 additions & 0 deletions jsv1/version1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package jsv1

import (
"fmt"
"net/http"
"strconv"

"github.com/parkr/ping/dnt"
)

const returnedJavaScript = "(function(){})();"
const lengthOfJavaScript = "17"

func Write(w http.ResponseWriter, code int) {
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/javascript")
w.Header().Set("Content-Length", lengthOfJavaScript)
fmt.Fprintf(w, returnedJavaScript)
}

func DoNotTrack(w http.ResponseWriter) {
Write(w, http.StatusOK)
dnt.SetDoNotTrack(w)
}

func Error(w http.ResponseWriter, code int, err string) {
w.WriteHeader(code)
content := fmt.Sprintf(`(function(){console.error("%s")})();`, err)
w.Header().Set("Content-Type", "application/javascript")
w.Header().Set("Content-Length", strconv.Itoa(len(content)))
fmt.Fprintf(w, content)
}
44 changes: 44 additions & 0 deletions jsv2/version2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package jsv2

import (
"fmt"
"net/http"
"strconv"
)

const returnedJavaScript = `
function logVisit(document) {
var httpRequest = new XMLHttpRequest();
httpRequest.onreadystatechange = () => {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status > 100 && httpRequest.status < 300) {
console.log("visit log result:", httpRequest.responseText)
} else {
console.error('There was a problem with the request.')
console.error(httpRequest.status, httpRequest.responseText, httpRequest)
}
}
};
const visitSearchParams = new URLSearchParams()
visitSearchParams.append('host', document.location.hostname)
visitSearchParams.append('path', document.location.pathname)
const visitURL = new URL('https://ping.parkermoo.re/submit.js')
visitURL.search = "?" + visitSearchParams.toString()
httpRequest.open('POST', visitURL.toString(), true);
httpRequest.send();
}
(function(){
document.addEventListener('readystatechange', (event) => {
if (document.readyState === 'complete') {
logVisit(document)
}
});
})()
`

func Write(w http.ResponseWriter, code int) {
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/javascript")
w.Header().Set("Content-Length", strconv.Itoa(len(returnedJavaScript)))
fmt.Fprintf(w, returnedJavaScript)
}
Loading

0 comments on commit 18ff96e

Please sign in to comment.