1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
package main
import (
"crypto/sha256"
"fmt"
"log"
"net/http"
"strings"
"github.com/gorilla/mux"
)
func apiErrCheck(err error, r *http.Request) {
if err != nil {
uip := getIPFromCtx(r.Context())
log.Printf("*** %v :: %v %v :: %v\n", uip, r.Method, r.URL, err.Error())
}
}
// Takes the output of queries and formats it for
// an HTTP response. Iterates over the string slice,
// appending each entry to a byte slice, and adding
// newlines where appropriate.
func parseQueryOut(out []string) []byte {
var data []byte
for i, e := range out {
data = append(data, []byte(e)...)
if !strings.HasSuffix(e, "\n") && i != len(out)-1 {
data = append(data, byte('\n'))
}
}
return data
}
// Removes duplicate statuses from query output
func uniq(str []string) []string {
keys := make(map[string]bool)
out := []string{}
for _, e := range str {
if _, ok := keys[e]; !ok {
keys[e] = true
out = append(out, e)
}
}
return out
}
// apiUserQuery is called via apiEndpointHandler when
// the endpoint is "users" and r.FormValue("q") is not empty.
// It queries the registry cache for users or user URLs
// matching the term supplied via r.FormValue("q")
func apiEndpointQuery(w http.ResponseWriter, r *http.Request) error {
query := r.FormValue("q")
urls := r.FormValue("url")
var out []string
var err error
vars := mux.Vars(r)
endpoint := vars["endpoint"]
// Handle user URL queries first, then nickname queries.
// Concatenate both outputs if they're both set.
// Also handle mention queries and status queries.
// If we made it this far and 'default' is matched,
// something went very wrong.
switch endpoint {
case "users":
var out2 []string
if query != "" {
out, err = twtxtCache.QueryUser(query)
apiErrCheck(err, r)
}
if urls != "" {
out2, err = twtxtCache.QueryUser(urls)
apiErrCheck(err, r)
}
out = append(out, out2...)
out = uniq(out)
case "mentions":
if urls == "" {
return fmt.Errorf("missing URL in mention query")
}
urls += ">"
out, err = twtxtCache.QueryInStatus(urls)
apiErrCheck(err, r)
case "tweets":
query = strings.ToLower(query)
out, err = twtxtCache.QueryInStatus(query)
apiErrCheck(err, r)
query = strings.Title(query)
out2, err := twtxtCache.QueryInStatus(query)
apiErrCheck(err, r)
query = strings.ToUpper(query)
out3, err := twtxtCache.QueryInStatus(query)
apiErrCheck(err, r)
out = append(out, out2...)
out = append(out, out3...)
out = uniq(out)
default:
return fmt.Errorf("endpoint query, no cases match")
}
data := parseQueryOut(out)
etag := fmt.Sprintf("%x", sha256.Sum256(data))
w.Header().Set("ETag", etag)
w.Header().Set("Content-Type", txtutf8)
_, err = w.Write(data)
return err
}
|