aboutsummaryrefslogtreecommitdiff
path: root/mod/module.hxx
blob: 25dce4328e247b35957b6796a23af6509e043df8 (plain)
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
// file      : mod/module.hxx -*- C++ -*-
// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef MOD_MODULE_HXX
#define MOD_MODULE_HXX

#include <web/module.hxx>

#include <libbrep/types.hxx>
#include <libbrep/utility.hxx>

#include <mod/utility.hxx>
#include <mod/options.hxx>
#include <mod/diagnostics.hxx>

namespace brep
{
  // Bring in commonly used names from the web namespace.
  //
  // @@ Maybe doing using namespace is the right way to handle this.
  //    There will, however, most likely be a conflict between
  //    web::handler and our handler. Or maybe not, need to try.
  //
  using web::status_code;
  using web::invalid_request;
  using web::sequence_error;
  using web::option_descriptions;
  using web::name_value;
  using web::name_values;
  using web::request;
  using web::response;
  using web::log;

  // This exception indicated a server error (5XX). In particular,
  // it is thrown by the fail diagnostics stream and is caught by the
  // handler implementation where it is both logged as an error and
  // returned to the user with the 5XX status code.
  //
  struct server_error
  {
    diag_data data;

    server_error (diag_data&& d): data (move (d)) {}
  };

  // Every handler member function that needs to produce any diagnostics
  // shall begin with:
  //
  // HANDLER_DIAG;
  //
  // This will instantiate the fail, error, warn, info, and trace
  // diagnostics streams with the function's name.
  //
#define HANDLER_DIAG                                                    \
  const fail_mark<server_error> fail (__PRETTY_FUNCTION__);             \
  const basic_mark error (severity::error,                              \
                          this->log_writer_,                            \
                          __PRETTY_FUNCTION__);                         \
  const basic_mark warn (severity::warning,                             \
                         this->log_writer_,                             \
                         __PRETTY_FUNCTION__);                          \
  const basic_mark info (severity::info,                                \
                         this->log_writer_,                             \
                         __PRETTY_FUNCTION__);                          \
  const basic_mark trace (severity::trace,                              \
                          this->log_writer_,                            \
                          __PRETTY_FUNCTION__)

  // Adaptation of the web::handler to our needs.
  //
  class handler: public web::handler
  {
  public:
    // If not empty, denotes the repository tenant the request is for.
    // Extracted by the handler implementation from the request (URL path,
    // parameters, etc).
    //
    string tenant;

    // Diagnostics.
    //
  protected:
    // Trace verbosity level.
    //
    // 0 - tracing disabled.
    // 1 - brief information regarding irregular situations, which not being
    //     an error can be of some interest.
    // 2 - @@ TODO: document
    //
    // While uint8 is more than enough, use uint16 for the ease of printing.
    //
    uint16_t verb_ = 0;

    template <class F> void l1 (const F& f) const {if (verb_ >= 1) f ();}
    template <class F> void l2 (const F& f) const {if (verb_ >= 2) f ();}

    // Set to true when the handler is successfully initialized.
    //
    bool initialized_ = false;

    // Implementation details.
    //
  protected:
    handler ();
    handler (const handler& );

    static name_values
    filter (const name_values&, const option_descriptions&);

    static option_descriptions
    convert (const cli::options&);

    static void
    append (option_descriptions& dst, const cli::options& src);

    static void
    append (option_descriptions& dst, const option_descriptions& src);

    // Can be used by handler implementation to parse HTTP request parameters.
    //
    class name_value_scanner: public cli::scanner
    {
    public:
      name_value_scanner (const name_values&) noexcept;

      virtual bool
      more ();

      virtual const char*
      peek ();

      virtual const char*
      next ();

      virtual void
      skip ();

    private:
      const name_values& name_values_;
      name_values::const_iterator i_;
      bool name_;
    };

  public:
    virtual const cli::options&
    cli_options () const = 0;

    virtual void
    init (cli::scanner&) = 0;

    // Can be overriden by custom request dispatcher to initialize
    // sub-handlers.
    //
    virtual void
    init (const name_values&);

    virtual void
    init (const name_values&, log&);

    virtual bool
    handle (request&, response&) = 0;

    virtual bool
    handle (request&, response&, log&);

    // web::handler interface.
    //
  public:
    // Custom request dispatcher can aggregate its own option descriptions
    // with sub-handlers option descriptions. In this case it should still call
    // the base implementation in order to include the brep::handler's options.
    //
    virtual option_descriptions
    options ();

  private:
    virtual void
    version (log&);

    // Can be overriden by the handler implementation to log version, etc.
    //
    virtual void
    version () {}

    name_values
    expand_options (const name_values&);

    // Diagnostics implementation details.
    //
  protected:
    log* log_ {nullptr}; // Diagnostics backend provided by the web server.

  private:
    // Extract function name from a __PRETTY_FUNCTION__.
    // Throw invalid_argument if fail to parse.
    //
    static string
    func_name (const char* pretty_name);

    void
    log_write (const diag_data&) const;

  protected:
    const diag_epilogue log_writer_;
  };
}

#endif // MOD_MODULE_HXX