1// Copyright (c) 2013-2018 Couchbase, Inc.
2// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3// except in compliance with the License. You may obtain a copy of the License at
4//   http://www.apache.org/licenses/LICENSE-2.0
5// Unless required by applicable law or agreed to in writing, software distributed under the
6// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7// either express or implied. See the License for the specific language governing permissions
8// and limitations under the License.
9
10// admin server to handle admin and system messages.
11//
12// Example server {
13//      reqch  := make(chan adminport.Request)
14//      server := adminport.NewHTTPServer("projector", "localhost:9999", "/adminport", reqch)
15//      server.Register(&protobuf.RequestMessage{})
16//
17//      loop:
18//      for {
19//          select {
20//          case req, ok := <-reqch:
21//              if ok {
22//                  msg := req.GetMessage()
23//                  // interpret request and compose a response
24//                  respMsg := &protobuf.ResponseMessage{}
25//                  err := msg.Send(respMsg)
26//              } else {
27//                  break loop
28//              }
29//          }
30//      }
31// }
32
33// TODO: IMPORTANT:
34//  Go 1.3 is supposed to have graceful shutdown of http server.
35//  Refer https://code.google.com/p/go/issues/detail?id=4674
36
37package adminport
38
39import (
40	"fmt"
41	base "github.com/couchbase/goxdcr/base"
42	"github.com/couchbase/goxdcr/log"
43	"net/http"
44	"sync"
45)
46import _ "expvar"
47
48var logger_server *log.CommonLogger = log.NewLogger("HttpServer", log.DefaultLoggerContext)
49
50type httpServer struct {
51	mu        sync.RWMutex   // handle concurrent updates to this object
52	srv       *http.Server   // http server
53	urlPrefix string         // URL path prefix for adminport
54	reqch     chan<- Request // request channel back to application
55
56	logPrefix string
57}
58
59// NewHTTPServer creates an instance of admin-server. Start() will actually
60// start the server.
61func NewHTTPServer(name, connAddr, urlPrefix string, reqch chan<- Request, handler RequestHandler) Server {
62
63	s := &httpServer{
64		reqch:     reqch,
65		urlPrefix: urlPrefix,
66		logPrefix: fmt.Sprintf("[%s:%s]", name, connAddr),
67	}
68	logger_server.Infof("%v new http server %v %v %v\n", s.logPrefix, name, connAddr, urlPrefix)
69	http.Handle(s.urlPrefix, handler)
70	handler.SetServer(s)
71	s.srv = &http.Server{
72		Addr:           connAddr,
73		Handler:        nil,
74		ReadTimeout:    base.AdminportReadTimeout,
75		WriteTimeout:   base.AdminportWriteTimeout,
76		MaxHeaderBytes: 1 << 20,
77	}
78	return s
79}
80
81// Start is part of Server interface.
82func (s *httpServer) Start() chan error {
83	errCh := make(chan error, 1)
84
85	// Server routine
86	go func() {
87		defer s.shutdown()
88
89		logger_server.Infof("%s starting ...\n", s.logPrefix)
90		// ListenAndServe blocks and returns a non-nil error if something wrong happens
91		err := s.srv.ListenAndServe()
92		logger_server.Errorf("%s exited with error %v\n", s.logPrefix, err)
93		errCh <- err
94	}()
95	return errCh
96}
97
98// Stop is part of Server interface. Once stopped, Start() cannot be called again
99func (s *httpServer) Stop() {
100	s.shutdown()
101	logger_server.Infof("%s ... stopped\n", s.logPrefix)
102}
103
104func (s *httpServer) shutdown() {
105	s.mu.Lock()
106	defer s.mu.Unlock()
107
108	if s.srv != nil {
109		s.srv.Close()
110		close(s.reqch)
111		s.srv = nil
112	}
113}
114
115// handle incoming request.
116func (s *httpServer) systemHandler(w http.ResponseWriter, r *http.Request) {
117	var err error
118
119	// Fault-tolerance. No need to crash the server in case of panic.
120	defer func() {
121		if r := recover(); r != nil {
122			logger_server.Errorf("adminport.request.recovered `%v`\n", r)
123		} else if err != nil {
124			logger_server.Errorf("%v\n", err)
125		}
126	}()
127
128	waitch := make(chan interface{}, 1)
129	// send and wait
130	s.reqch <- &httpAdminRequest{srv: s, req: r, waitch: waitch}
131	val := <-waitch
132
133	switch v := (val).(type) {
134	case error:
135		http.Error(w, v.Error(), http.StatusInternalServerError)
136		err = fmt.Errorf("%v, %v", ErrorInternal, v)
137		logger_server.Errorf("%v", err)
138	case *Response:
139		if v.TagPrintingBody && logger_server.GetLogLevel() >= log.LogLevelDebug {
140			bodyRedact := base.TagUDBytes(base.DeepCopyByteArray(v.Body))
141			logger_server.Debugf("Response from goxdcr rest server. status=%v\n body in string form=%v\n", v.StatusCode, string(bodyRedact))
142		} else {
143			logger_server.Debugf("Response from goxdcr rest server. status=%v\n body in string form=%v", v.StatusCode, string(v.Body))
144		}
145		w.Header().Set(base.ContentType, base.JsonContentType)
146		w.WriteHeader(v.StatusCode)
147		w.Write(v.Body)
148	}
149}
150
151// concrete type implementing Request interface
152type httpAdminRequest struct {
153	srv    *httpServer
154	req    *http.Request
155	waitch chan interface{}
156}
157
158// GetMessage is part of Request interface.
159func (r *httpAdminRequest) GetHttpRequest() *http.Request {
160	return r.req
161}
162
163// Send is part of Request interface.
164func (r *httpAdminRequest) Send(response *Response) error {
165	r.waitch <- response
166	close(r.waitch)
167	return nil
168}
169
170// SendError is part of Request interface.
171func (r *httpAdminRequest) SendError(err error) error {
172	r.waitch <- err
173	close(r.waitch)
174	return nil
175}
176
177//xdcr implementaton of RequestHandler
178type Handler struct {
179	server *httpServer
180}
181
182func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
183	if logger_server.GetLogLevel() >= log.LogLevelDebug {
184		rr := base.CloneAndTagHttpRequest(r) // rr == redactedRequest
185		logger_server.Debugf("Request received from ServeHTTP. r=%v\nwith path, %v and method, %v\n", rr, rr.URL.Path, rr.Method)
186	}
187	h.server.systemHandler(w, r)
188}
189
190func (h *Handler) SetServer(s Server) error {
191	server, ok := s.(*httpServer)
192	if !ok {
193		return ErrorInvalidServerType
194	}
195	h.server = server
196	return nil
197}
198
199func (h *Handler) GetServer() Server {
200	return h.server
201}
202