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
|
// file : web/module -*- C++ -*-
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license : MIT; see accompanying LICENSE file
#ifndef WEB_MODULE
#define WEB_MODULE
#include <string>
#include <vector>
#include <iosfwd>
#include <cstdint> // uint16_t
#include <utility> // move()
#include <stdexcept> // runtime_error
namespace web
{
// Exception indicating HTTP request/response sequencing error.
// For example, trying to change the status code after some
// content has already been written.
//
struct sequence_error: std::runtime_error {};
// HTTP status code.
//
// @@ Define some commonly used constants?
//
using status_code = std::uint16_t;
struct name_value
{
// These should eventually become string_view's.
//
std::string name;
std::string value;
name_value () {}
name_value (std::string n, std::string v)
: name (std::move (n)), value (std::move (v)) {}
};
using name_values = std::vector<name_value>;
class request
{
public:
//@@ Why not pass parameters directly? Lazy parsing?
//@@ Why not have something like operator[] for lookup? Probably
// in name_values.
//@@ Maybe parameter_list() and parameter_map()?
//
virtual const name_values&
parameters () = 0;
};
class response
{
public:
// Set status code, content type, and get the stream to write
// the content to. If the buffer argument is true (default),
// then buffer the entire content before sending it as a
// response. This allows us to change the status code in
// case of an error.
//
// Specifically, if there is already content in the buffer
// and the status code is changed, then the old content is
// discarded. If the content was not buffered and the status
// is changed, then the sequence_error exception is thrown.
// If this exception leaves module::handle(), then the
// implementation shall terminate the response in a suitable
// but unspecified manner. In particular, there is no guarantee
// that the user will be notified of an error or observe the
// new status.
//
virtual std::ostream&
content (status_code, const std::string& type, bool buffer = true) = 0;
// Set status code without writing any content.
//
virtual void
status (status_code) = 0;
};
// A web server logging backend. The module can use it to log
// diagnostics that is meant for the web server operator rather
// than the user.
//
// The module can cast this basic interface to the web server's
// specific implementation that may provide a richer interface.
//
class log
{
public:
virtual void
write (const char* msg);
};
// The web server creates a new module instance for each request
// by copy-initializing it with the module exemplar. This way we
// achieve two things: we can freely use module data members
// without worrying about multi-threading issues and we
// automatically get started with the initial state for each
// request. If you really need to share some rw-data between
// all the modules, use static data members with appropriate
// locking. See the <service> header in one of the web server
// directories (e.g., apache/) if you need to see the code that
// does this.
//
class module
{
public:
virtual void
handle (request&, response&, log&) = 0;
};
}
#endif // WEB_MODULE
|