GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/webapi/fcgi.h Lines: 0 13 0.0 %
Date: 2018-10-07 00:43:30 Branches: 0 0 - %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System
3
 */
4
5
#ifndef CVMFS_WEBAPI_FCGI_H_
6
#define CVMFS_WEBAPI_FCGI_H_
7
8
#include <netinet/in.h>
9
#include <stdint.h>
10
11
#include <cstring>
12
#include <map>
13
#include <string>
14
15
// TODO(jblomer): deal with termination signals
16
17
/**
18
 * Implements a simple FastCGI responder role.  This responder cannot multi-
19
 * plex connections.  See http://www.fastcgi.com/devkit/doc/fcgi-spec.html
20
 * It is supposed to be used in the following way.  Don't forget to sanitize
21
 * everything you get out from fcgi.
22
 *
23
 * unsigned char *buf;
24
 * unsigned length;
25
 * uint64_t id = 0;
26
 * FastCgi::Event event;
27
 * while ((event = fcgi.NextEvent(&buf, &length, &id)) != FastCgi::kEventExit) {
28
 *   switch (event) {
29
 *     case FastCgi::kEventAbortReq:
30
 *       fcgi.AbortRequest();
31
 *       break;
32
 *     case FastCgi::kEventStdin:
33
 *       // Use id to detect if this new input is part of the same request than
34
 *       // the previous one.
35
 *       // Process buf; if length == 0, the input stream is finished
36
 *       // For POST: compare CONTENT-LENGTH with actual stream length
37
 *       fcgi.GetParam("PARAMETER", ...) ...
38
 *       fcgi.SendData("...", false);
39
 *       fcgi.SendData("...", true);
40
 *       fcgi.SendError("...", true);
41
 *       fcgi.EndRequest(0);  // or failure status code
42
 *       break;
43
 *     default:
44
 *       abort();
45
 *   }
46
 * }
47
 */
