summaryrefslogtreecommitdiff
path: root/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'server.go')
-rw-r--r--server.go167
1 files changed, 167 insertions, 0 deletions
diff --git a/server.go b/server.go
new file mode 100644
index 0000000..a241f05
--- /dev/null
+++ b/server.go
@@ -0,0 +1,167 @@
+// Copyright 2020, Shulhan <m.shulhan@gmail.com>. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package kbbi
+
+import (
+ "fmt"
+ "log"
+ stdhttp "net/http"
+ "strings"
+ "time"
+
+ "github.com/shuLhan/share/lib/http"
+)
+
+const (
+ defListen = ":3394"
+ emptyResponse = "{}"
+)
+
+//
+// Server for KBBI with caching and spell checking functionalities.
+//
+type Server struct {
+ http *http.Server
+ kamus *kamusCache
+
+ // The client that forward request to official KBBI server.
+ forwardc *directClient
+
+ // If offline is true and the word definition is not found on cache,
+ // the server will not forward request to official KBBI server.
+ offline bool
+}
+
+//
+// NewServer create and initialize the server.
+//
+func NewServer() (server *Server, err error) {
+ opts := &http.ServerOptions{
+ Address: defListen,
+ }
+
+ server = &Server{}
+
+ server.kamus, err = newKamusCache("")
+ if err != nil {
+ return nil, fmt.Errorf("http.NewServer: %w", err)
+ }
+
+ server.http, err = http.NewServer(opts)
+ if err != nil {
+ return nil, fmt.Errorf("http.NewServer: %w", err)
+ }
+
+ server.forwardc, err = newDirectClient(nil)
+ if err != nil {
+ return nil, fmt.Errorf("http.NewServer: %w", err)
+ }
+
+ err = server.registerEndpoints()
+ if err != nil {
+ return nil, fmt.Errorf("http.NewServer: %w", err)
+ }
+
+ return server, nil
+}
+
+//
+// Start the HTTP server.
+//
+func (server *Server) Start() (err error) {
+ go server.dumpCache()
+
+ return server.http.Start()
+}
+
+//
+// dumpCache periodically dump the cache to file to be loaded later.
+//
+func (server *Server) dumpCache() {
+ ticker := time.NewTicker(1 * time.Hour)
+
+ for range ticker.C {
+ if !server.kamus.isChanging() {
+ continue
+ }
+ err := server.kamus.store()
+ if err != nil {
+ log.Println("server.dumpCache: ", err)
+ }
+ }
+}
+
+func (server *Server) handleDefinisi(
+ httpRes stdhttp.ResponseWriter,
+ httpReq *stdhttp.Request,
+ reqBody []byte,
+) (resBody []byte, err error) {
+ paramKata := httpReq.Form.Get(paramNameKata)
+ if len(paramKata) == 0 {
+ return []byte(emptyResponse), nil
+ }
+
+ inputs := strings.Split(paramKata, ",")
+ res := make(DefinisiResponse, len(inputs))
+
+ for _, in := range inputs {
+ in = strings.TrimSpace(in)
+ if len(in) == 0 {
+ continue
+ }
+
+ kata := server.kamus.get(in)
+ if kata != nil {
+ res[in] = kata
+ continue
+ }
+
+ if server.offline {
+ continue
+ }
+
+ // The word does not exist in cache, retrieve it from official
+ // website.
+ fwRes, err := server.forwardc.CariDefinisi([]string{in})
+ if err != nil {
+ kata.err = err
+ continue
+ }
+
+ for k, v := range fwRes {
+ if k == in {
+ res[in] = v
+ server.kamus.set(k, v)
+ delete(fwRes, k)
+ break
+ }
+ }
+ }
+
+ if len(res) == 0 {
+ return []byte(emptyResponse), nil
+ }
+
+ resBody, err = res.pack()
+ if err != nil {
+ return nil, err
+ }
+
+ return resBody, nil
+}
+
+//
+// registerEndpoints register the API endpoints.
+//
+func (server *Server) registerEndpoints() (err error) {
+ epDefinisi := &http.Endpoint{
+ Method: http.RequestMethodGet,
+ Path: pathAPIDefinisi,
+ RequestType: http.RequestTypeQuery,
+ ResponseType: http.ResponseTypeJSON,
+ Call: server.handleDefinisi,
+ }
+ return server.http.RegisterEndpoint(epDefinisi)
+}