48
class FastCgi {
49
 public:
50
  enum Event {
51
    kEventExit = 0,
52
    kEventTransportError,
53
    kEventAbortReq,
54
    kEventStdin,
55
  };
56
57
  FastCgi();
58
  ~FastCgi();
59
  bool IsFcgi();
60
  void AbortRequest();
61
  void EndRequest(uint32_t exit_code);
62
  bool SendData(const std::string &data, bool finish);
63
  bool SendError(const std::string &data, bool finish);
64
  void ReturnBadRequest(const std::string &reason);
65
  void ReturnNotFound();
66
  Event NextEvent(unsigned char **buf, unsigned *length, uint64_t *id);
67
68
  bool GetParam(const std::string &key, std::string *value);
69
  std::string DumpParams();
70
71
  bool MkTcpSocket(const std::string &ip4_address, uint16_t port);
72
73
 private:
74
  static const int kCgiListnsockFileno = 0;
75
76
  static const unsigned kMaxContentLength;
77
78
  /**
79
   * Value for version component of Header.
80
   */
81
  static const int kVersion1 = 1;
82
83
  /**
84
   * Value for requestId component of Header.
85
   */
86
  static const int kNullRequestId = 0;
87
88
  /**
89
   * Mask for flags component of BeginRequestBody.
90
   */
91
  static const int kKeepConn = 1;
92
93
  /**
94
   * Values the server might query from the app.
95
   */
96
  static const char *kValueMaxConns;
97
  static const char *kValueMaxReqs;
98
  static const char *kValueMpxConns;
99
100
  /**
101
   * Values for type component of Header.
102
   */
103
  enum RecordType {
104
    kTypeBegin = 1,           // Application record, WS --> App
105
    kTypeAbort = 2,           // Application record, WS --> App
106
    kTypeEnd = 3,             // Application record, App --> WS
107
    kTypeParams = 4,          // Application record, WS --> App
108
    kTypeStdin = 5,           // Application stream record, WS --> App
109
    kTypeStdout = 6,          // Application stream record, App --> WS
110
    kTypeStderr = 7,          // Application stream record, App --> WS
111
    kTypeData = 8,            // Application stream record, WS --> App
112
    kTypeValues = 9,          // Management record, WS --> App
113
    kTypeValuesResult = 10,   // Management record, App --> WS
114
    kTypeUnknown = 11,        // Management record, App --> WS
115
  };
116
117
  /**
118
   * Values for role component of BeginRequestBody.
119
   */
120
  enum Role {
121
    kRoleResponder = 1,
122
    kRoleAuthorizer = 2,  // unavailable in this implementation
123
    kRoleFilter = 3,  // unavailable in this implementation
124
  };
125
126
  /**
127
   * Values for protocolStatus component of EndRequestBody.
128
   */
129
  enum ProtocolStatus {
130
    kStatusReqComplete = 0,
131
    kStatusCantMpxConn = 1,
132
    kStatusOverloaded = 2,  // unused in this implementation
133
    kStatusUnknownRole = 3,
134
  };
135
136
  struct RawHeader {
137
    RawHeader()
138
      : version(kVersion1), type(0), request_id_b1(0), request_id_b0(0)
139
      , content_length_b1(0), content_length_b0(0), padding_length(0)
140
      , reserved(0)
141
    { }
142
    unsigned char version;
143
    unsigned char type;
144
    unsigned char request_id_b1;
145
    unsigned char request_id_b0;
146
    unsigned char content_length_b1;
147
    unsigned char content_length_b0;
148
    unsigned char padding_length;
149
    unsigned char reserved;
150
  };
151
152
  struct Header {
153
    unsigned char type;
154
    unsigned char padding_length;
155
    uint16_t request_id;
156
    uint16_t content_length;
157
  };
158
159
  struct BeginRequestBody {
160
    unsigned char role_b1;
161
    unsigned char role_b0;
162
    unsigned char flags;
163
    unsigned char reserved[5];
164
  };
165
166
  struct EndRequestBody {
167
    EndRequestBody()
168
      : app_status_b3(0), app_status_b2(0), app_status_b1(0), app_status_b0(0)
169
      , protocol_status(0)
170
    {
171
      memset(reserved, 0, 3);
172
    }
173
    unsigned char app_status_b3;
174
    unsigned char app_status_b2;
175
    unsigned char app_status_b1;
176
    unsigned char app_status_b0;
177
    unsigned char protocol_status;
178
    unsigned char reserved[3];
179
  };
180
181
  struct UnknownTypeBody {
182
    UnknownTypeBody() : type(0) {
183
      memset(reserved, 0, 7);
184
    }
185
    unsigned char type;
186
    unsigned char reserved[7];
187
  };
188
189
  bool CheckValidSource(const struct sockaddr_in &addr_in);
190
  void CloseConnection();
191
192
  bool ReadHeader(int fd_transport, Header *header);
193
  bool ReadContent(uint16_t content_length, unsigned char padding_length);
194
  bool ReadBeginBody(int fd_transport, uint16_t *role, bool *keep_connection);
195
  bool ReadParams(const Header &first_header);
196
197
  bool ParseKvPair(const char *data, unsigned len,
198
                   unsigned *nparsed, std::string *key, std::string *value);
199
200
  void ReplyUnknownType(int fd_transport, unsigned char received_type);
201
  void ReplyEndRequest(int fd_transport, uint16_t req_id,
202
                       uint32_t exit_code, unsigned char status);
203
  bool ReplyStream(unsigned char type,
204
                   const unsigned char *data, unsigned length);
205
206
  bool ProcessValues(const Header &request_header);
207
208
  inline uint16_t MkUint16(const unsigned char b1, const unsigned char b0) {
209
    return (static_cast<uint16_t>(b1) << 8) + static_cast<uint16_t>(b0);
210
  }
211
212
  inline void FlattenUint16(uint16_t val, unsigned char *b1, unsigned char *b0)
213
  {
214
    *b1 = static_cast<unsigned char>((val >> 8) & 0xff);
215
    *b0 = static_cast<unsigned char>(val & 0xff);
216
  }
217
218
  unsigned AddShortKv(const std::string &key, const std::string &value,
219
                      unsigned buf_size, unsigned char *buf);
220
221
  /**
222
   * kCgiListnsockFileno for fcgi processes spawned by the server, otherwise
223
   * filled by MkTcpSocket().
224
   */
225
  int fd_sock_;
226
  bool is_tcp_socket_;
227
228
  unsigned char content_buf_[64 * 1024 + 255];
229
  int fd_transport_;
230
231
  /**
232
   * Returned by NextEvent().  Has a different value for every request.  Never
233
   * zero, so the application can initialize its id state to zero and detect
234
   * if a stdin event is for the same request or a new one.
235
   */
236
  uint64_t global_request_id_;
237
238
  int request_id_;
239
  bool keep_connection_;
240
  std::map<std::string, std::string> params_;
241
};
242
243
#endif  // CVMFS_WEBAPI_FCGI_H_